@danieldeusing/seedr 0.1.97 → 0.1.99

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.
@@ -18,7 +18,7 @@ function registryDirName(config) {
18
18
  }
19
19
  return value;
20
20
  }
21
- var ALL_TYPES = ["skill", "plugin", "hook", "agent", "mcp", "settings", "command"];
21
+ var ALL_TYPES = ["skill", "plugin", "hook", "agent", "mcp", "settings", "command", "rule"];
22
22
  var KNOWN_TYPES = new Set(ALL_TYPES);
23
23
  function typeDirName(type) {
24
24
  return type === "settings" || type === "mcp" ? type : `${type}s`;
@@ -46,6 +46,23 @@ function canonicalAgents(values) {
46
46
  const present = new Set(values.map(canonicalAgent).filter((a) => a !== null));
47
47
  return CANONICAL_AGENTS.filter((a) => present.has(a));
48
48
  }
49
+ var AGENT_COMPATIBILITY = {
50
+ skill: ["claude", "copilot", "antigravity", "codex", "opencode"],
51
+ command: ["claude"],
52
+ // Copilot's subagent files carry the same name/description frontmatter.
53
+ // OpenCode's do not — theirs use `mode` and a `tools` map — so porting there
54
+ // needs a frontmatter translation, not a path.
55
+ agent: ["claude", "copilot"],
56
+ hook: ["claude"],
57
+ plugin: ["claude", "copilot", "antigravity", "codex", "opencode"],
58
+ settings: ["claude"],
59
+ mcp: ["claude", "codex", "opencode", "copilot"],
60
+ rule: ["claude", "copilot", "antigravity", "codex", "opencode"]
61
+ };
62
+ function isTypeSupported(type, agent) {
63
+ const canonical = canonicalAgent(agent);
64
+ return canonical !== null && AGENT_COMPATIBILITY[type].includes(canonical);
65
+ }
49
66
 
50
67
  // ../registry-ops/src/sourceTypes.ts
51
68
  var CANONICAL_SOURCE_TYPES = ["official", "seedr", "community"];
@@ -756,17 +773,35 @@ async function fetchItemToDestination(item, destPath) {
756
773
  }
757
774
 
758
775
  // src/config/agents.ts
776
+ import { existsSync as existsSync12 } from "fs";
759
777
  import { homedir as homedir2 } from "os";
760
778
  import { join as join14 } from "path";
761
779
  var home = homedir2();
780
+ function claudeUserJsonPath() {
781
+ const configDir = process.env.CLAUDE_CONFIG_DIR;
782
+ if (configDir) {
783
+ const scoped = join14(configDir, ".config.json");
784
+ if (existsSync12(scoped)) return scoped;
785
+ }
786
+ return join14(configDir || home, ".claude.json");
787
+ }
788
+ var copilotUserRoot = () => process.env.COPILOT_HOME || join14(home, ".copilot");
789
+ var codexUserRoot = () => process.env.CODEX_HOME || join14(home, ".codex");
790
+ var openCodeUserConfigDir = () => process.env.OPENCODE_CONFIG_DIR || (process.env.XDG_CONFIG_HOME ? join14(process.env.XDG_CONFIG_HOME, "opencode") : join14(home, ".config", "opencode"));
791
+ var claudeUserRoot = () => process.env.CLAUDE_CONFIG_DIR || join14(home, ".claude");
762
792
  var SKILL_DIRECTORY = {
763
793
  path: "skills"
764
794
  };
765
795
  var ANTIGRAVITY = {
766
796
  name: "Google Antigravity",
767
797
  shortName: "antigravity",
798
+ // Project content is the agent-neutral `.agents/` tree, which IS Antigravity's
799
+ // own project root. The personal tier is not the same spelling: the shipped
800
+ // CLI reads `~/.gemini/config/skills`, and the vendor documentation states
801
+ // outright that `~/.agents` does not exist for it. `~/.gemini/skills` is the
802
+ // predecessor Gemini CLI location and is not written.
768
803
  projectRoot: ".agents",
769
- userRoot: join14(home, ".agents"),
804
+ userRoot: join14(home, ".gemini", "config"),
770
805
  contentTypes: { skill: SKILL_DIRECTORY }
771
806
  };
772
807
  var CODING_AGENTS = {
@@ -774,7 +809,7 @@ var CODING_AGENTS = {
774
809
  name: "Claude Code",
775
810
  shortName: "claude",
776
811
  projectRoot: ".claude",
777
- userRoot: join14(home, ".claude"),
812
+ userRoot: claudeUserRoot(),
778
813
  contentTypes: {
779
814
  skill: SKILL_DIRECTORY,
780
815
  command: {
@@ -800,9 +835,15 @@ var CODING_AGENTS = {
800
835
  copilot: {
801
836
  name: "GitHub Copilot",
802
837
  shortName: "copilot",
838
+ // Project content is `.github/`, but the personal tier is `~/.copilot/` —
839
+ // the two are not the same spelling. `~/.github/skills` is read by nothing;
840
+ // `~/.copilot/skills` is what the CLI itself populates.
803
841
  projectRoot: ".github",
804
- userRoot: join14(home, ".github"),
805
- contentTypes: { skill: SKILL_DIRECTORY }
842
+ userRoot: copilotUserRoot(),
843
+ // Subagents are `<name>.agent.md` under `agents/`, with the same
844
+ // `name` + `description` frontmatter a Claude subagent carries — confirmed
845
+ // from files on a real machine.
846
+ contentTypes: { skill: SKILL_DIRECTORY, agent: { path: "agents" } }
806
847
  },
807
848
  antigravity: ANTIGRAVITY,
808
849
  gemini: ANTIGRAVITY,
@@ -810,14 +851,19 @@ var CODING_AGENTS = {
810
851
  name: "OpenAI Codex CLI",
811
852
  shortName: "codex",
812
853
  projectRoot: ".codex",
813
- userRoot: join14(home, ".codex"),
854
+ userRoot: codexUserRoot(),
814
855
  contentTypes: { skill: SKILL_DIRECTORY }
815
856
  },
816
857
  opencode: {
817
858
  name: "OpenCode",
818
859
  shortName: "opencode",
860
+ // `~/.opencode` is the LEGACY user directory. The global config directory
861
+ // is `~/.config/opencode`, which is where this package already writes
862
+ // `opencode.json` and `AGENTS.md` — splitting one agent across two roots
863
+ // made `list` and `remove` diverge, and creating `~/.opencode` on a machine
864
+ // that had none adds a capability directory ranked above every project one.
819
865
  projectRoot: ".opencode",
820
- userRoot: join14(home, ".opencode"),
866
+ userRoot: openCodeUserConfigDir(),
821
867
  contentTypes: { skill: SKILL_DIRECTORY }
822
868
  }
823
869
  };
@@ -849,7 +895,7 @@ function getSettingsPath(scope, cwd = process.cwd()) {
849
895
  case "project":
850
896
  return join14(cwd, ".claude/settings.json");
851
897
  case "user":
852
- return join14(home, ".claude/settings.json");
898
+ return join14(claudeUserRoot(), "settings.json");
853
899
  case "local":
854
900
  return join14(cwd, ".claude/settings.local.json");
855
901
  }
@@ -858,11 +904,13 @@ function getMcpConfigPath(agent, scope, cwd = process.cwd()) {
858
904
  const isUser = scope === "user";
859
905
  switch (canonicalAgent(agent) ?? agent) {
860
906
  case "claude":
861
- return isUser ? join14(home, ".claude.json") : join14(cwd, ".mcp.json");
907
+ return isUser ? claudeUserJsonPath() : join14(cwd, ".mcp.json");
862
908
  case "codex":
863
- return isUser ? join14(home, ".codex", "config.toml") : join14(cwd, ".codex", "config.toml");
909
+ return isUser ? join14(codexUserRoot(), "config.toml") : join14(cwd, ".codex", "config.toml");
864
910
  case "opencode":
865
- return isUser ? join14(home, ".config", "opencode", "opencode.json") : join14(cwd, "opencode.json");
911
+ return isUser ? join14(openCodeUserConfigDir(), "opencode.json") : join14(cwd, "opencode.json");
912
+ case "copilot":
913
+ return isUser ? join14(copilotUserRoot(), "mcp-config.json") : join14(cwd, ".mcp.json");
866
914
  default:
867
915
  throw new Error(`${agent} has no verified MCP configuration format`);
868
916
  }
@@ -1041,18 +1089,94 @@ function error(message) {
1041
1089
  }
1042
1090
 
1043
1091
  // src/handlers/skill.ts
1044
- import { relative as relative5, dirname as dirname7 } from "path";
1092
+ import { relative as relative5, dirname as dirname8, join as join15 } from "path";
1045
1093
  import { homedir as homedir3 } from "os";
1046
1094
  import { readdir as readdir2, symlink as symlink2 } from "fs/promises";
1047
1095
  import chalk2 from "chalk";
1048
1096
  import ora from "ora";
1097
+
1098
+ // src/utils/json.ts
1099
+ import { readFile as readFile4, stat as stat2 } from "fs/promises";
1100
+ import { dirname as dirname7 } from "path";
1101
+ async function readJson(path) {
1102
+ try {
1103
+ const content = await readFile4(path, "utf-8");
1104
+ return JSON.parse(content);
1105
+ } catch (error2) {
1106
+ if (error2.code === "ENOENT") {
1107
+ return {};
1108
+ }
1109
+ if (error2 instanceof SyntaxError) {
1110
+ throw new Error(`${path} is not valid JSON: ${error2.message}`, { cause: error2 });
1111
+ }
1112
+ throw error2;
1113
+ }
1114
+ }
1115
+ async function existingFileMode(path) {
1116
+ try {
1117
+ return (await stat2(path)).mode & 511;
1118
+ } catch (error2) {
1119
+ if (error2.code === "ENOENT") return void 0;
1120
+ throw error2;
1121
+ }
1122
+ }
1123
+ async function writeJson(path, data) {
1124
+ await ensureDir(dirname7(path));
1125
+ const mode = await existingFileMode(path);
1126
+ await writeFileAtomic(path, JSON.stringify(data, null, 2) + "\n", { mode });
1127
+ }
1128
+ function deepMerge(target, source) {
1129
+ const result = { ...target };
1130
+ for (const key of Object.keys(source)) {
1131
+ const sourceValue = source[key];
1132
+ const targetValue = result[key];
1133
+ if (sourceValue !== null && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue !== null && typeof targetValue === "object" && !Array.isArray(targetValue)) {
1134
+ result[key] = deepMerge(
1135
+ targetValue,
1136
+ sourceValue
1137
+ );
1138
+ } else if (sourceValue !== void 0) {
1139
+ result[key] = sourceValue;
1140
+ }
1141
+ }
1142
+ return result;
1143
+ }
1144
+
1145
+ // src/handlers/skill.ts
1049
1146
  var SLUG_LABEL = "skill slug";
1050
- var READS_CENTRAL_DIR = /* @__PURE__ */ new Set(["antigravity", "codex", "opencode"]);
1147
+ function readsCentralDir(agent, scope) {
1148
+ const canonical = canonicalAgent(agent) ?? agent;
1149
+ if (canonical === "antigravity") return scope !== "user";
1150
+ return canonical === "codex" || canonical === "opencode";
1151
+ }
1152
+ var OWNERS_FILE = ".seedr-central-owners.json";
1153
+ async function readOwners(scope, cwd) {
1154
+ return readJson(join15(centralSkillsDir(scope, cwd), OWNERS_FILE));
1155
+ }
1156
+ async function writeOwners(owners, scope, cwd) {
1157
+ await writeJson(join15(centralSkillsDir(scope, cwd), OWNERS_FILE), owners);
1158
+ }
1159
+ async function addOwners(slug, agents, scope, cwd) {
1160
+ if (agents.length === 0) return;
1161
+ const owners = await readOwners(scope, cwd);
1162
+ owners[slug] = [.../* @__PURE__ */ new Set([...owners[slug] ?? [], ...agents])];
1163
+ await writeOwners(owners, scope, cwd);
1164
+ }
1165
+ async function dropOwner(slug, agent, scope, cwd) {
1166
+ const owners = await readOwners(scope, cwd);
1167
+ const recorded = owners[slug];
1168
+ if (!recorded) return [];
1169
+ const remaining = recorded.filter((owner) => owner !== (canonicalAgent(agent) ?? agent));
1170
+ if (remaining.length > 0) owners[slug] = remaining;
1171
+ else delete owners[slug];
1172
+ await writeOwners(owners, scope, cwd);
1173
+ return remaining;
1174
+ }
1051
1175
  function getScopeRoot(scope, cwd) {
1052
1176
  return scope === "user" ? homedir3() : cwd;
1053
1177
  }
1054
1178
  function centralSkillsDir(scope, cwd) {
1055
- return dirname7(getAgentsPath("skill", "any-slug", scope, cwd));
1179
+ return dirname8(getAgentsPath("skill", "any-slug", scope, cwd));
1056
1180
  }
1057
1181
  async function resolveCentralPath(slug, scope, cwd) {
1058
1182
  const scopeRoot = getScopeRoot(scope, cwd);
@@ -1077,9 +1201,9 @@ async function installToCentralLocation(item, sourcePath, scope, force, cwd) {
1077
1201
  return { centralPath, created: !existed };
1078
1202
  }
1079
1203
  async function createAgentSymlink(centralPath, destPath) {
1080
- await ensureDir(dirname7(destPath));
1204
+ await ensureDir(dirname8(destPath));
1081
1205
  await removePathEntry(destPath);
1082
- const relPath = relative5(dirname7(destPath), centralPath);
1206
+ const relPath = relative5(dirname8(destPath), centralPath);
1083
1207
  await symlink2(relPath, destPath);
1084
1208
  }
1085
1209
  async function installSkillForAgent(item, agent, scope, method, force, cwd, centralPath) {
@@ -1121,7 +1245,7 @@ async function installSkill(item, agents, scope, method, force, cwd = process.cw
1121
1245
  central = await installToCentralLocation(item, sourcePath, scope, force, cwd);
1122
1246
  }
1123
1247
  for (const agent of agents) {
1124
- if (READS_CENTRAL_DIR.has(canonicalAgent(agent) ?? agent) && central) {
1248
+ if (readsCentralDir(agent, scope) && central) {
1125
1249
  results.push({ agent, success: true, path: central.centralPath });
1126
1250
  continue;
1127
1251
  }
@@ -1138,15 +1262,30 @@ async function installSkill(item, agents, scope, method, force, cwd = process.cw
1138
1262
  }
1139
1263
  if (central?.created && !results.some((result) => result.success)) {
1140
1264
  await removePathEntry(central.centralPath);
1265
+ return results;
1266
+ }
1267
+ if (central) {
1268
+ const centralReaders = agents.filter(
1269
+ (agent) => readsCentralDir(agent, scope) && results.some((result) => result.agent === agent && result.success)
1270
+ );
1271
+ await addOwners(item.slug, centralReaders, scope, cwd);
1141
1272
  }
1142
1273
  return results;
1143
1274
  }
1144
1275
  async function uninstallSkill(slug, agent, scope, cwd = process.cwd()) {
1145
1276
  assertValidSlug(slug, SLUG_LABEL);
1277
+ const central = await resolveCentralPath(slug, scope, cwd);
1146
1278
  const destPath = await resolveAgentSkillPath(agent, slug, scope, cwd);
1147
- if (destPath && await removePathEntry(destPath)) return true;
1148
- if (READS_CENTRAL_DIR.has(canonicalAgent(agent) ?? agent)) {
1149
- return removePathEntry(await resolveCentralPath(slug, scope, cwd));
1279
+ if (readsCentralDir(agent, scope) || destPath === central) {
1280
+ const remaining = await dropOwner(slug, agent, scope, cwd);
1281
+ if (remaining.length > 0) {
1282
+ return true;
1283
+ }
1284
+ return removePathEntry(central);
1285
+ }
1286
+ if (destPath && await removePathEntry(destPath)) {
1287
+ await dropOwner(slug, agent, scope, cwd);
1288
+ return true;
1150
1289
  }
1151
1290
  return false;
1152
1291
  }
@@ -1154,13 +1293,21 @@ async function getInstalledSkills(agent, scope, cwd = process.cwd()) {
1154
1293
  const dirs = /* @__PURE__ */ new Set();
1155
1294
  const own = getContentPath(agent, "skill", scope, cwd);
1156
1295
  if (own) dirs.add(own);
1157
- if (READS_CENTRAL_DIR.has(canonicalAgent(agent) ?? agent)) dirs.add(centralSkillsDir(scope, cwd));
1296
+ if (readsCentralDir(agent, scope)) dirs.add(centralSkillsDir(scope, cwd));
1297
+ const owners = await readOwners(scope, cwd);
1298
+ const centralDir = centralSkillsDir(scope, cwd);
1299
+ const canonical = canonicalAgent(agent) ?? agent;
1300
+ const isInstalledHere = (entry, dir) => {
1301
+ if (entry.name.startsWith(".")) return false;
1302
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) return false;
1303
+ const recorded = dir === centralDir ? owners[entry.name] : void 0;
1304
+ return !recorded || recorded.includes(canonical);
1305
+ };
1158
1306
  const slugs = /* @__PURE__ */ new Set();
1159
1307
  for (const dir of dirs) {
1160
1308
  if (!await exists(dir)) continue;
1161
- const entries = await readdir2(dir, { withFileTypes: true });
1162
- for (const entry of entries) {
1163
- if ((entry.isDirectory() || entry.isSymbolicLink()) && !entry.name.startsWith(".")) slugs.add(entry.name);
1309
+ for (const entry of await readdir2(dir, { withFileTypes: true })) {
1310
+ if (isInstalledHere(entry, dir)) slugs.add(entry.name);
1164
1311
  }
1165
1312
  }
1166
1313
  return [...slugs];
@@ -1172,7 +1319,7 @@ async function planSkill(item, agents, scope, method, cwd) {
1172
1319
  let centralPath;
1173
1320
  if (method === "symlink") {
1174
1321
  centralPath = await resolveCentralPath(item.slug, scope, cwd);
1175
- const sharedBy = agents.filter((agent) => READS_CENTRAL_DIR.has(canonicalAgent(agent) ?? agent));
1322
+ const sharedBy = agents.filter((agent) => readsCentralDir(agent, scope));
1176
1323
  changes.push({
1177
1324
  agent: "shared",
1178
1325
  kind: await kindFor(centralPath),
@@ -1181,7 +1328,7 @@ async function planSkill(item, agents, scope, method, cwd) {
1181
1328
  });
1182
1329
  }
1183
1330
  for (const agent of agents) {
1184
- if (centralPath && READS_CENTRAL_DIR.has(canonicalAgent(agent) ?? agent)) continue;
1331
+ if (centralPath && readsCentralDir(agent, scope)) continue;
1185
1332
  const destPath = await resolveAgentSkillPath(agent, item.slug, scope, cwd);
1186
1333
  if (!destPath) throw new Error(`${CODING_AGENTS[agent].name} does not support skills`);
1187
1334
  changes.push({
@@ -1208,10 +1355,13 @@ var skillHandler = {
1208
1355
  };
1209
1356
 
1210
1357
  export {
1358
+ ALL_TYPES,
1211
1359
  mainFileName,
1212
1360
  isLegacyAgent,
1213
1361
  canonicalAgent,
1214
1362
  canonicalAgents,
1363
+ AGENT_COMPATIBILITY,
1364
+ isTypeSupported,
1215
1365
  SLUG_PATTERN2 as SLUG_PATTERN,
1216
1366
  MAX_SLUG_LENGTH2 as MAX_SLUG_LENGTH,
1217
1367
  isValidSlug2 as isValidSlug,
@@ -1242,6 +1392,10 @@ export {
1242
1392
  fetchItemFile,
1243
1393
  getItemSourcePath,
1244
1394
  fetchItemToDestination,
1395
+ copilotUserRoot,
1396
+ codexUserRoot,
1397
+ openCodeUserConfigDir,
1398
+ claudeUserRoot,
1245
1399
  CODING_AGENTS,
1246
1400
  ALL_AGENTS,
1247
1401
  getAgentConfig,
@@ -1269,6 +1423,9 @@ export {
1269
1423
  success,
1270
1424
  warn,
1271
1425
  error,
1426
+ readJson,
1427
+ writeJson,
1428
+ deepMerge,
1272
1429
  installSkill,
1273
1430
  uninstallSkill,
1274
1431
  getInstalledSkills,