@moskala/oneagent-core 0.4.0 → 0.4.2

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/dist/index.js ADDED
@@ -0,0 +1,857 @@
1
+ // src/constants.ts
2
+ var ONEAGENT_DIR = ".oneagent";
3
+ // src/agents.ts
4
+ var AGENT_DEFINITIONS = [
5
+ {
6
+ target: "claude",
7
+ displayName: "Claude Code",
8
+ hint: "CLAUDE.md + .claude/rules/",
9
+ detectIndicators: ["CLAUDE.md", ".claude"],
10
+ mainFile: "CLAUDE.md",
11
+ rulesDir: ".claude/rules",
12
+ skillsDir: ".claude/skills",
13
+ commandsDir: ".claude/commands"
14
+ },
15
+ {
16
+ target: "cursor",
17
+ displayName: "Cursor",
18
+ hint: "AGENTS.md + .cursor/rules/",
19
+ detectIndicators: [".cursor", ".cursorrules"],
20
+ mainFile: "AGENTS.md",
21
+ rulesDir: ".cursor/rules",
22
+ skillsDir: ".cursor/skills",
23
+ deprecatedFiles: [".cursorrules"],
24
+ commandsDir: ".cursor/commands"
25
+ },
26
+ {
27
+ target: "windsurf",
28
+ displayName: "Windsurf",
29
+ hint: "AGENTS.md + .windsurf/rules/",
30
+ detectIndicators: [".windsurf", ".windsurfrules"],
31
+ mainFile: "AGENTS.md",
32
+ rulesDir: ".windsurf/rules",
33
+ skillsDir: ".windsurf/skills",
34
+ deprecatedFiles: [".windsurfrules"]
35
+ },
36
+ {
37
+ target: "opencode",
38
+ displayName: "OpenCode",
39
+ hint: "AGENTS.md + .opencode/",
40
+ detectIndicators: ["opencode.json", ".opencode"],
41
+ mainFile: "AGENTS.md",
42
+ rulesDir: ".opencode/rules",
43
+ skillsDir: ".opencode/skills",
44
+ commandsDir: ".opencode/commands"
45
+ },
46
+ {
47
+ target: "copilot",
48
+ displayName: "GitHub Copilot",
49
+ hint: ".github/instructions/*.instructions.md",
50
+ detectIndicators: [".github/copilot-instructions.md", ".github/instructions"],
51
+ mainFile: ".github/copilot-instructions.md",
52
+ skillsDir: ".github/skills"
53
+ }
54
+ ];
55
+ function getAgentDef(target) {
56
+ return AGENT_DEFINITIONS.find((d) => d.target === target);
57
+ }
58
+ // src/config.ts
59
+ import { parse, stringify } from "yaml";
60
+ import path from "path";
61
+ import fs from "fs/promises";
62
+ var CONFIG_REL = `${ONEAGENT_DIR}/config.yml`;
63
+ var ALL_AGENT_TARGETS = ["claude", "cursor", "windsurf", "opencode", "copilot"];
64
+ function activeTargets(config) {
65
+ return ALL_AGENT_TARGETS.filter((t) => config.targets[t]);
66
+ }
67
+ function makeTargets(...enabled) {
68
+ return Object.fromEntries(ALL_AGENT_TARGETS.map((t) => [t, enabled.includes(t)]));
69
+ }
70
+ async function configExists(root) {
71
+ return fs.access(path.join(root, CONFIG_REL)).then(() => true, () => false);
72
+ }
73
+ async function readConfig(root) {
74
+ const content = await fs.readFile(path.join(root, CONFIG_REL), "utf-8");
75
+ return parse(content);
76
+ }
77
+ async function writeConfig(root, config) {
78
+ const filePath = path.join(root, CONFIG_REL);
79
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
80
+ await fs.writeFile(filePath, stringify(config));
81
+ }
82
+ // src/detect.ts
83
+ import path2 from "path";
84
+ import fs2 from "fs/promises";
85
+ var AGENT_FILES = [
86
+ "CLAUDE.md",
87
+ "AGENTS.md",
88
+ ".cursorrules",
89
+ ".windsurfrules",
90
+ ".github/copilot-instructions.md"
91
+ ];
92
+ async function readDetectedFile(root, rel) {
93
+ const absolutePath = path2.join(root, rel);
94
+ try {
95
+ const stat = await fs2.lstat(absolutePath);
96
+ if (stat.isSymbolicLink()) {
97
+ const linkTarget = await fs2.readlink(absolutePath);
98
+ const resolved = path2.resolve(path2.dirname(absolutePath), linkTarget);
99
+ if (resolved.startsWith(path2.join(root, ONEAGENT_DIR)))
100
+ return null;
101
+ }
102
+ const content = await fs2.readFile(absolutePath, "utf-8");
103
+ return {
104
+ relativePath: rel,
105
+ absolutePath,
106
+ sizeBytes: stat.size,
107
+ modifiedAt: stat.mtime,
108
+ content
109
+ };
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ async function detectExistingFiles(root) {
115
+ const results = await Promise.all(AGENT_FILES.map((rel) => readDetectedFile(root, rel)));
116
+ return results.filter((f) => f !== null);
117
+ }
118
+ function filesHaveSameContent(files) {
119
+ if (files.length <= 1)
120
+ return true;
121
+ const first = files[0].content;
122
+ return files.every((f) => f.content === first);
123
+ }
124
+ var DEPRECATED_FILES = AGENT_DEFINITIONS.flatMap((d) => d.deprecatedFiles ?? []);
125
+ async function removeDeprecatedFiles(root) {
126
+ for (const rel of DEPRECATED_FILES) {
127
+ const absPath = path2.join(root, rel);
128
+ try {
129
+ const stat = await fs2.lstat(absPath);
130
+ if (!stat.isSymbolicLink())
131
+ await fs2.unlink(absPath);
132
+ } catch {}
133
+ }
134
+ }
135
+ // src/rules.ts
136
+ import path3 from "path";
137
+ import fs3 from "fs/promises";
138
+ async function readRules(root) {
139
+ const rulesDir = path3.join(root, ONEAGENT_DIR, "rules");
140
+ try {
141
+ const files = await fs3.readdir(rulesDir);
142
+ return files.filter((f) => f.endsWith(".md")).map((f) => ({ name: path3.basename(f, ".md"), path: path3.join(rulesDir, f) })).sort((a, b) => a.name.localeCompare(b.name));
143
+ } catch {
144
+ return [];
145
+ }
146
+ }
147
+ // src/commands.ts
148
+ import path4 from "path";
149
+ import fs4 from "fs/promises";
150
+ async function readCommands(root) {
151
+ const commandsDir = path4.join(root, ONEAGENT_DIR, "commands");
152
+ try {
153
+ const files = await fs4.readdir(commandsDir);
154
+ return files.filter((f) => f.endsWith(".md")).map((f) => ({ name: path4.basename(f, ".md"), path: path4.join(commandsDir, f) })).sort((a, b) => a.name.localeCompare(b.name));
155
+ } catch {
156
+ return [];
157
+ }
158
+ }
159
+ // src/skills.ts
160
+ import path5 from "path";
161
+ import fs5 from "fs/promises";
162
+ var VALID_MODES = ["ask", "edit", "agent"];
163
+ function parseSkillFrontmatter(raw) {
164
+ const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
165
+ if (!match)
166
+ return { description: "", mode: "agent", content: raw };
167
+ const frontmatter = match[1] ?? "";
168
+ const content = match[2] ?? "";
169
+ const descMatch = frontmatter.match(/description:\s*["']?([^"'\n]+)["']?/);
170
+ const description = descMatch?.[1]?.trim() ?? "";
171
+ const modeMatch = frontmatter.match(/mode:\s*["']?([^"'\n]+)["']?/);
172
+ const modeRaw = modeMatch?.[1]?.trim() ?? "agent";
173
+ const mode = VALID_MODES.includes(modeRaw) ? modeRaw : "agent";
174
+ return { description, mode, content };
175
+ }
176
+ async function readSkillFile(filePath) {
177
+ const raw = await fs5.readFile(filePath, "utf-8");
178
+ const { description, mode, content } = parseSkillFrontmatter(raw);
179
+ return { name: path5.basename(filePath, ".md"), path: filePath, description, mode, content };
180
+ }
181
+ async function readSkills(root) {
182
+ const skillsDir = path5.join(root, ONEAGENT_DIR, "skills");
183
+ try {
184
+ const files = await fs5.readdir(skillsDir);
185
+ const mdFiles = files.filter((f) => f.endsWith(".md"));
186
+ const skills = await Promise.all(mdFiles.map((f) => readSkillFile(path5.join(skillsDir, f))));
187
+ return skills.sort((a, b) => a.name.localeCompare(b.name));
188
+ } catch {
189
+ return [];
190
+ }
191
+ }
192
+ // src/symlinks.ts
193
+ import path6 from "path";
194
+ import fs6 from "fs/promises";
195
+ async function ensureDir(dirPath) {
196
+ await fs6.mkdir(dirPath, { recursive: true });
197
+ }
198
+ async function createSymlink(symlinkPath, target) {
199
+ await ensureDir(path6.dirname(symlinkPath));
200
+ try {
201
+ const stat = await fs6.lstat(symlinkPath);
202
+ if (stat.isSymbolicLink() && await fs6.readlink(symlinkPath) === target)
203
+ return;
204
+ } catch {}
205
+ for (let attempt = 0;attempt < 3; attempt++) {
206
+ await fs6.rm(symlinkPath, { recursive: true, force: true });
207
+ try {
208
+ await fs6.symlink(target, symlinkPath);
209
+ return;
210
+ } catch (err) {
211
+ if (err.code !== "EEXIST" || attempt === 2)
212
+ throw err;
213
+ }
214
+ }
215
+ }
216
+ function relativeTarget(symlinkPath, targetAbsPath) {
217
+ return path6.relative(path6.dirname(symlinkPath), targetAbsPath);
218
+ }
219
+ function buildMainSymlinks(root, targets) {
220
+ const instructionsAbs = path6.join(root, ONEAGENT_DIR, "instructions.md");
221
+ const seen = new Map;
222
+ for (const target of targets) {
223
+ const def = AGENT_DEFINITIONS.find((d) => d.target === target);
224
+ const symlinkPath = path6.join(root, def.mainFile);
225
+ if (!seen.has(symlinkPath)) {
226
+ seen.set(symlinkPath, {
227
+ symlinkPath,
228
+ target: relativeTarget(symlinkPath, instructionsAbs),
229
+ label: path6.relative(root, symlinkPath)
230
+ });
231
+ }
232
+ }
233
+ return Array.from(seen.values());
234
+ }
235
+ function buildRulesSymlinks(root, targets) {
236
+ const targetAbs = path6.join(root, ONEAGENT_DIR, "rules");
237
+ return AGENT_DEFINITIONS.filter((d) => targets.includes(d.target) && d.rulesDir).map((d) => {
238
+ const symlinkPath = path6.join(root, d.rulesDir);
239
+ return { symlinkPath, target: relativeTarget(symlinkPath, targetAbs), label: d.rulesDir };
240
+ });
241
+ }
242
+ function buildSkillSymlinks(root, targets) {
243
+ const targetAbs = path6.join(root, ONEAGENT_DIR, "skills");
244
+ return AGENT_DEFINITIONS.filter((d) => targets.includes(d.target) && d.skillsDir).map((d) => {
245
+ const symlinkPath = path6.join(root, d.skillsDir);
246
+ return { symlinkPath, target: relativeTarget(symlinkPath, targetAbs), label: d.skillsDir };
247
+ });
248
+ }
249
+ function buildCommandSymlinks(root, targets) {
250
+ const targetAbs = path6.join(root, ONEAGENT_DIR, "commands");
251
+ return AGENT_DEFINITIONS.filter((d) => targets.includes(d.target) && d.commandsDir).map((d) => {
252
+ const symlinkPath = path6.join(root, d.commandsDir);
253
+ return { symlinkPath, target: relativeTarget(symlinkPath, targetAbs), label: d.commandsDir };
254
+ });
255
+ }
256
+ function buildAgentsDirSymlinks(root) {
257
+ const symlinkPath = path6.join(root, ".agents/skills");
258
+ const targetAbs = path6.join(root, ONEAGENT_DIR, "skills");
259
+ return [{ symlinkPath, target: relativeTarget(symlinkPath, targetAbs), label: ".agents/skills" }];
260
+ }
261
+ async function migrateFilesFromDir(srcDir, destDir, root) {
262
+ await fs6.mkdir(destDir, { recursive: true });
263
+ let entries;
264
+ try {
265
+ entries = await fs6.readdir(srcDir, { withFileTypes: true });
266
+ } catch {
267
+ return;
268
+ }
269
+ for (const entry of entries) {
270
+ const srcFile = path6.join(srcDir, entry.name);
271
+ const fileStat = await fs6.lstat(srcFile);
272
+ if (fileStat.isSymbolicLink())
273
+ continue;
274
+ if (fileStat.isDirectory()) {
275
+ const destSub = path6.join(destDir, entry.name);
276
+ try {
277
+ await fs6.access(destSub);
278
+ } catch {
279
+ await fs6.mkdir(destDir, { recursive: true });
280
+ await fs6.rename(srcFile, destSub);
281
+ }
282
+ continue;
283
+ }
284
+ if (!fileStat.isFile())
285
+ continue;
286
+ const destFile = path6.join(destDir, entry.name);
287
+ let destExists = false;
288
+ try {
289
+ await fs6.access(destFile);
290
+ destExists = true;
291
+ } catch {}
292
+ if (destExists) {
293
+ const [srcContent, destContent] = await Promise.all([
294
+ fs6.readFile(srcFile, "utf-8"),
295
+ fs6.readFile(destFile, "utf-8")
296
+ ]);
297
+ if (srcContent !== destContent) {
298
+ const backupDir = path6.join(root, ONEAGENT_DIR, "backup");
299
+ await fs6.mkdir(backupDir, { recursive: true });
300
+ const safeName = path6.relative(root, srcFile).replace(/\//g, "_");
301
+ await fs6.writeFile(path6.join(backupDir, safeName), srcContent);
302
+ }
303
+ await fs6.unlink(srcFile);
304
+ } else {
305
+ await fs6.rename(srcFile, destFile);
306
+ }
307
+ }
308
+ }
309
+ async function migrateAndRemoveDir(src, dest, root) {
310
+ let stat;
311
+ try {
312
+ stat = await fs6.lstat(src);
313
+ } catch {
314
+ return;
315
+ }
316
+ if (stat.isSymbolicLink() || !stat.isDirectory())
317
+ return;
318
+ await migrateFilesFromDir(src, dest, root);
319
+ await fs6.rm(src, { recursive: true, force: true });
320
+ }
321
+ async function migrateRuleAndSkillFiles(root) {
322
+ const destRules = path6.join(root, ONEAGENT_DIR, "rules");
323
+ const destSkills = path6.join(root, ONEAGENT_DIR, "skills");
324
+ const destCommands = path6.join(root, ONEAGENT_DIR, "commands");
325
+ for (const def of AGENT_DEFINITIONS) {
326
+ if (def.rulesDir)
327
+ await migrateAndRemoveDir(path6.join(root, def.rulesDir), destRules, root);
328
+ }
329
+ for (const def of AGENT_DEFINITIONS) {
330
+ if (def.skillsDir)
331
+ await migrateAndRemoveDir(path6.join(root, def.skillsDir), destSkills, root);
332
+ }
333
+ await migrateAndRemoveDir(path6.join(root, ".agents/skills"), destSkills, root);
334
+ for (const def of AGENT_DEFINITIONS) {
335
+ if (def.commandsDir)
336
+ await migrateAndRemoveDir(path6.join(root, def.commandsDir), destCommands, root);
337
+ }
338
+ }
339
+ async function backupDirRecursive(srcDir, backupDir, prefix) {
340
+ let entries;
341
+ try {
342
+ entries = await fs6.readdir(srcDir, { withFileTypes: true });
343
+ } catch {
344
+ return;
345
+ }
346
+ for (const entry of entries) {
347
+ const srcPath = path6.join(srcDir, entry.name);
348
+ const lstat = await fs6.lstat(srcPath);
349
+ if (lstat.isSymbolicLink())
350
+ continue;
351
+ if (lstat.isDirectory()) {
352
+ await backupDirRecursive(srcPath, backupDir, `${prefix}_${entry.name}`);
353
+ } else if (lstat.isFile()) {
354
+ await fs6.mkdir(backupDir, { recursive: true });
355
+ await fs6.copyFile(srcPath, path6.join(backupDir, `${prefix}_${entry.name}`));
356
+ }
357
+ }
358
+ }
359
+ async function cleanupAgentDir(root, target) {
360
+ const def = AGENT_DEFINITIONS.find((d) => d.target === target);
361
+ const backupDir = path6.join(root, ONEAGENT_DIR, "backup");
362
+ const agentDir = [def.rulesDir, def.skillsDir, def.commandsDir].filter(Boolean).map((d) => d.split("/")[0]).find((d) => d !== ".github");
363
+ if (agentDir) {
364
+ const agentDirAbs = path6.join(root, agentDir);
365
+ let stat;
366
+ try {
367
+ stat = await fs6.lstat(agentDirAbs);
368
+ } catch {}
369
+ if (stat && stat.isDirectory() && !stat.isSymbolicLink()) {
370
+ await backupDirRecursive(agentDirAbs, backupDir, agentDir);
371
+ await fs6.rm(agentDirAbs, { recursive: true, force: true });
372
+ }
373
+ }
374
+ if (target === "opencode") {
375
+ const opPath = path6.join(root, "opencode.json");
376
+ try {
377
+ const content = await fs6.readFile(opPath, "utf-8");
378
+ await fs6.mkdir(backupDir, { recursive: true });
379
+ await fs6.writeFile(path6.join(backupDir, "opencode.json"), content);
380
+ } catch {}
381
+ try {
382
+ await fs6.unlink(opPath);
383
+ } catch {}
384
+ }
385
+ }
386
+ async function createAllSymlinks(entries) {
387
+ const deduped = new Map;
388
+ for (const e of entries)
389
+ deduped.set(e.symlinkPath, e);
390
+ for (const e of deduped.values()) {
391
+ await createSymlink(e.symlinkPath, e.target);
392
+ }
393
+ }
394
+ async function checkSymlink(entry) {
395
+ try {
396
+ const stat = await fs6.lstat(entry.symlinkPath);
397
+ if (!stat.isSymbolicLink()) {
398
+ return { ...entry, exists: true, valid: false };
399
+ }
400
+ const linkTarget = await fs6.readlink(entry.symlinkPath);
401
+ return { ...entry, exists: true, valid: linkTarget === entry.target };
402
+ } catch {
403
+ return { ...entry, exists: false, valid: false };
404
+ }
405
+ }
406
+ // src/copilot.ts
407
+ import path7 from "path";
408
+ import fs7 from "fs/promises";
409
+ function copilotFilePath(root, ruleName) {
410
+ return path7.join(root, ".github/instructions", `${ruleName}.instructions.md`);
411
+ }
412
+ async function generateCopilotRule(root, rule) {
413
+ const destPath = copilotFilePath(root, rule.name);
414
+ await fs7.mkdir(path7.dirname(destPath), { recursive: true });
415
+ await fs7.copyFile(rule.path, destPath);
416
+ }
417
+ async function generateCopilotRules(root, rules) {
418
+ await Promise.all(rules.map((rule) => generateCopilotRule(root, rule)));
419
+ }
420
+ function buildCopilotPromptContent(skill) {
421
+ const lines = ["---", `mode: "${skill.mode}"`];
422
+ if (skill.description)
423
+ lines.push(`description: "${skill.description}"`);
424
+ lines.push("---", skill.content);
425
+ return lines.join(`
426
+ `);
427
+ }
428
+ function copilotPromptFilePath(root, skillName) {
429
+ return path7.join(root, ".github/prompts", `${skillName}.prompt.md`);
430
+ }
431
+ async function generateCopilotSkill(root, skill) {
432
+ const filePath = copilotPromptFilePath(root, skill.name);
433
+ await fs7.mkdir(path7.dirname(filePath), { recursive: true });
434
+ await fs7.writeFile(filePath, buildCopilotPromptContent(skill));
435
+ }
436
+ async function generateCopilotSkills(root, skills) {
437
+ await Promise.all(skills.map((skill) => generateCopilotSkill(root, skill)));
438
+ }
439
+ // src/opencode.ts
440
+ import path8 from "path";
441
+ import fs8 from "fs/promises";
442
+ async function readOpencode(root) {
443
+ try {
444
+ const content = await fs8.readFile(path8.join(root, "opencode.json"), "utf-8");
445
+ return JSON.parse(content);
446
+ } catch {
447
+ return null;
448
+ }
449
+ }
450
+ function buildOpencodeConfig(existing) {
451
+ return {
452
+ ...existing,
453
+ instructions: `${ONEAGENT_DIR}/instructions.md`
454
+ };
455
+ }
456
+ async function addOpenCodePlugin(root, id) {
457
+ const filePath = path8.join(root, "opencode.json");
458
+ let existing;
459
+ try {
460
+ existing = JSON.parse(await fs8.readFile(filePath, "utf-8"));
461
+ } catch {
462
+ return;
463
+ }
464
+ const current = Array.isArray(existing.plugin) ? existing.plugin : [];
465
+ if (current.includes(id))
466
+ return;
467
+ existing.plugin = [...current, id];
468
+ await fs8.writeFile(filePath, JSON.stringify(existing, null, 2) + `
469
+ `);
470
+ }
471
+ async function writeOpencode(root, _rules) {
472
+ const existing = await readOpencode(root);
473
+ const config = buildOpencodeConfig(existing);
474
+ await fs8.writeFile(path8.join(root, "opencode.json"), JSON.stringify(config, null, 2) + `
475
+ `);
476
+ }
477
+ // src/generate.ts
478
+ import path9 from "path";
479
+ import fs9 from "fs/promises";
480
+ async function detectGenerateCollisions(root, config) {
481
+ const [rules, skills] = await Promise.all([readRules(root), readSkills(root)]);
482
+ const targets = activeTargets(config);
483
+ const mainEntries = buildMainSymlinks(root, targets);
484
+ const ruleSkillEntries = [
485
+ ...buildRulesSymlinks(root, targets),
486
+ ...buildSkillSymlinks(root, targets),
487
+ ...buildCommandSymlinks(root, targets)
488
+ ];
489
+ const [mainCollisions, ruleSkillSymlinkCollisions] = await Promise.all([
490
+ Promise.all(mainEntries.map((entry) => readDetectedFile(root, path9.relative(root, entry.symlinkPath)))).then((files) => files.filter((f) => f !== null)),
491
+ Promise.all(ruleSkillEntries.map((entry) => readDetectedFile(root, path9.relative(root, entry.symlinkPath)))).then((files) => files.filter((f) => f !== null))
492
+ ]);
493
+ const copilotCollisions = [];
494
+ if (targets.includes("copilot")) {
495
+ const checks = await Promise.all([
496
+ ...rules.map(async (rule) => {
497
+ const filePath = copilotFilePath(root, rule.name);
498
+ try {
499
+ const [source, dest] = await Promise.all([
500
+ fs9.readFile(rule.path, "utf-8"),
501
+ fs9.readFile(filePath, "utf-8")
502
+ ]);
503
+ if (source === dest)
504
+ return null;
505
+ const stat = await fs9.lstat(filePath);
506
+ return { relativePath: path9.relative(root, filePath), absolutePath: filePath, sizeBytes: stat.size, modifiedAt: stat.mtime, content: dest };
507
+ } catch {
508
+ return null;
509
+ }
510
+ }),
511
+ ...skills.map(async (skill) => {
512
+ const filePath = copilotPromptFilePath(root, skill.name);
513
+ try {
514
+ const content = await fs9.readFile(filePath, "utf-8");
515
+ if (content === buildCopilotPromptContent(skill))
516
+ return null;
517
+ const stat = await fs9.lstat(filePath);
518
+ return { relativePath: path9.relative(root, filePath), absolutePath: filePath, sizeBytes: stat.size, modifiedAt: stat.mtime, content };
519
+ } catch {
520
+ return null;
521
+ }
522
+ })
523
+ ]);
524
+ copilotCollisions.push(...checks.filter((c) => c !== null));
525
+ }
526
+ return {
527
+ mainFiles: mainCollisions,
528
+ ruleSkillFiles: [...ruleSkillSymlinkCollisions, ...copilotCollisions]
529
+ };
530
+ }
531
+ async function generate(root, config) {
532
+ const [rules, skills] = await Promise.all([readRules(root), readSkills(root)]);
533
+ const targets = activeTargets(config);
534
+ await migrateRuleAndSkillFiles(root);
535
+ const mainSymlinks = buildMainSymlinks(root, targets);
536
+ const rulesSymlinks = buildRulesSymlinks(root, targets);
537
+ const skillSymlinks = await buildSkillSymlinks(root, targets);
538
+ const commandSymlinks = buildCommandSymlinks(root, targets);
539
+ await createAllSymlinks([...mainSymlinks, ...rulesSymlinks, ...skillSymlinks, ...commandSymlinks, ...buildAgentsDirSymlinks(root)]);
540
+ if (targets.includes("copilot")) {
541
+ await Promise.all([generateCopilotRules(root, rules), generateCopilotSkills(root, skills)]);
542
+ }
543
+ if (targets.includes("opencode")) {
544
+ await writeOpencode(root, rules);
545
+ }
546
+ }
547
+ // src/status.ts
548
+ import fs10 from "fs/promises";
549
+ async function checkGeneratedFile(root, rule) {
550
+ const filePath = copilotFilePath(root, rule.name);
551
+ try {
552
+ const [source, dest] = await Promise.all([
553
+ fs10.readFile(rule.path, "utf-8"),
554
+ fs10.readFile(filePath, "utf-8")
555
+ ]);
556
+ return { path: filePath, exists: true, upToDate: source === dest };
557
+ } catch {
558
+ return { path: filePath, exists: false, upToDate: false };
559
+ }
560
+ }
561
+ async function checkOpencodeStatus(root, _rules) {
562
+ const existing = await readOpencode(root);
563
+ if (!existing)
564
+ return { exists: false, valid: false };
565
+ return { exists: true, valid: existing["instructions"] === `${ONEAGENT_DIR}/instructions.md` };
566
+ }
567
+ async function checkCopilotPrompt(root, skill) {
568
+ const filePath = copilotPromptFilePath(root, skill.name);
569
+ const expected = buildCopilotPromptContent(skill);
570
+ try {
571
+ const content = await fs10.readFile(filePath, "utf-8");
572
+ return { path: filePath, exists: true, upToDate: content === expected };
573
+ } catch {
574
+ return { path: filePath, exists: false, upToDate: false };
575
+ }
576
+ }
577
+ async function checkStatus(root, config) {
578
+ const [rules, skills] = await Promise.all([readRules(root), readSkills(root)]);
579
+ const targets = activeTargets(config);
580
+ const allEntries = [
581
+ ...buildMainSymlinks(root, targets),
582
+ ...buildRulesSymlinks(root, targets),
583
+ ...buildSkillSymlinks(root, targets),
584
+ ...buildCommandSymlinks(root, targets),
585
+ ...buildAgentsDirSymlinks(root)
586
+ ];
587
+ const symlinks = await Promise.all(allEntries.map(checkSymlink));
588
+ const generatedFiles = targets.includes("copilot") ? await Promise.all([
589
+ ...rules.map((rule) => checkGeneratedFile(root, rule)),
590
+ ...skills.map((skill) => checkCopilotPrompt(root, skill))
591
+ ]) : [];
592
+ const opencode = await checkOpencodeStatus(root, rules);
593
+ return { symlinks, generatedFiles, opencode };
594
+ }
595
+ // src/apply-template.ts
596
+ import path10 from "path";
597
+ import fs11 from "fs/promises";
598
+ import { execFile } from "child_process";
599
+ import { promisify } from "util";
600
+ var execFileAsync = promisify(execFile);
601
+ function parseTemplateYaml(yamlText, fallbackName = "custom") {
602
+ const nameMatch = yamlText.match(/^name:\s*(.+)$/m);
603
+ const name = nameMatch?.[1]?.trim() ?? fallbackName;
604
+ const descMatch = yamlText.match(/^description:\s*(.+)$/m);
605
+ const description = descMatch?.[1]?.trim() ?? "";
606
+ const extendsMatch = yamlText.match(/^extends:\s*(.+)$/m);
607
+ const extendsValue = extendsMatch?.[1]?.trim();
608
+ const skills = parseSkillsFromYaml(yamlText);
609
+ const plugins = parsePluginsFromYaml(yamlText);
610
+ return { name, description, skills, plugins, ...extendsValue ? { extends: extendsValue } : {} };
611
+ }
612
+ function parseSkillsFromYaml(yamlText) {
613
+ const skills = [];
614
+ const section = yamlText.match(/^skills:\s*\n((?:(?: -.+|\s{4}.+)\n?)*)/m);
615
+ if (!section)
616
+ return skills;
617
+ const block = section[1];
618
+ const entries = block.split(/\n(?= -)/);
619
+ for (const entry of entries) {
620
+ const repoMatch = entry.match(/repo:\s*(\S+)/);
621
+ const skillMatch = entry.match(/skill:\s*(\S+)/);
622
+ if (repoMatch && skillMatch) {
623
+ skills.push({ repo: repoMatch[1].trim(), skill: skillMatch[1].trim() });
624
+ }
625
+ }
626
+ return skills;
627
+ }
628
+ function parsePluginsFromYaml(yamlText) {
629
+ const plugins = [];
630
+ const section = yamlText.match(/^plugins:\s*\n((?:(?: -.+|\s{4}.+)\n?)*)/m);
631
+ if (!section)
632
+ return plugins;
633
+ const block = section[1];
634
+ const entries = block.split(/\n(?= -)/);
635
+ for (const entry of entries) {
636
+ const targetMatch = entry.match(/target:\s*(\S+)/);
637
+ const idMatch = entry.match(/id:\s*(.+)/);
638
+ if (targetMatch && idMatch) {
639
+ plugins.push({
640
+ target: targetMatch[1].trim(),
641
+ id: idMatch[1].trim()
642
+ });
643
+ }
644
+ }
645
+ return plugins;
646
+ }
647
+ async function resolveExtends(child, resolveBuiltin) {
648
+ if (!child.extends)
649
+ return child;
650
+ let parent;
651
+ if (child.extends.startsWith("https://")) {
652
+ parent = await fetchTemplateFromGitHub(child.extends);
653
+ } else if (resolveBuiltin) {
654
+ const resolved = await resolveBuiltin(child.extends);
655
+ if (!resolved)
656
+ throw new Error(`Unknown builtin template: "${child.extends}"`);
657
+ parent = resolved;
658
+ } else {
659
+ throw new Error(`Cannot resolve extends: "${child.extends}"`);
660
+ }
661
+ return {
662
+ name: child.name,
663
+ description: child.description,
664
+ instructions: child.instructions,
665
+ skills: [...parent.skills, ...child.skills],
666
+ plugins: [...parent.plugins, ...child.plugins],
667
+ rules: [...parent.rules, ...child.rules]
668
+ };
669
+ }
670
+ async function applyTemplateFiles(root, template) {
671
+ const oneagentDir = path10.join(root, ONEAGENT_DIR);
672
+ await fs11.mkdir(path10.join(oneagentDir, "rules"), { recursive: true });
673
+ await fs11.mkdir(path10.join(oneagentDir, "skills"), { recursive: true });
674
+ await fs11.writeFile(path10.join(oneagentDir, "instructions.md"), template.instructions);
675
+ for (const rule of template.rules) {
676
+ await fs11.writeFile(path10.join(oneagentDir, "rules", `${rule.name}.md`), rule.content);
677
+ }
678
+ }
679
+ async function installTemplateSkills(root, template) {
680
+ const installed = [];
681
+ const failed = [];
682
+ for (const entry of template.skills) {
683
+ try {
684
+ await execFileAsync("npx", ["skills", "add", entry.repo, "--skill", entry.skill, "--agent", "universal", "--yes"], { cwd: root });
685
+ installed.push(entry);
686
+ } catch (err) {
687
+ const reason = err instanceof Error ? err.message : String(err);
688
+ failed.push({ entry, reason });
689
+ }
690
+ }
691
+ return { installed, failed };
692
+ }
693
+ var BUILTIN_SKILL_REPO = "https://github.com/moskalakamil/oneagent";
694
+ var BUILTIN_SKILL_NAME = "oneagent";
695
+ async function installBuiltinSkill(root) {
696
+ try {
697
+ await execFileAsync("npx", ["skills", "add", BUILTIN_SKILL_REPO, "--skill", BUILTIN_SKILL_NAME, "--agent", "universal", "--yes"], { cwd: root });
698
+ return true;
699
+ } catch {
700
+ return false;
701
+ }
702
+ }
703
+ async function installTemplatePlugins(root, template, activeTargets2) {
704
+ const installed = [];
705
+ const manual = [];
706
+ const failed = [];
707
+ for (const plugin of template.plugins) {
708
+ if (!activeTargets2.includes(plugin.target))
709
+ continue;
710
+ try {
711
+ switch (plugin.target) {
712
+ case "claude":
713
+ await execFileAsync("claude", ["plugin", "install", plugin.id], { cwd: root });
714
+ installed.push(plugin);
715
+ break;
716
+ case "copilot":
717
+ await execFileAsync("copilot", ["plugin", "install", plugin.id], { cwd: root });
718
+ installed.push(plugin);
719
+ break;
720
+ case "opencode":
721
+ await addOpenCodePlugin(root, plugin.id);
722
+ installed.push(plugin);
723
+ break;
724
+ case "cursor":
725
+ manual.push(plugin);
726
+ break;
727
+ case "windsurf":
728
+ break;
729
+ }
730
+ } catch (err) {
731
+ const reason = err instanceof Error ? err.message : String(err);
732
+ failed.push({ plugin, reason });
733
+ }
734
+ }
735
+ return { installed, manual, failed };
736
+ }
737
+ async function fetchTemplateFromGitHub(url) {
738
+ const { owner, repo, branch, subdir } = parseGitHubUrl(url);
739
+ const branchExplicit = url.includes("/tree/");
740
+ const resolvedBranch = branchExplicit ? branch : await fetchDefaultBranch(owner, repo);
741
+ const rawBase = `https://raw.githubusercontent.com/${owner}/${repo}/${resolvedBranch}${subdir ? `/${subdir}` : ""}`;
742
+ const [yamlText, instructions] = await Promise.all([
743
+ fetchText(`${rawBase}/template.yml`),
744
+ fetchText(`${rawBase}/instructions.md`)
745
+ ]);
746
+ const parsed = parseTemplateYaml(yamlText);
747
+ const rules = await fetchGitHubRules(url);
748
+ const base = { ...parsed, instructions, rules };
749
+ return resolveExtends(base);
750
+ }
751
+ async function fetchDefaultBranch(owner, repo) {
752
+ try {
753
+ const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
754
+ headers: { Accept: "application/vnd.github.v3+json" }
755
+ });
756
+ if (!response.ok)
757
+ return "main";
758
+ const data = await response.json();
759
+ return data.default_branch ?? "main";
760
+ } catch {
761
+ return "main";
762
+ }
763
+ }
764
+ async function fetchText(url) {
765
+ const response = await fetch(url);
766
+ if (!response.ok) {
767
+ throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
768
+ }
769
+ return response.text();
770
+ }
771
+ function parseGitHubUrl(url) {
772
+ const match = url.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\/tree\/([^/]+?)(?:\/(.+))?)?(?:\/)?$/);
773
+ if (!match) {
774
+ throw new Error(`Invalid GitHub URL: "${url}". Expected format: https://github.com/owner/repo`);
775
+ }
776
+ const [, owner, repo, branch = "main", subdir = ""] = match;
777
+ return { owner, repo, branch, subdir };
778
+ }
779
+ async function fetchGitHubRules(repoUrl) {
780
+ const { owner, repo, branch, subdir } = parseGitHubUrl(repoUrl);
781
+ const rulesPath = subdir ? `${subdir}/rules` : "rules";
782
+ const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${rulesPath}?ref=${branch}`;
783
+ try {
784
+ const response = await fetch(apiUrl, {
785
+ headers: { Accept: "application/vnd.github.v3+json" }
786
+ });
787
+ if (!response.ok)
788
+ return [];
789
+ const files = await response.json();
790
+ const mdFiles = files.filter((f) => f.name.endsWith(".md") && f.download_url);
791
+ const rules = await Promise.all(mdFiles.map(async (f) => {
792
+ const content = await fetchText(f.download_url);
793
+ return { name: path10.basename(f.name, ".md"), content };
794
+ }));
795
+ return rules;
796
+ } catch {
797
+ return [];
798
+ }
799
+ }
800
+ export {
801
+ writeOpencode,
802
+ writeConfig,
803
+ resolveExtends,
804
+ removeDeprecatedFiles,
805
+ readSkills,
806
+ readSkillFile,
807
+ readRules,
808
+ readOpencode,
809
+ readDetectedFile,
810
+ readConfig,
811
+ readCommands,
812
+ parseTemplateYaml,
813
+ parseSkillsFromYaml,
814
+ parseSkillFrontmatter,
815
+ parsePluginsFromYaml,
816
+ migrateRuleAndSkillFiles,
817
+ makeTargets,
818
+ installTemplateSkills,
819
+ installTemplatePlugins,
820
+ installBuiltinSkill,
821
+ getAgentDef,
822
+ generateCopilotSkills,
823
+ generateCopilotSkill,
824
+ generateCopilotRules,
825
+ generateCopilotRule,
826
+ generate,
827
+ filesHaveSameContent,
828
+ fetchTemplateFromGitHub,
829
+ ensureDir,
830
+ detectGenerateCollisions,
831
+ detectExistingFiles,
832
+ createSymlink,
833
+ createAllSymlinks,
834
+ copilotPromptFilePath,
835
+ copilotFilePath,
836
+ configExists,
837
+ cleanupAgentDir,
838
+ checkSymlink,
839
+ checkStatus,
840
+ checkOpencodeStatus,
841
+ checkGeneratedFile,
842
+ checkCopilotPrompt,
843
+ buildSkillSymlinks,
844
+ buildRulesSymlinks,
845
+ buildOpencodeConfig,
846
+ buildMainSymlinks,
847
+ buildCopilotPromptContent,
848
+ buildCommandSymlinks,
849
+ buildAgentsDirSymlinks,
850
+ applyTemplateFiles,
851
+ addOpenCodePlugin,
852
+ activeTargets,
853
+ ONEAGENT_DIR,
854
+ ALL_AGENT_TARGETS,
855
+ AGENT_FILES,
856
+ AGENT_DEFINITIONS
857
+ };