oneagent 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +299 -196
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -609,7 +609,7 @@ var import_picocolors, import_sisteransi, at = (t) => t === 161 || t === 164 ||
609
609
  ` && (r && p && (i += st(r)), n && (i += it(n))), V += h.length, m = A, A = g.next();
610
610
  }
611
611
  return i;
612
- }, At, _, bt, z, rt = (t) => ("columns" in t) && typeof t.columns == "number" ? t.columns : 80, nt = (t) => ("rows" in t) && typeof t.rows == "number" ? t.rows : 20, Vt, kt, yt, Lt, Wt;
612
+ }, At, _, bt, z, rt = (t) => ("columns" in t) && typeof t.columns == "number" ? t.columns : 80, nt = (t) => ("rows" in t) && typeof t.rows == "number" ? t.rows : 20, Vt, kt, yt, Lt, Wt, $t;
613
613
  var init_dist2 = __esm(() => {
614
614
  import_picocolors = __toESM(require_picocolors(), 1);
615
615
  import_sisteransi = __toESM(require_src(), 1);
@@ -826,6 +826,27 @@ var init_dist2 = __esm(() => {
826
826
  });
827
827
  }
828
828
  };
829
+ $t = class $t extends x {
830
+ get userInputWithCursor() {
831
+ if (this.state === "submit")
832
+ return this.userInput;
833
+ const e = this.userInput;
834
+ if (this.cursor >= e.length)
835
+ return `${this.userInput}█`;
836
+ const s = e.slice(0, this.cursor), [i, ...r] = e.slice(this.cursor);
837
+ return `${s}${import_picocolors.default.inverse(i)}${r.join("")}`;
838
+ }
839
+ get cursor() {
840
+ return this._cursor;
841
+ }
842
+ constructor(e) {
843
+ super({ ...e, initialUserInput: e.initialUserInput ?? e.initialValue }), this.on("userInput", (s) => {
844
+ this._setValue(s);
845
+ }), this.on("finalize", () => {
846
+ this.value || (this.value = e.defaultValue), this.value === undefined && (this.value = "");
847
+ });
848
+ }
849
+ };
829
850
  });
830
851
 
831
852
  // ../../node_modules/.bun/@clack+prompts@1.0.1/node_modules/@clack/prompts/dist/index.mjs
@@ -1067,7 +1088,7 @@ ${l}
1067
1088
  }
1068
1089
  }
1069
1090
  } }).prompt();
1070
- }, We = (t = "", r) => {
1091
+ }, R2, We = (t = "", r) => {
1071
1092
  (r?.output ?? process.stdout).write(`${import_picocolors2.default.gray(ht2)} ${t}
1072
1093
  `);
1073
1094
  }, Le = (t = "", r) => {
@@ -1244,7 +1265,35 @@ ${n}
1244
1265
  }
1245
1266
  }
1246
1267
  } }).prompt();
1247
- }, Qt;
1268
+ }, Qt, Ze = (t) => new $t({ validate: t.validate, placeholder: t.placeholder, defaultValue: t.defaultValue, initialValue: t.initialValue, output: t.output, signal: t.signal, input: t.input, render() {
1269
+ const r = t?.withGuide ?? _.withGuide, s = `${`${r ? `${import_picocolors2.default.gray(d)}
1270
+ ` : ""}${W2(this.state)} `}${t.message}
1271
+ `, i = t.placeholder ? import_picocolors2.default.inverse(t.placeholder[0]) + import_picocolors2.default.dim(t.placeholder.slice(1)) : import_picocolors2.default.inverse(import_picocolors2.default.hidden("_")), a = this.userInput ? this.userInputWithCursor : i, o = this.value ?? "";
1272
+ switch (this.state) {
1273
+ case "error": {
1274
+ const u = this.error ? ` ${import_picocolors2.default.yellow(this.error)}` : "", l = r ? `${import_picocolors2.default.yellow(d)} ` : "", n = r ? import_picocolors2.default.yellow(x2) : "";
1275
+ return `${s.trim()}
1276
+ ${l}${a}
1277
+ ${n}${u}
1278
+ `;
1279
+ }
1280
+ case "submit": {
1281
+ const u = o ? ` ${import_picocolors2.default.dim(o)}` : "", l = r ? import_picocolors2.default.gray(d) : "";
1282
+ return `${s}${l}${u}`;
1283
+ }
1284
+ case "cancel": {
1285
+ const u = o ? ` ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(o))}` : "", l = r ? import_picocolors2.default.gray(d) : "";
1286
+ return `${s}${l}${u}${o.trim() ? `
1287
+ ${l}` : ""}`;
1288
+ }
1289
+ default: {
1290
+ const u = r ? `${import_picocolors2.default.cyan(d)} ` : "", l = r ? import_picocolors2.default.cyan(x2) : "";
1291
+ return `${s}${u}${a}
1292
+ ${l}
1293
+ `;
1294
+ }
1295
+ }
1296
+ } }).prompt();
1248
1297
  var init_dist3 = __esm(() => {
1249
1298
  init_dist2();
1250
1299
  init_dist2();
@@ -1286,10 +1335,58 @@ var init_dist3 = __esm(() => {
1286
1335
  Ee = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 };
1287
1336
  St2 = `${Ae}8;;`;
1288
1337
  Ht = new RegExp(`(?:\\${kt2}(?<code>\\d+)m|\\${St2}(?<uri>.*)${Ct2})`, "y");
1338
+ R2 = { message: (t = [], { symbol: r = import_picocolors2.default.gray(d), secondarySymbol: s = import_picocolors2.default.gray(d), output: i = process.stdout, spacing: a = 1, withGuide: o } = {}) => {
1339
+ const u = [], l = o ?? _.withGuide, n = l ? s : "", c = l ? `${r} ` : "", g = l ? `${s} ` : "";
1340
+ for (let p = 0;p < a; p++)
1341
+ u.push(n);
1342
+ const F = Array.isArray(t) ? t : t.split(`
1343
+ `);
1344
+ if (F.length > 0) {
1345
+ const [p, ...E] = F;
1346
+ p.length > 0 ? u.push(`${c}${p}`) : u.push(l ? r : "");
1347
+ for (const $ of E)
1348
+ $.length > 0 ? u.push(`${g}${$}`) : u.push(l ? s : "");
1349
+ }
1350
+ i.write(`${u.join(`
1351
+ `)}
1352
+ `);
1353
+ }, info: (t, r) => {
1354
+ R2.message(t, { ...r, symbol: import_picocolors2.default.blue(ft2) });
1355
+ }, success: (t, r) => {
1356
+ R2.message(t, { ...r, symbol: import_picocolors2.default.green(Ft2) });
1357
+ }, step: (t, r) => {
1358
+ R2.message(t, { ...r, symbol: import_picocolors2.default.green(V) });
1359
+ }, warn: (t, r) => {
1360
+ R2.message(t, { ...r, symbol: import_picocolors2.default.yellow(yt2) });
1361
+ }, warning: (t, r) => {
1362
+ R2.warn(t, r);
1363
+ }, error: (t, r) => {
1364
+ R2.message(t, { ...r, symbol: import_picocolors2.default.red(Et2) });
1365
+ } };
1289
1366
  Ke = import_picocolors2.default.magenta;
1290
1367
  zt = { light: C("─", "-"), heavy: C("━", "="), block: C("█", "#") };
1291
1368
  Qt = `${import_picocolors2.default.gray(d)} `;
1292
1369
  });
1370
+
1371
+ // src/utils.ts
1372
+ function timeAgo(date) {
1373
+ const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
1374
+ if (seconds < 60)
1375
+ return `${seconds}s ago`;
1376
+ const minutes = Math.floor(seconds / 60);
1377
+ if (minutes < 60)
1378
+ return `${minutes}m ago`;
1379
+ const hours = Math.floor(minutes / 60);
1380
+ if (hours < 24)
1381
+ return `${hours}h ago`;
1382
+ const days = Math.floor(hours / 24);
1383
+ if (days < 30)
1384
+ return `${days}d ago`;
1385
+ const months = Math.floor(days / 30);
1386
+ if (months < 12)
1387
+ return `${months}mo ago`;
1388
+ return `${Math.floor(months / 12)}y ago`;
1389
+ }
1293
1390
  // ../core/src/agents.ts
1294
1391
  var AGENT_DEFINITIONS;
1295
1392
  var init_agents = __esm(() => {
@@ -1301,7 +1398,8 @@ var init_agents = __esm(() => {
1301
1398
  detectIndicators: ["CLAUDE.md", ".claude"],
1302
1399
  mainFile: "CLAUDE.md",
1303
1400
  rulesDir: ".claude/rules",
1304
- skillsDir: ".claude/skills"
1401
+ skillsDir: ".claude/skills",
1402
+ commandsDir: ".claude/commands"
1305
1403
  },
1306
1404
  {
1307
1405
  target: "cursor",
@@ -1311,7 +1409,8 @@ var init_agents = __esm(() => {
1311
1409
  mainFile: "AGENTS.md",
1312
1410
  rulesDir: ".cursor/rules",
1313
1411
  skillsDir: ".cursor/skills",
1314
- deprecatedFiles: [".cursorrules"]
1412
+ deprecatedFiles: [".cursorrules"],
1413
+ commandsDir: ".cursor/commands"
1315
1414
  },
1316
1415
  {
1317
1416
  target: "windsurf",
@@ -1330,7 +1429,8 @@ var init_agents = __esm(() => {
1330
1429
  detectIndicators: ["opencode.json", ".opencode"],
1331
1430
  mainFile: "AGENTS.md",
1332
1431
  rulesDir: ".opencode/rules",
1333
- skillsDir: ".opencode/skills"
1432
+ skillsDir: ".opencode/skills",
1433
+ commandsDir: ".opencode/commands"
1334
1434
  },
1335
1435
  {
1336
1436
  target: "copilot",
@@ -8366,15 +8466,6 @@ async function removeDeprecatedFiles(root) {
8366
8466
  } catch {}
8367
8467
  }
8368
8468
  }
8369
- async function detectDeprecatedCommandFiles(root) {
8370
- const commandsDir = path2.join(root, ".claude/commands");
8371
- try {
8372
- const entries = await fs2.readdir(commandsDir, { withFileTypes: true });
8373
- return entries.filter((e2) => e2.isFile()).map((e2) => path2.join(".claude/commands", e2.name));
8374
- } catch {
8375
- return [];
8376
- }
8377
- }
8378
8469
  var AGENT_FILES, DEPRECATED_FILES;
8379
8470
  var init_detect = __esm(() => {
8380
8471
  init_agents();
@@ -8391,34 +8482,20 @@ var init_detect = __esm(() => {
8391
8482
  // ../core/src/rules.ts
8392
8483
  import path3 from "path";
8393
8484
  import fs3 from "fs/promises";
8394
- function parseFrontmatter(raw) {
8395
- const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
8396
- if (!match)
8397
- return { applyTo: "**", content: raw };
8398
- const frontmatter = match[1] ?? "";
8399
- const content = match[2] ?? "";
8400
- const applyToMatch = frontmatter.match(/applyTo:\s*["']?([^"'\n]+)["']?/);
8401
- const applyTo = applyToMatch?.[1]?.trim() ?? "**";
8402
- return { applyTo, content };
8403
- }
8404
- async function readRuleFile(filePath) {
8405
- const raw = await fs3.readFile(filePath, "utf-8");
8406
- const { applyTo, content } = parseFrontmatter(raw);
8407
- return { name: path3.basename(filePath, ".md"), path: filePath, applyTo, content };
8408
- }
8409
8485
  async function readRules(root) {
8410
8486
  const rulesDir = path3.join(root, ".oneagent/rules");
8411
8487
  try {
8412
8488
  const files = await fs3.readdir(rulesDir);
8413
- const mdFiles = files.filter((f) => f.endsWith(".md"));
8414
- const rules = await Promise.all(mdFiles.map((f) => readRuleFile(path3.join(rulesDir, f))));
8415
- return rules.sort((a, b) => a.name.localeCompare(b.name));
8489
+ 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));
8416
8490
  } catch {
8417
8491
  return [];
8418
8492
  }
8419
8493
  }
8420
8494
  var init_rules = () => {};
8421
8495
 
8496
+ // ../core/src/commands.ts
8497
+ var init_commands = () => {};
8498
+
8422
8499
  // ../core/src/skills.ts
8423
8500
  import path4 from "path";
8424
8501
  import fs4 from "fs/promises";
@@ -8502,6 +8579,13 @@ function buildSkillSymlinks(root, targets) {
8502
8579
  return { symlinkPath, target: relativeTarget(symlinkPath, targetAbs), label: d2.skillsDir };
8503
8580
  });
8504
8581
  }
8582
+ function buildCommandSymlinks(root, targets) {
8583
+ const targetAbs = path5.join(root, ".oneagent/commands");
8584
+ return AGENT_DEFINITIONS.filter((d2) => targets.includes(d2.target) && d2.commandsDir).map((d2) => {
8585
+ const symlinkPath = path5.join(root, d2.commandsDir);
8586
+ return { symlinkPath, target: relativeTarget(symlinkPath, targetAbs), label: d2.commandsDir };
8587
+ });
8588
+ }
8505
8589
  function buildAgentsDirSymlinks(root) {
8506
8590
  const symlinkPath = path5.join(root, ".agents/skills");
8507
8591
  const targetAbs = path5.join(root, ".oneagent/skills");
@@ -8557,14 +8641,21 @@ async function migrateAndRemoveDir(src, dest, root) {
8557
8641
  async function migrateRuleAndSkillFiles(root) {
8558
8642
  const destRules = path5.join(root, ".oneagent/rules");
8559
8643
  const destSkills = path5.join(root, ".oneagent/skills");
8560
- await migrateAndRemoveDir(path5.join(root, ".cursor/rules"), destRules, root);
8561
- await migrateAndRemoveDir(path5.join(root, ".claude/rules"), destRules, root);
8562
- await migrateAndRemoveDir(path5.join(root, ".windsurf/rules"), destRules, root);
8563
- await migrateAndRemoveDir(path5.join(root, ".opencode/rules"), destRules, root);
8644
+ const destCommands = path5.join(root, ".oneagent/commands");
8645
+ for (const def of AGENT_DEFINITIONS) {
8646
+ if (def.rulesDir)
8647
+ await migrateAndRemoveDir(path5.join(root, def.rulesDir), destRules, root);
8648
+ }
8564
8649
  await migrateAndRemoveDir(path5.join(root, ".agents/skills"), destSkills, root);
8650
+ for (const def of AGENT_DEFINITIONS) {
8651
+ if (def.commandsDir)
8652
+ await migrateAndRemoveDir(path5.join(root, def.commandsDir), destCommands, root);
8653
+ }
8565
8654
  }
8566
8655
  async function createAllSymlinks(entries) {
8567
- await Promise.all(entries.map((e2) => createSymlink(e2.symlinkPath, e2.target)));
8656
+ for (const e2 of entries) {
8657
+ await createSymlink(e2.symlinkPath, e2.target);
8658
+ }
8568
8659
  }
8569
8660
  async function checkSymlink(entry) {
8570
8661
  try {
@@ -8585,19 +8676,13 @@ var init_symlinks = __esm(() => {
8585
8676
  // ../core/src/copilot.ts
8586
8677
  import path6 from "path";
8587
8678
  import fs6 from "fs/promises";
8588
- function buildCopilotContent(rule) {
8589
- return `---
8590
- applyTo: "${rule.applyTo}"
8591
- ---
8592
- ${rule.content}`;
8593
- }
8594
8679
  function copilotFilePath(root, ruleName) {
8595
8680
  return path6.join(root, ".github/instructions", `${ruleName}.instructions.md`);
8596
8681
  }
8597
8682
  async function generateCopilotRule(root, rule) {
8598
- const filePath = copilotFilePath(root, rule.name);
8599
- await fs6.mkdir(path6.dirname(filePath), { recursive: true });
8600
- await fs6.writeFile(filePath, buildCopilotContent(rule));
8683
+ const destPath = copilotFilePath(root, rule.name);
8684
+ await fs6.mkdir(path6.dirname(destPath), { recursive: true });
8685
+ await fs6.copyFile(rule.path, destPath);
8601
8686
  }
8602
8687
  async function generateCopilotRules(root, rules) {
8603
8688
  await Promise.all(rules.map((rule) => generateCopilotRule(root, rule)));
@@ -8672,7 +8757,8 @@ async function detectGenerateCollisions(root, config) {
8672
8757
  const mainEntries = buildMainSymlinks(root, targets);
8673
8758
  const ruleSkillEntries = [
8674
8759
  ...buildRulesSymlinks(root, targets),
8675
- ...buildSkillSymlinks(root, targets)
8760
+ ...buildSkillSymlinks(root, targets),
8761
+ ...buildCommandSymlinks(root, targets)
8676
8762
  ];
8677
8763
  const [mainCollisions, ruleSkillSymlinkCollisions] = await Promise.all([
8678
8764
  Promise.all(mainEntries.map((entry) => readDetectedFile(root, path8.relative(root, entry.symlinkPath)))).then((files) => files.filter((f) => f !== null)),
@@ -8684,11 +8770,14 @@ async function detectGenerateCollisions(root, config) {
8684
8770
  ...rules.map(async (rule) => {
8685
8771
  const filePath = copilotFilePath(root, rule.name);
8686
8772
  try {
8687
- const content = await fs8.readFile(filePath, "utf-8");
8688
- if (content === buildCopilotContent(rule))
8773
+ const [source, dest] = await Promise.all([
8774
+ fs8.readFile(rule.path, "utf-8"),
8775
+ fs8.readFile(filePath, "utf-8")
8776
+ ]);
8777
+ if (source === dest)
8689
8778
  return null;
8690
8779
  const stat = await fs8.lstat(filePath);
8691
- return { relativePath: path8.relative(root, filePath), absolutePath: filePath, sizeBytes: stat.size, modifiedAt: stat.mtime, content };
8780
+ return { relativePath: path8.relative(root, filePath), absolutePath: filePath, sizeBytes: stat.size, modifiedAt: stat.mtime, content: dest };
8692
8781
  } catch {
8693
8782
  return null;
8694
8783
  }
@@ -8720,7 +8809,8 @@ async function generate(root, config) {
8720
8809
  const mainSymlinks = buildMainSymlinks(root, targets);
8721
8810
  const rulesSymlinks = buildRulesSymlinks(root, targets);
8722
8811
  const skillSymlinks = await buildSkillSymlinks(root, targets);
8723
- await createAllSymlinks([...mainSymlinks, ...rulesSymlinks, ...skillSymlinks, ...buildAgentsDirSymlinks(root)]);
8812
+ const commandSymlinks = buildCommandSymlinks(root, targets);
8813
+ await createAllSymlinks([...mainSymlinks, ...rulesSymlinks, ...skillSymlinks, ...commandSymlinks, ...buildAgentsDirSymlinks(root)]);
8724
8814
  if (targets.includes("copilot")) {
8725
8815
  await Promise.all([generateCopilotRules(root, rules), generateCopilotSkills(root, skills)]);
8726
8816
  }
@@ -8742,10 +8832,12 @@ var init_generate = __esm(() => {
8742
8832
  import fs9 from "fs/promises";
8743
8833
  async function checkGeneratedFile(root, rule) {
8744
8834
  const filePath = copilotFilePath(root, rule.name);
8745
- const expected = buildCopilotContent(rule);
8746
8835
  try {
8747
- const content = await fs9.readFile(filePath, "utf-8");
8748
- return { path: filePath, exists: true, upToDate: content === expected };
8836
+ const [source, dest] = await Promise.all([
8837
+ fs9.readFile(rule.path, "utf-8"),
8838
+ fs9.readFile(filePath, "utf-8")
8839
+ ]);
8840
+ return { path: filePath, exists: true, upToDate: source === dest };
8749
8841
  } catch {
8750
8842
  return { path: filePath, exists: false, upToDate: false };
8751
8843
  }
@@ -8773,6 +8865,7 @@ async function checkStatus(root, config) {
8773
8865
  ...buildMainSymlinks(root, targets),
8774
8866
  ...buildRulesSymlinks(root, targets),
8775
8867
  ...buildSkillSymlinks(root, targets),
8868
+ ...buildCommandSymlinks(root, targets),
8776
8869
  ...buildAgentsDirSymlinks(root)
8777
8870
  ];
8778
8871
  const symlinks = await Promise.all(allEntries.map(checkSymlink));
@@ -8797,6 +8890,31 @@ import path9 from "path";
8797
8890
  import fs10 from "fs/promises";
8798
8891
  import { execFile } from "child_process";
8799
8892
  import { promisify } from "util";
8893
+ function parseTemplateYaml(yamlText, fallbackName = "custom") {
8894
+ const nameMatch = yamlText.match(/^name:\s*(.+)$/m);
8895
+ const name = nameMatch?.[1]?.trim() ?? fallbackName;
8896
+ const descMatch = yamlText.match(/^description:\s*(.+)$/m);
8897
+ const description = descMatch?.[1]?.trim() ?? "";
8898
+ const skills = parseSkillsFromYaml(yamlText);
8899
+ const plugins = parsePluginsFromYaml(yamlText);
8900
+ return { name, description, skills, plugins };
8901
+ }
8902
+ function parseSkillsFromYaml(yamlText) {
8903
+ const skills = [];
8904
+ const section = yamlText.match(/^skills:\s*\n((?:(?: -.+|\s{4}.+)\n?)*)/m);
8905
+ if (!section)
8906
+ return skills;
8907
+ const block = section[1];
8908
+ const entries = block.split(/\n(?= -)/);
8909
+ for (const entry of entries) {
8910
+ const repoMatch = entry.match(/repo:\s*(\S+)/);
8911
+ const skillMatch = entry.match(/skill:\s*(\S+)/);
8912
+ if (repoMatch && skillMatch) {
8913
+ skills.push({ repo: repoMatch[1].trim(), skill: skillMatch[1].trim() });
8914
+ }
8915
+ }
8916
+ return skills;
8917
+ }
8800
8918
  function parsePluginsFromYaml(yamlText) {
8801
8919
  const plugins = [];
8802
8920
  const section = yamlText.match(/^plugins:\s*\n((?:(?: -.+|\s{4}.+)\n?)*)/m);
@@ -8825,47 +8943,54 @@ async function applyTemplateFiles(root, template) {
8825
8943
  await fs10.writeFile(path9.join(oneagentDir, "rules", `${rule.name}.md`), rule.content);
8826
8944
  }
8827
8945
  }
8828
- async function installTemplateSkills(root, template, onSkillInstalled) {
8829
- for (const identifier of template.skills) {
8946
+ async function installTemplateSkills(root, template) {
8947
+ const results = await Promise.all(template.skills.map(async (entry) => {
8830
8948
  try {
8831
- await execFileAsync("npx", ["skills", "add", identifier, "--agent", "universal", "--yes"], { cwd: root });
8832
- onSkillInstalled?.(identifier);
8949
+ await execFileAsync("npx", ["skills", "add", entry.repo, "--skill", entry.skill, "--agent", "universal", "--yes"], { cwd: root });
8950
+ return { entry, ok: true };
8833
8951
  } catch (err) {
8834
- const message = err instanceof Error ? err.message : String(err);
8835
- throw new Error(`Failed to install skill "${identifier}": ${message}`);
8952
+ const reason = err instanceof Error ? err.message : String(err);
8953
+ return { entry, ok: false, reason };
8836
8954
  }
8837
- }
8955
+ }));
8956
+ return {
8957
+ installed: results.filter((r) => r.ok).map((r) => r.entry),
8958
+ failed: results.filter((r) => !r.ok).map((r) => ({ entry: r.entry, reason: r.reason }))
8959
+ };
8838
8960
  }
8839
- async function installTemplatePlugins(root, template, activeTargets2, onPluginInstalled) {
8961
+ async function installTemplatePlugins(root, template, activeTargets2) {
8840
8962
  const installed = [];
8841
8963
  const manual = [];
8964
+ const failed = [];
8842
8965
  for (const plugin of template.plugins) {
8843
8966
  if (!activeTargets2.includes(plugin.target))
8844
8967
  continue;
8845
- switch (plugin.target) {
8846
- case "claude":
8847
- await execFileAsync("claude", ["plugin", "install", plugin.id], { cwd: root });
8848
- installed.push(plugin);
8849
- onPluginInstalled?.(plugin);
8850
- break;
8851
- case "copilot":
8852
- await execFileAsync("copilot", ["plugin", "install", plugin.id], { cwd: root });
8853
- installed.push(plugin);
8854
- onPluginInstalled?.(plugin);
8855
- break;
8856
- case "opencode":
8857
- await addOpenCodePlugin(root, plugin.id);
8858
- installed.push(plugin);
8859
- onPluginInstalled?.(plugin);
8860
- break;
8861
- case "cursor":
8862
- manual.push(plugin);
8863
- break;
8864
- case "windsurf":
8865
- break;
8968
+ try {
8969
+ switch (plugin.target) {
8970
+ case "claude":
8971
+ await execFileAsync("claude", ["plugin", "install", plugin.id], { cwd: root });
8972
+ installed.push(plugin);
8973
+ break;
8974
+ case "copilot":
8975
+ await execFileAsync("copilot", ["plugin", "install", plugin.id], { cwd: root });
8976
+ installed.push(plugin);
8977
+ break;
8978
+ case "opencode":
8979
+ await addOpenCodePlugin(root, plugin.id);
8980
+ installed.push(plugin);
8981
+ break;
8982
+ case "cursor":
8983
+ manual.push(plugin);
8984
+ break;
8985
+ case "windsurf":
8986
+ break;
8987
+ }
8988
+ } catch (err) {
8989
+ const reason = err instanceof Error ? err.message : String(err);
8990
+ failed.push({ plugin, reason });
8866
8991
  }
8867
8992
  }
8868
- return { installed, manual };
8993
+ return { installed, manual, failed };
8869
8994
  }
8870
8995
  async function fetchTemplateFromGitHub(url) {
8871
8996
  const rawBase = githubUrlToRawBase(url);
@@ -8873,22 +8998,7 @@ async function fetchTemplateFromGitHub(url) {
8873
8998
  fetchText(`${rawBase}/template.yml`),
8874
8999
  fetchText(`${rawBase}/instructions.md`)
8875
9000
  ]);
8876
- const descMatch = yamlText.match(/^description:\s*(.+)$/m);
8877
- const description = descMatch?.[1]?.trim() ?? "";
8878
- const nameMatch = yamlText.match(/^name:\s*(.+)$/m);
8879
- const name = nameMatch?.[1]?.trim() ?? "custom";
8880
- const skills = [];
8881
- const skillsBlockMatch = yamlText.match(/^skills:\s*\n((?: - .+\n?)*)/m);
8882
- if (skillsBlockMatch) {
8883
- const lines = skillsBlockMatch[1].split(`
8884
- `).filter(Boolean);
8885
- for (const line of lines) {
8886
- const skill = line.replace(/^\s*-\s*/, "").trim();
8887
- if (skill)
8888
- skills.push(skill);
8889
- }
8890
- }
8891
- const plugins = parsePluginsFromYaml(yamlText);
9001
+ const { name, description, skills, plugins } = parseTemplateYaml(yamlText);
8892
9002
  const rules = await fetchGitHubRules(url);
8893
9003
  return { name, description, skills, plugins, instructions, rules };
8894
9004
  }
@@ -8899,20 +9009,23 @@ async function fetchText(url) {
8899
9009
  }
8900
9010
  return response.text();
8901
9011
  }
8902
- function githubUrlToRawBase(url) {
8903
- const match = url.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\/tree\/([^/]+))?(?:\/.*)?$/);
9012
+ function parseGitHubUrl(url) {
9013
+ const match = url.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\/tree\/([^/]+?)(?:\/(.+))?)?(?:\/)?$/);
8904
9014
  if (!match) {
8905
9015
  throw new Error(`Invalid GitHub URL: "${url}". Expected format: https://github.com/owner/repo`);
8906
9016
  }
8907
- const [, owner, repo, branch = "main"] = match;
8908
- return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
9017
+ const [, owner, repo, branch = "main", subdir = ""] = match;
9018
+ return { owner, repo, branch, subdir };
9019
+ }
9020
+ function githubUrlToRawBase(url) {
9021
+ const { owner, repo, branch, subdir } = parseGitHubUrl(url);
9022
+ const base = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
9023
+ return subdir ? `${base}/${subdir}` : base;
8909
9024
  }
8910
9025
  async function fetchGitHubRules(repoUrl) {
8911
- const match = repoUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\/tree\/([^/]+))?(?:\/.*)?$/);
8912
- if (!match)
8913
- return [];
8914
- const [, owner, repo, branch = "main"] = match;
8915
- const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/rules?ref=${branch}`;
9026
+ const { owner, repo, branch, subdir } = parseGitHubUrl(repoUrl);
9027
+ const rulesPath = subdir ? `${subdir}/rules` : "rules";
9028
+ const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${rulesPath}?ref=${branch}`;
8916
9029
  try {
8917
9030
  const response = await fetch(apiUrl, {
8918
9031
  headers: { Accept: "application/vnd.github.v3+json" }
@@ -8942,6 +9055,7 @@ var init_src = __esm(() => {
8942
9055
  init_config();
8943
9056
  init_detect();
8944
9057
  init_rules();
9058
+ init_commands();
8945
9059
  init_skills();
8946
9060
  init_symlinks();
8947
9061
  init_copilot();
@@ -8951,39 +9065,6 @@ var init_src = __esm(() => {
8951
9065
  init_apply_template();
8952
9066
  });
8953
9067
 
8954
- // src/utils.ts
8955
- async function warnDeprecatedCommandFiles(root) {
8956
- const deprecated = await detectDeprecatedCommandFiles(root);
8957
- if (deprecated.length === 0)
8958
- return;
8959
- Ve(deprecated.map((f) => ` • ${f}`).join(`
8960
- `) + `
8961
-
8962
- Move them to .oneagent/skills/ to manage them with oneagent.`, ".claude/commands/ is deprecated — use .oneagent/skills/ instead");
8963
- }
8964
- function timeAgo(date) {
8965
- const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
8966
- if (seconds < 60)
8967
- return `${seconds}s ago`;
8968
- const minutes = Math.floor(seconds / 60);
8969
- if (minutes < 60)
8970
- return `${minutes}m ago`;
8971
- const hours = Math.floor(minutes / 60);
8972
- if (hours < 24)
8973
- return `${hours}h ago`;
8974
- const days = Math.floor(hours / 24);
8975
- if (days < 30)
8976
- return `${days}d ago`;
8977
- const months = Math.floor(days / 30);
8978
- if (months < 12)
8979
- return `${months}mo ago`;
8980
- return `${Math.floor(months / 12)}y ago`;
8981
- }
8982
- var init_utils = __esm(() => {
8983
- init_dist3();
8984
- init_src();
8985
- });
8986
-
8987
9068
  // ../templates/src/index.ts
8988
9069
  import path10 from "path";
8989
9070
  import fs11 from "fs/promises";
@@ -8994,20 +9075,7 @@ async function loadTemplate(name) {
8994
9075
  fs11.readFile(path10.join(templateDir, "template.yml"), "utf-8"),
8995
9076
  fs11.readFile(path10.join(templateDir, "instructions.md"), "utf-8")
8996
9077
  ]);
8997
- const descMatch = yamlText.match(/^description:\s*(.+)$/m);
8998
- const description = descMatch?.[1]?.trim() ?? "";
8999
- const skills2 = [];
9000
- const skillsBlockMatch = yamlText.match(/^skills:\s*\n((?: - .+\n?)*)/m);
9001
- if (skillsBlockMatch) {
9002
- const lines = skillsBlockMatch[1].split(`
9003
- `).filter(Boolean);
9004
- for (const line of lines) {
9005
- const skill = line.replace(/^\s*-\s*/, "").trim();
9006
- if (skill)
9007
- skills2.push(skill);
9008
- }
9009
- }
9010
- const plugins = parsePluginsFromYaml(yamlText);
9078
+ const { description, skills: skills2, plugins } = parseTemplateYaml(yamlText, name);
9011
9079
  const rulesDir = path10.join(templateDir, "rules");
9012
9080
  let rules2 = [];
9013
9081
  try {
@@ -9024,12 +9092,17 @@ async function resolveBuiltinTemplate(name) {
9024
9092
  return null;
9025
9093
  return loadTemplate(name);
9026
9094
  }
9027
- var __dirname2, TEMPLATE_NAMES, BUILTIN_TEMPLATE_NAMES;
9095
+ var __dirname2, TEMPLATE_NAMES, BUILTIN_TEMPLATE_NAMES, BUILTIN_TEMPLATE_META;
9028
9096
  var init_src2 = __esm(() => {
9029
9097
  init_src();
9030
9098
  __dirname2 = path10.dirname(fileURLToPath(import.meta.url));
9031
9099
  TEMPLATE_NAMES = ["default", "react", "react-native"];
9032
9100
  BUILTIN_TEMPLATE_NAMES = TEMPLATE_NAMES;
9101
+ BUILTIN_TEMPLATE_META = [
9102
+ { name: "default", description: "General programming starter" },
9103
+ { name: "react", description: "React / Next.js project starter" },
9104
+ { name: "react-native", description: "React Native / Expo project starter" }
9105
+ ];
9033
9106
  });
9034
9107
 
9035
9108
  // src/commands/init.ts
@@ -9124,6 +9197,32 @@ async function backupFiles(root, files) {
9124
9197
  await fs12.writeFile(path11.join(backupDir, safeName), file.content);
9125
9198
  }
9126
9199
  }
9200
+ async function pickTemplateInteractively() {
9201
+ const result = await Je({
9202
+ message: "Which template would you like to use?",
9203
+ options: [
9204
+ ...BUILTIN_TEMPLATE_META.map((t) => ({
9205
+ value: t.name,
9206
+ label: t.name,
9207
+ hint: t.description
9208
+ })),
9209
+ { value: "__github__", label: "Custom GitHub template", hint: "Enter a GitHub URL" }
9210
+ ]
9211
+ });
9212
+ if (Ct(result))
9213
+ cancelAndExit();
9214
+ if (result === "__github__") {
9215
+ const url = await Ze({
9216
+ message: "GitHub URL:",
9217
+ placeholder: "https://github.com/owner/repo",
9218
+ validate: (v) => !v || !v.startsWith("https://github.com/") ? "Must be a GitHub URL" : undefined
9219
+ });
9220
+ if (Ct(url))
9221
+ cancelAndExit();
9222
+ return url;
9223
+ }
9224
+ return result;
9225
+ }
9127
9226
  async function resolveTemplate(templateArg) {
9128
9227
  if (templateArg.startsWith("https://github.com/")) {
9129
9228
  return fetchTemplateFromGitHub(templateArg);
@@ -9133,34 +9232,13 @@ async function resolveTemplate(templateArg) {
9133
9232
  return builtin;
9134
9233
  throw new Error(`Unknown template "${templateArg}". Use one of: ${BUILTIN_TEMPLATE_NAMES.join(", ")} — or a GitHub URL.`);
9135
9234
  }
9136
- var DOTAI_META_RULE = `---
9137
- applyTo: "**"
9138
- ---
9139
- # oneagent
9140
-
9141
- This project uses [oneagent](https://github.com/moskalakamil/oneagent) to manage AI agent configuration.
9142
-
9143
- Rules are stored in \`.oneagent/rules/\` and distributed to agents automatically via symlinks or generated files.
9144
-
9145
- To add a new rule, create a \`.md\` file in \`.oneagent/rules/\` with optional frontmatter:
9146
-
9147
- \`\`\`md
9148
- ---
9149
- applyTo: "**/*.ts"
9150
- ---
9151
- # Rule name
9152
-
9153
- Rule content here.
9154
- \`\`\`
9155
-
9156
- Then run \`dotai generate\` to distribute the rule to all configured agents.
9157
- `, init_default;
9235
+ var ABOUT_ONEAGENT_RULE_PATH, init_default;
9158
9236
  var init_init = __esm(() => {
9159
9237
  init_dist();
9160
9238
  init_dist3();
9161
- init_utils();
9162
9239
  init_src();
9163
9240
  init_src2();
9241
+ ABOUT_ONEAGENT_RULE_PATH = new URL("../assets/about-oneagent.md", import.meta.url);
9164
9242
  init_default = defineCommand2({
9165
9243
  meta: {
9166
9244
  name: "init",
@@ -9183,11 +9261,14 @@ var init_init = __esm(() => {
9183
9261
  const detected = await detectExistingFiles(root);
9184
9262
  let template = null;
9185
9263
  let importedContent = "";
9186
- if (args.template) {
9264
+ const templateFlagPresent = process.argv.includes("--template");
9265
+ const templateValue = typeof args.template === "string" && args.template.length > 0 ? args.template : null;
9266
+ const templateArg = templateValue || (templateFlagPresent ? await pickTemplateInteractively() : null);
9267
+ if (templateArg) {
9187
9268
  const s3 = bt2();
9188
- s3.start(`Resolving template "${args.template}"...`);
9269
+ s3.start(`Resolving template "${templateArg}"...`);
9189
9270
  try {
9190
- template = await resolveTemplate(args.template);
9271
+ template = await resolveTemplate(templateArg);
9191
9272
  s3.stop(`Template "${template.name}" ready.`);
9192
9273
  } catch (err) {
9193
9274
  s3.stop("Failed.");
@@ -9204,6 +9285,7 @@ var init_init = __esm(() => {
9204
9285
  s.start("Setting up .oneagent/ directory...");
9205
9286
  await fs12.mkdir(path11.join(root, ".oneagent/rules"), { recursive: true });
9206
9287
  await fs12.mkdir(path11.join(root, ".oneagent/skills"), { recursive: true });
9288
+ await fs12.mkdir(path11.join(root, ".oneagent/commands"), { recursive: true });
9207
9289
  await backupFiles(root, detected);
9208
9290
  await removeDeprecatedFiles(root);
9209
9291
  await migrateRuleAndSkillFiles(root);
@@ -9218,41 +9300,64 @@ Add your AI instructions here.
9218
9300
  `;
9219
9301
  await fs12.writeFile(path11.join(root, ".oneagent/instructions.md"), instructionsContent);
9220
9302
  }
9221
- await fs12.writeFile(path11.join(root, ".oneagent/rules/oneagent.md"), DOTAI_META_RULE);
9303
+ await fs12.copyFile(ABOUT_ONEAGENT_RULE_PATH, path11.join(root, ".oneagent/rules/about-oneagent.md"));
9222
9304
  s.stop("Directory structure created.");
9223
- await warnDeprecatedCommandFiles(root);
9305
+ const commandFiles = await fs12.readdir(path11.join(root, ".oneagent/commands")).catch(() => []);
9306
+ if (commandFiles.some((f) => f.endsWith(".md"))) {
9307
+ R2.warn("Commands detected in .oneagent/commands/. Consider migrating to .oneagent/skills/ — skills are distributed to more agents and support richer features.");
9308
+ }
9309
+ if (commandFiles.some((f) => f.endsWith(".md"))) {
9310
+ const commandsSupported = new Set(AGENT_DEFINITIONS.filter((d2) => d2.commandsDir).map((d2) => d2.target));
9311
+ const unsupported = selectedTargets.filter((t) => !commandsSupported.has(t));
9312
+ if (unsupported.length > 0) {
9313
+ const names = unsupported.map((t) => AGENT_DEFINITIONS.find((d2) => d2.target === t).displayName).join(", ");
9314
+ R2.warn(`Commands in .oneagent/commands/ will not be available in: ${names} — these agents do not support custom slash commands.`);
9315
+ }
9316
+ }
9224
9317
  const s2 = bt2();
9225
9318
  s2.start("Generating symlinks and agent files...");
9226
9319
  await generate(root, config2);
9227
9320
  s2.stop("Done.");
9228
- const fetchedSkills = [];
9321
+ let skillResult = { installed: [], failed: [] };
9229
9322
  if (template && template.skills.length > 0) {
9230
9323
  const s3 = bt2();
9231
9324
  s3.start("Installing skills...");
9232
- await installTemplateSkills(root, template, (id) => fetchedSkills.push(id));
9233
- s3.stop(`Installed ${fetchedSkills.length} skill(s).`);
9325
+ skillResult = await installTemplateSkills(root, template);
9326
+ s3.stop(`Installed ${skillResult.installed.length} skill(s).`);
9327
+ for (const f of skillResult.failed) {
9328
+ R2.warn(`Skill "${f.entry.skill}" (${f.entry.repo}) could not be installed and was skipped.`);
9329
+ }
9234
9330
  }
9235
- let pluginResult = { installed: [], manual: [] };
9331
+ let pluginResult = { installed: [], manual: [], failed: [] };
9236
9332
  if (template && template.plugins.length > 0) {
9237
9333
  const s4 = bt2();
9238
9334
  s4.start("Installing plugins...");
9239
9335
  pluginResult = await installTemplatePlugins(root, template, selectedTargets);
9240
9336
  s4.stop(`Installed ${pluginResult.installed.length} plugin(s).`);
9337
+ for (const f of pluginResult.failed) {
9338
+ R2.warn(`Plugin "${f.plugin.id}" (${f.plugin.target}) could not be installed and was skipped.`);
9339
+ }
9241
9340
  }
9242
9341
  const lines = [
9243
9342
  ...template ? [
9244
9343
  `Template: ${template.name} — ${template.description}`,
9245
- ...fetchedSkills.length > 0 ? [`Fetched ${fetchedSkills.length} skill(s): ${fetchedSkills.join(", ")}`] : [],
9344
+ ...skillResult.installed.length > 0 ? [`Installed ${skillResult.installed.length} skill(s): ${skillResult.installed.map((s3) => s3.skill).join(", ")}`] : [],
9246
9345
  ...template.rules.length > 0 ? [`Added ${template.rules.length} rule(s) from template`] : [],
9247
- ...pluginResult.installed.length > 0 ? [`Installed ${pluginResult.installed.length} plugin(s): ${pluginResult.installed.map((p) => p.id).join(", ")}`] : [],
9248
- ...pluginResult.manual.map((p) => `Run in Cursor chat: /add-plugin ${p.id}`)
9346
+ ...pluginResult.installed.length > 0 ? [`Installed ${pluginResult.installed.length} plugin(s): ${pluginResult.installed.map((p) => p.id).join(", ")}`] : []
9249
9347
  ] : ["Created .oneagent/instructions.md"],
9250
- "Created .oneagent/rules/oneagent.md",
9348
+ "Created .oneagent/rules/about-oneagent.md",
9251
9349
  ...selectedTargets.map((t) => `Configured: ${t}`),
9252
9350
  ...detected.length > 0 ? [`Backed up ${detected.length} file(s) to .oneagent/backup/`] : []
9253
9351
  ];
9254
9352
  Ve(lines.map((l) => ` • ${l}`).join(`
9255
9353
  `), "Setup complete");
9354
+ if (pluginResult.manual.length > 0) {
9355
+ Ve(`This template includes ${pluginResult.manual.length} Cursor plugin(s).
9356
+ To install them, run each command in the Cursor chat:
9357
+
9358
+ ${pluginResult.manual.map((p) => ` /add-plugin ${p.id}`).join(`
9359
+ `)}`, "Action required: Cursor plugins");
9360
+ }
9256
9361
  Le("Run `oneagent status` to verify your setup.");
9257
9362
  }
9258
9363
  });
@@ -9269,7 +9374,6 @@ var generate_default;
9269
9374
  var init_generate2 = __esm(() => {
9270
9375
  init_dist();
9271
9376
  init_dist3();
9272
- init_utils();
9273
9377
  init_src();
9274
9378
  generate_default = defineCommand2({
9275
9379
  meta: {
@@ -9294,7 +9398,6 @@ var init_generate2 = __esm(() => {
9294
9398
  await fs13.writeFile(path12.join(backupDir, safeName), file.content);
9295
9399
  }
9296
9400
  }
9297
- await warnDeprecatedCommandFiles(root);
9298
9401
  if (ruleSkillFiles.length > 0) {
9299
9402
  Ve(ruleSkillFiles.map((f) => ` • ${f.relativePath}`).join(`
9300
9403
  `), "These rule/skill files are not dotai symlinks");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oneagent",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "One source of truth for AI agent rules — distributed via symlinks to Claude, Cursor, Windsurf, Copilot, OpenCode",
6
6
  "license": "MIT",