@danieldeusing/seedr 0.1.80 → 0.1.82

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.
@@ -2,33 +2,96 @@
2
2
 
3
3
  // src/config/registry.ts
4
4
  import { readFile, readdir, writeFile, mkdir } from "fs/promises";
5
+ import { existsSync } from "fs";
5
6
  import { join, dirname } from "path";
6
7
  import { fileURLToPath } from "url";
7
8
  var __dirname = dirname(fileURLToPath(import.meta.url));
8
- var REGISTRY_PATH = join(__dirname, "../../../../registry");
9
+ var FETCH_TIMEOUT_MS = 15e3;
10
+ function findPackageRoot(startDir) {
11
+ let dir = startDir;
12
+ for (; ; ) {
13
+ if (existsSync(join(dir, "package.json"))) return dir;
14
+ const parent = dirname(dir);
15
+ if (parent === dir) return null;
16
+ dir = parent;
17
+ }
18
+ }
19
+ function resolveLocalRegistryPath() {
20
+ const packageRoot = findPackageRoot(__dirname);
21
+ if (!packageRoot) return null;
22
+ return join(packageRoot, "..", "..", "registry");
23
+ }
24
+ var REGISTRY_PATH = resolveLocalRegistryPath();
9
25
  var GITHUB_RAW_URL = "https://raw.githubusercontent.com/danieldeusing/seedr/main/registry";
26
+ var SLUG_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
27
+ function assertValidSlug(slug) {
28
+ if (!SLUG_PATTERN.test(slug)) {
29
+ throw new Error(`Invalid item slug: "${slug}"`);
30
+ }
31
+ }
32
+ function assertSafeNodeName(name) {
33
+ if (name.includes("/") || name.includes("\\") || name.includes("..")) {
34
+ throw new Error(`Unsafe file name in registry content: "${name}"`);
35
+ }
36
+ }
10
37
  var cache = {
11
38
  index: null,
12
39
  types: /* @__PURE__ */ new Map(),
13
40
  assembled: null
14
41
  };
42
+ function typeDirName(type) {
43
+ if (type === "mcp" || type === "settings") return type;
44
+ return `${type}s`;
45
+ }
46
+ function isValidManifestIndex(value) {
47
+ return typeof value === "object" && value !== null && typeof value.types === "object" && value.types !== null;
48
+ }
15
49
  async function loadFile(filename) {
16
- try {
17
- return await readFile(join(REGISTRY_PATH, filename), "utf-8");
18
- } catch {
19
- return fetchRemote(`${GITHUB_RAW_URL}/${filename}`);
50
+ if (REGISTRY_PATH) {
51
+ try {
52
+ return await readFile(join(REGISTRY_PATH, filename), "utf-8");
53
+ } catch {
54
+ }
20
55
  }
56
+ return fetchRemote(`${GITHUB_RAW_URL}/${filename}`);
21
57
  }
22
- async function fetchRemote(url) {
23
- const response = await fetch(url);
58
+ async function fetchResponse(url) {
59
+ let response;
60
+ try {
61
+ response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
62
+ } catch (error2) {
63
+ if (error2 instanceof Error && error2.name === "TimeoutError") {
64
+ throw new Error(`Registry unreachable: timed out fetching ${url}`, { cause: error2 });
65
+ }
66
+ throw new Error(`Registry unreachable: ${url}`, { cause: error2 });
67
+ }
24
68
  if (!response.ok) {
25
69
  throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
26
70
  }
71
+ return response;
72
+ }
73
+ async function fetchRemote(url) {
74
+ const response = await fetchResponse(url);
27
75
  return response.text();
28
76
  }
77
+ async function fetchRemoteBuffer(url) {
78
+ const response = await fetchResponse(url);
79
+ return Buffer.from(await response.arrayBuffer());
80
+ }
29
81
  async function loadIndex() {
30
82
  if (cache.index) return cache.index;
31
- const content = await loadFile("manifest.json");
83
+ if (REGISTRY_PATH) {
84
+ try {
85
+ const localContent = await readFile(join(REGISTRY_PATH, "manifest.json"), "utf-8");
86
+ const parsed = JSON.parse(localContent);
87
+ if (isValidManifestIndex(parsed)) {
88
+ cache.index = parsed;
89
+ return parsed;
90
+ }
91
+ } catch {
92
+ }
93
+ }
94
+ const content = await fetchRemote(`${GITHUB_RAW_URL}/manifest.json`);
32
95
  const data = JSON.parse(content);
33
96
  cache.index = data;
34
97
  return data;
@@ -75,7 +138,8 @@ async function searchItems(query) {
75
138
  );
76
139
  }
77
140
  async function getItemFull(item) {
78
- const typeDir = item.type + "s";
141
+ assertValidSlug(item.slug);
142
+ const typeDir = typeDirName(item.type);
79
143
  const itemJsonPath = `${typeDir}/${item.slug}/item.json`;
80
144
  const content = await loadFile(itemJsonPath);
81
145
  return JSON.parse(content);
@@ -85,9 +149,10 @@ function getItemBaseUrl(item) {
85
149
  const rawUrl = item.externalUrl.replace("github.com", "raw.githubusercontent.com").replace("/tree/", "/");
86
150
  return { local: null, remote: rawUrl };
87
151
  }
88
- const typeDir = item.type + "s";
152
+ assertValidSlug(item.slug);
153
+ const typeDir = typeDirName(item.type);
89
154
  return {
90
- local: join(REGISTRY_PATH, typeDir, item.slug),
155
+ local: REGISTRY_PATH ? join(REGISTRY_PATH, typeDir, item.slug) : null,
91
156
  remote: `${GITHUB_RAW_URL}/${typeDir}/${item.slug}`
92
157
  };
93
158
  }
@@ -106,7 +171,11 @@ function getItemSourcePath(item) {
106
171
  if (item.sourceType !== "toolr") {
107
172
  return null;
108
173
  }
109
- const typeDir = item.type + "s";
174
+ if (!REGISTRY_PATH) {
175
+ return null;
176
+ }
177
+ assertValidSlug(item.slug);
178
+ const typeDir = typeDirName(item.type);
110
179
  return join(REGISTRY_PATH, typeDir, item.slug);
111
180
  }
112
181
  async function fetchItemToDestination(item, destPath) {
@@ -124,14 +193,15 @@ async function fetchItemToDestination(item, destPath) {
124
193
  const filesToFetch = item.type === "skill" ? ["SKILL.md"] : item.type === "plugin" ? [".claude-plugin/plugin.json"] : [`${item.type}.md`];
125
194
  await mkdir(destPath, { recursive: true });
126
195
  await Promise.all(filesToFetch.map(async (file) => {
127
- const content = await fetchRemote(`${remote}/${file}`);
196
+ const content = await fetchRemoteBuffer(`${remote}/${file}`);
128
197
  const filePath = join(destPath, file);
129
198
  await mkdir(dirname(filePath), { recursive: true });
130
- await writeFile(filePath, content, "utf-8");
199
+ await writeFile(filePath, content);
131
200
  }));
132
201
  }
133
202
  async function fetchFileTree(nodes, baseUrl, destPath) {
134
203
  await Promise.all(nodes.map(async (node) => {
204
+ assertSafeNodeName(node.name);
135
205
  const nodePath = join(destPath, node.name);
136
206
  if (node.type === "directory") {
137
207
  await mkdir(nodePath, { recursive: true });
@@ -139,8 +209,8 @@ async function fetchFileTree(nodes, baseUrl, destPath) {
139
209
  await fetchFileTree(node.children, `${baseUrl}/${node.name}`, nodePath);
140
210
  }
141
211
  } else {
142
- const content = await fetchRemote(`${baseUrl}/${node.name}`);
143
- await writeFile(nodePath, content, "utf-8");
212
+ const content = await fetchRemoteBuffer(`${baseUrl}/${node.name}`);
213
+ await writeFile(nodePath, content);
144
214
  }
145
215
  }));
146
216
  }
@@ -317,6 +387,7 @@ import {
317
387
  cp,
318
388
  rm
319
389
  } from "fs/promises";
390
+ import { homedir as homedir2 } from "os";
320
391
  import { dirname as dirname2, join as join3, relative, isAbsolute } from "path";
321
392
  async function exists(path) {
322
393
  try {
@@ -329,6 +400,11 @@ async function exists(path) {
329
400
  async function ensureDir(path) {
330
401
  await mkdir2(path, { recursive: true });
331
402
  }
403
+ async function assertOverwritable(path, force) {
404
+ if (!force && await exists(path)) {
405
+ throw new Error(`${path} already exists; pass --force to overwrite`);
406
+ }
407
+ }
332
408
  async function installFile(source, destination, method) {
333
409
  await ensureDir(dirname2(destination));
334
410
  try {
@@ -355,8 +431,9 @@ async function writeTextFile(path, content) {
355
431
  await ensureDir(dirname2(path));
356
432
  await writeFile2(path, content, "utf-8");
357
433
  }
358
- function getAgentsPath(contentType, slug, cwd) {
359
- return join3(cwd, ".agents", contentType + "s", slug);
434
+ function getAgentsPath(contentType, slug, scope, cwd) {
435
+ const base = scope === "user" ? homedir2() : cwd;
436
+ return join3(base, ".agents", contentType + "s", slug);
360
437
  }
361
438
  async function copyDirectory(source, destination) {
362
439
  await cp(source, destination, { recursive: true });
@@ -530,8 +607,9 @@ import { join as join4, relative as relative2, dirname as dirname3 } from "path"
530
607
  import { readdir as readdir2, symlink as symlink2, rm as rm2 } from "fs/promises";
531
608
  import chalk2 from "chalk";
532
609
  import ora from "ora";
533
- async function installToCentralLocation(item, sourcePath, cwd) {
534
- const centralPath = getAgentsPath("skill", item.slug, cwd);
610
+ async function installToCentralLocation(item, sourcePath, scope, force, cwd) {
611
+ const centralPath = getAgentsPath("skill", item.slug, scope, cwd);
612
+ await assertOverwritable(centralPath, force);
535
613
  await rm2(centralPath, { recursive: true, force: true });
536
614
  if (sourcePath && await exists(sourcePath)) {
537
615
  await copyDirectory(sourcePath, centralPath);
@@ -546,7 +624,7 @@ async function createAgentSymlink(centralPath, destPath) {
546
624
  const relPath = relative2(dirname3(destPath), centralPath);
547
625
  await symlink2(relPath, destPath);
548
626
  }
549
- async function installSkillForAgent(item, agent, scope, method, cwd, centralPath) {
627
+ async function installSkillForAgent(item, agent, scope, method, force, cwd, centralPath) {
550
628
  const spinner = ora(
551
629
  `Installing ${item.name} for ${CODING_AGENTS[agent].name}...`
552
630
  ).start();
@@ -556,6 +634,7 @@ async function installSkillForAgent(item, agent, scope, method, cwd, centralPath
556
634
  throw new Error(`${CODING_AGENTS[agent].name} does not support skills`);
557
635
  }
558
636
  const destPath = join4(destDir, item.slug);
637
+ await assertOverwritable(destPath, force);
559
638
  const sourcePath = getItemSourcePath(item);
560
639
  if (method === "symlink" && centralPath) {
561
640
  await createAgentSymlink(centralPath, destPath);
@@ -576,12 +655,12 @@ async function installSkillForAgent(item, agent, scope, method, cwd, centralPath
576
655
  return { agent, success: false, path: "", error: errorMsg };
577
656
  }
578
657
  }
579
- async function installSkill(item, agents, scope, method, cwd = process.cwd()) {
658
+ async function installSkill(item, agents, scope, method, force, cwd = process.cwd()) {
580
659
  const results = [];
581
660
  const sourcePath = getItemSourcePath(item);
582
661
  let centralPath;
583
662
  if (method === "symlink") {
584
- centralPath = await installToCentralLocation(item, sourcePath, cwd);
663
+ centralPath = await installToCentralLocation(item, sourcePath, scope, force, cwd);
585
664
  }
586
665
  for (const agent of agents) {
587
666
  const readsAgentsDir = agent === "gemini" || agent === "codex" || agent === "opencode";
@@ -594,6 +673,7 @@ async function installSkill(item, agents, scope, method, cwd = process.cwd()) {
594
673
  agent,
595
674
  scope,
596
675
  method,
676
+ force,
597
677
  cwd,
598
678
  centralPath
599
679
  );
@@ -621,8 +701,8 @@ async function getInstalledSkills(agent, scope, cwd = process.cwd()) {
621
701
  }
622
702
  var skillHandler = {
623
703
  type: "skill",
624
- async install(item, agents, scope, method, cwd) {
625
- return installSkill(item, agents, scope, method, cwd);
704
+ async install(item, agents, scope, method, force, cwd) {
705
+ return installSkill(item, agents, scope, method, force, cwd);
626
706
  },
627
707
  async uninstall(slug, agent, scope, cwd) {
628
708
  return uninstallSkill(slug, agent, scope, cwd);
@@ -642,6 +722,7 @@ export {
642
722
  fetchItemToDestination,
643
723
  exists,
644
724
  ensureDir,
725
+ assertOverwritable,
645
726
  installFile,
646
727
  removeFile,
647
728
  writeTextFile,
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  ALL_AGENTS,
4
4
  CODING_AGENTS,
5
+ assertOverwritable,
5
6
  brand,
6
7
  cancelled,
7
8
  confirm,
@@ -38,7 +39,7 @@ import {
38
39
  success,
39
40
  warn,
40
41
  writeTextFile
41
- } from "./chunk-5XFKC3T7.js";
42
+ } from "./chunk-77M7PGDO.js";
42
43
 
43
44
  // src/cli.ts
44
45
  import { Command as Command5 } from "commander";
@@ -65,10 +66,45 @@ function handleCommandError(error2) {
65
66
  process.exit(1);
66
67
  }
67
68
 
69
+ // src/utils/validate-options.ts
70
+ var SCOPES = ["project", "user", "local"];
71
+ var METHODS = ["symlink", "copy"];
72
+ var TYPES = [
73
+ "skill",
74
+ "hook",
75
+ "agent",
76
+ "plugin",
77
+ "command",
78
+ "settings",
79
+ "mcp"
80
+ ];
81
+ function validateScope(value) {
82
+ if (value === void 0) return null;
83
+ if (!SCOPES.includes(value)) {
84
+ return `Invalid scope "${value}". Must be one of: ${SCOPES.join(", ")}`;
85
+ }
86
+ return null;
87
+ }
88
+ function validateMethod(value) {
89
+ if (value === void 0) return null;
90
+ if (!METHODS.includes(value)) {
91
+ return `Invalid method "${value}". Must be one of: ${METHODS.join(", ")}`;
92
+ }
93
+ return null;
94
+ }
95
+ function validateType(value) {
96
+ if (value === void 0) return null;
97
+ if (!TYPES.includes(value)) {
98
+ return `Invalid type "${value}". Must be one of: ${TYPES.join(", ")}`;
99
+ }
100
+ return null;
101
+ }
102
+
68
103
  // src/utils/analytics.ts
69
104
  var ANALYTICS_URL = "https://seedr.danieldeusing.de/api/installs";
70
105
  function trackInstalls(slug, type, results, scope) {
71
106
  if (process.env.SEEDR_NO_TELEMETRY) return;
107
+ const version = true ? "0.1.82" : "dev";
72
108
  for (const result of results) {
73
109
  if (!result.success) continue;
74
110
  fetch(ANALYTICS_URL, {
@@ -77,9 +113,9 @@ function trackInstalls(slug, type, results, scope) {
77
113
  body: JSON.stringify({
78
114
  slug,
79
115
  type,
80
- agent: result.agent,
116
+ tool: result.agent,
81
117
  scope,
82
- version: "0.1.80"
118
+ version
83
119
  }),
84
120
  signal: AbortSignal.timeout(4e3)
85
121
  }).catch(() => {
@@ -107,7 +143,7 @@ import { join } from "path";
107
143
  import { readdir } from "fs/promises";
108
144
  import chalk2 from "chalk";
109
145
  import ora from "ora";
110
- async function installAgentForCodingAgent(item, agent, scope, method, cwd) {
146
+ async function installAgentForCodingAgent(item, agent, scope, method, force, cwd) {
111
147
  const spinner = ora(
112
148
  `Installing ${item.name} for ${CODING_AGENTS[agent].name}...`
113
149
  ).start();
@@ -117,6 +153,7 @@ async function installAgentForCodingAgent(item, agent, scope, method, cwd) {
117
153
  throw new Error(`${CODING_AGENTS[agent].name} does not support agents`);
118
154
  }
119
155
  const destPath = join(destDir, `${item.slug}.md`);
156
+ await assertOverwritable(destPath, force);
120
157
  const sourcePath = getItemSourcePath(item);
121
158
  if (sourcePath && method === "symlink") {
122
159
  const sourceFile = join(sourcePath, "AGENT.md");
@@ -144,10 +181,10 @@ async function installAgentForCodingAgent(item, agent, scope, method, cwd) {
144
181
  return { agent, success: false, path: "", error: errorMsg };
145
182
  }
146
183
  }
147
- async function installAgent(item, agents, scope, method, cwd = process.cwd()) {
184
+ async function installAgent(item, agents, scope, method, force, cwd = process.cwd()) {
148
185
  const results = [];
149
186
  for (const agent of agents) {
150
- const result = await installAgentForCodingAgent(item, agent, scope, method, cwd);
187
+ const result = await installAgentForCodingAgent(item, agent, scope, method, force, cwd);
151
188
  results.push(result);
152
189
  }
153
190
  return results;
@@ -171,8 +208,8 @@ async function getInstalledAgents(agent, scope, cwd = process.cwd()) {
171
208
  }
172
209
  var agentHandler = {
173
210
  type: "agent",
174
- async install(item, agents, scope, method, cwd) {
175
- return installAgent(item, agents, scope, method, cwd);
211
+ async install(item, agents, scope, method, force, cwd) {
212
+ return installAgent(item, agents, scope, method, force, cwd);
176
213
  },
177
214
  async uninstall(slug, agent, scope, cwd) {
178
215
  return uninstallAgent(slug, agent, scope, cwd);
@@ -253,7 +290,7 @@ function findScriptFile(item) {
253
290
  }
254
291
  return null;
255
292
  }
256
- async function installHookForAgent(item, agent, scope, _method, cwd) {
293
+ async function installHookForAgent(item, agent, scope, _method, force, cwd) {
257
294
  const spinner = ora2(
258
295
  `Installing ${item.name} for ${CODING_AGENTS[agent].name}...`
259
296
  ).start();
@@ -273,6 +310,7 @@ async function installHookForAgent(item, agent, scope, _method, cwd) {
273
310
  await mkdir(hooksDir, { recursive: true });
274
311
  const sourcePath = getItemSourcePath(item);
275
312
  const destScriptPath = join2(hooksDir, scriptFile);
313
+ await assertOverwritable(destScriptPath, force);
276
314
  if (sourcePath && await exists(sourcePath)) {
277
315
  const sourceScriptPath = join2(sourcePath, scriptFile);
278
316
  await copyFile(sourceScriptPath, destScriptPath);
@@ -327,10 +365,10 @@ async function installHookForAgent(item, agent, scope, _method, cwd) {
327
365
  return { agent, success: false, path: "", error: errorMsg };
328
366
  }
329
367
  }
330
- async function installHook(item, agents, scope, method, cwd = process.cwd()) {
368
+ async function installHook(item, agents, scope, method, force, cwd = process.cwd()) {
331
369
  const results = [];
332
370
  for (const agent of agents) {
333
- const result = await installHookForAgent(item, agent, scope, method, cwd);
371
+ const result = await installHookForAgent(item, agent, scope, method, force, cwd);
334
372
  results.push(result);
335
373
  }
336
374
  return results;
@@ -403,8 +441,8 @@ async function getInstalledHooks(agent, scope, cwd = process.cwd()) {
403
441
  }
404
442
  var hookHandler = {
405
443
  type: "hook",
406
- async install(item, agents, scope, method, cwd) {
407
- return installHook(item, agents, scope, method, cwd);
444
+ async install(item, agents, scope, method, force, cwd) {
445
+ return installHook(item, agents, scope, method, force, cwd);
408
446
  },
409
447
  async uninstall(slug, agent, scope, cwd) {
410
448
  return uninstallHook(slug, agent, scope, cwd);
@@ -424,7 +462,7 @@ function parseMcpDefinition(content) {
424
462
  throw new Error("Invalid MCP definition: must be valid JSON");
425
463
  }
426
464
  }
427
- async function installMcpForAgent(item, agent, scope, _method, cwd) {
465
+ async function installMcpForAgent(item, agent, scope, _method, force, cwd) {
428
466
  const spinner = ora3(
429
467
  `Installing ${item.name} for ${CODING_AGENTS[agent].name}...`
430
468
  ).start();
@@ -434,6 +472,11 @@ async function installMcpForAgent(item, agent, scope, _method, cwd) {
434
472
  const configPath = getMcpPath(scope, cwd);
435
473
  const config = await readJson(configPath);
436
474
  config.mcpServers = config.mcpServers || {};
475
+ if (!force && mcpDef.name in config.mcpServers) {
476
+ throw new Error(
477
+ `MCP server "${mcpDef.name}" already exists in ${configPath}; pass --force to overwrite`
478
+ );
479
+ }
437
480
  config.mcpServers[mcpDef.name] = mcpDef.config;
438
481
  await writeJson(configPath, config);
439
482
  spinner.succeed(
@@ -448,10 +491,10 @@ async function installMcpForAgent(item, agent, scope, _method, cwd) {
448
491
  return { agent, success: false, path: "", error: errorMsg };
449
492
  }
450
493
  }
451
- async function installMcp(item, agents, scope, method, cwd = process.cwd()) {
494
+ async function installMcp(item, agents, scope, method, force, cwd = process.cwd()) {
452
495
  const results = [];
453
496
  for (const agent of agents) {
454
- const result = await installMcpForAgent(item, agent, scope, method, cwd);
497
+ const result = await installMcpForAgent(item, agent, scope, method, force, cwd);
455
498
  results.push(result);
456
499
  }
457
500
  return results;
@@ -475,8 +518,8 @@ async function getInstalledMcpServers(_agent, scope, cwd = process.cwd()) {
475
518
  }
476
519
  var mcpHandler = {
477
520
  type: "mcp",
478
- async install(item, agents, scope, method, cwd) {
479
- return installMcp(item, agents, scope, method, cwd);
521
+ async install(item, agents, scope, method, force, cwd) {
522
+ return installMcp(item, agents, scope, method, force, cwd);
480
523
  },
481
524
  async uninstall(slug, agent, scope, cwd) {
482
525
  return uninstallMcp(slug, agent, scope, cwd);
@@ -496,7 +539,7 @@ function parseSettings(content) {
496
539
  throw new Error("Invalid settings: must be valid JSON");
497
540
  }
498
541
  }
499
- async function installSettingsForAgent(item, agent, scope, _method, cwd) {
542
+ async function installSettingsForAgent(item, agent, scope, _method, _force, cwd) {
500
543
  const spinner = ora4(
501
544
  `Installing ${item.name} for ${CODING_AGENTS[agent].name}...`
502
545
  ).start();
@@ -522,10 +565,10 @@ async function installSettingsForAgent(item, agent, scope, _method, cwd) {
522
565
  return { agent, success: false, path: "", error: errorMsg };
523
566
  }
524
567
  }
525
- async function installSettings(item, agents, scope, method, cwd = process.cwd()) {
568
+ async function installSettings(item, agents, scope, method, force, cwd = process.cwd()) {
526
569
  const results = [];
527
570
  for (const agent of agents) {
528
- const result = await installSettingsForAgent(item, agent, scope, method, cwd);
571
+ const result = await installSettingsForAgent(item, agent, scope, method, force, cwd);
529
572
  results.push(result);
530
573
  }
531
574
  return results;
@@ -585,8 +628,8 @@ async function getInstalledSettings(agent, scope, cwd = process.cwd()) {
585
628
  }
586
629
  var settingsHandler = {
587
630
  type: "settings",
588
- async install(item, agents, scope, method, cwd) {
589
- return installSettings(item, agents, scope, method, cwd);
631
+ async install(item, agents, scope, method, force, cwd) {
632
+ return installSettings(item, agents, scope, method, force, cwd);
590
633
  },
591
634
  async uninstall(slug, agent, scope, cwd) {
592
635
  return uninstallSettings(slug, agent, scope, cwd);
@@ -643,7 +686,7 @@ async function ensureMarketplaceRegistered(marketplace, item) {
643
686
  };
644
687
  await writeJson(KNOWN_MARKETPLACES_PATH, known);
645
688
  }
646
- async function installPluginForAgent(item, agent, scope, method, cwd) {
689
+ async function installPluginForAgent(item, agent, scope, method, force, cwd) {
647
690
  const spinner = ora5(
648
691
  `Installing ${item.name} for ${CODING_AGENTS[agent].name}...`
649
692
  ).start();
@@ -667,6 +710,12 @@ async function installPluginForAgent(item, agent, scope, method, cwd) {
667
710
  const pluginId = getPluginId(pluginName, marketplace);
668
711
  await ensureMarketplaceRegistered(marketplace, item);
669
712
  const cachePath = getPluginCachePath(marketplace, pluginName, version);
713
+ try {
714
+ await assertOverwritable(cachePath, force);
715
+ } catch (error2) {
716
+ await rm2(tmpPath, { recursive: true, force: true });
717
+ throw error2;
718
+ }
670
719
  await installDirectory(tmpPath, cachePath, "copy");
671
720
  await rm2(tmpPath, { recursive: true, force: true });
672
721
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -705,10 +754,10 @@ async function installPluginForAgent(item, agent, scope, method, cwd) {
705
754
  return { agent, success: false, path: "", error: errorMsg };
706
755
  }
707
756
  }
708
- async function installPlugin(item, agents, scope, method, cwd = process.cwd()) {
757
+ async function installPlugin(item, agents, scope, method, force, cwd = process.cwd()) {
709
758
  const results = [];
710
759
  for (const agent of agents) {
711
- const result = await installPluginForAgent(item, agent, scope, method, cwd);
760
+ const result = await installPluginForAgent(item, agent, scope, method, force, cwd);
712
761
  results.push(result);
713
762
  }
714
763
  return results;
@@ -757,8 +806,8 @@ async function getInstalledPlugins(agent, scope, cwd = process.cwd()) {
757
806
  }
758
807
  var pluginHandler = {
759
808
  type: "plugin",
760
- async install(item, agents, scope, method, cwd) {
761
- return installPlugin(item, agents, scope, method, cwd);
809
+ async install(item, agents, scope, method, force, cwd) {
810
+ return installPlugin(item, agents, scope, method, force, cwd);
762
811
  },
763
812
  async uninstall(slug, agent, scope, cwd) {
764
813
  return uninstallPlugin(slug, agent, scope, cwd);
@@ -839,7 +888,7 @@ function printDryRunSummary(item, agents, scope, method, cwd) {
839
888
  console.log(` Method: ${chalk7.white(method)}`);
840
889
  console.log();
841
890
  if (method === "symlink" && item.type === "skill") {
842
- const centralPath = getAgentsPath("skill", item.slug, cwd);
891
+ const centralPath = getAgentsPath("skill", item.slug, scope, cwd);
843
892
  console.log(brand(" Central storage:"));
844
893
  console.log(` ${chalk7.gray("\u2192")} ${chalk7.gray(centralPath)}`);
845
894
  console.log();
@@ -883,6 +932,11 @@ var addCommand = new Command("add").description("Install a skill, agent, hook, o
883
932
  try {
884
933
  printLogo();
885
934
  intro("Seedr");
935
+ const optionError = validateType(options.type) || validateScope(options.scope) || validateMethod(options.method);
936
+ if (optionError) {
937
+ error(optionError);
938
+ process.exit(1);
939
+ }
886
940
  const itemName = name;
887
941
  const contentType = options.type;
888
942
  const item = await resolveItem(itemName, contentType);
@@ -931,7 +985,7 @@ var addCommand = new Command("add").description("Install a skill, agent, hook, o
931
985
  } else if (agents.length === 1) {
932
986
  method = "copy";
933
987
  } else {
934
- const symlinkPath = getAgentsPath(item.type, item.slug, process.cwd());
988
+ const symlinkPath = getAgentsPath(item.type, item.slug, scope, process.cwd());
935
989
  const selected = await selectMethod(symlinkPath);
936
990
  if (p.isCancel(selected)) {
937
991
  cancelled();
@@ -954,8 +1008,9 @@ var addCommand = new Command("add").description("Install a skill, agent, hook, o
954
1008
  return;
955
1009
  }
956
1010
  }
1011
+ const force = Boolean(options.force) || !options.yes;
957
1012
  console.log();
958
- const results = await handler.install(item, agents, scope, method, process.cwd());
1013
+ const results = await handler.install(item, agents, scope, method, force, process.cwd());
959
1014
  trackInstalls(item.slug, item.type, results, scope);
960
1015
  printInstallSummary(results);
961
1016
  outro("Installation complete");
@@ -1106,10 +1161,15 @@ var removeCommand = new Command3("remove").alias("rm").description("Remove an in
1106
1161
  "Comma-separated coding agents or 'all'"
1107
1162
  ).option(
1108
1163
  "--scope <scope>",
1109
- "Installation scope: project, user, or global",
1164
+ "Installation scope: project, user, or local",
1110
1165
  "project"
1111
1166
  ).option("-y, --yes", "Skip confirmation prompts").action(async (name, options) => {
1112
1167
  try {
1168
+ const optionError = validateScope(options.scope) || validateType(options.type);
1169
+ if (optionError) {
1170
+ console.log(chalk9.red(optionError));
1171
+ process.exit(1);
1172
+ }
1113
1173
  const scope = options.scope;
1114
1174
  const type = options.type;
1115
1175
  if (!type) {
@@ -1222,9 +1282,78 @@ Browse available skills at https://seedr.danieldeusing.de
1222
1282
  }
1223
1283
  });
1224
1284
 
1285
+ // package.json
1286
+ var package_default = {
1287
+ name: "@danieldeusing/seedr",
1288
+ version: "0.1.82",
1289
+ description: "Seed your projects with AI configurations",
1290
+ type: "module",
1291
+ bin: {
1292
+ seedr: "dist/cli.js"
1293
+ },
1294
+ exports: {
1295
+ ".": {
1296
+ import: "./dist/index.js",
1297
+ types: "./dist/index.d.ts"
1298
+ }
1299
+ },
1300
+ files: [
1301
+ "dist"
1302
+ ],
1303
+ scripts: {
1304
+ build: "tsup",
1305
+ dev: "tsx src/cli.ts",
1306
+ typecheck: "tsc --noEmit",
1307
+ lint: "eslint src/",
1308
+ "lint:fix": "eslint src/ --fix",
1309
+ clean: "rm -rf dist",
1310
+ test: "vitest run",
1311
+ "test:watch": "vitest",
1312
+ "test:coverage": "vitest run --coverage"
1313
+ },
1314
+ dependencies: {
1315
+ "@clack/prompts": "^1.0.1",
1316
+ chalk: "^5.3.0",
1317
+ commander: "^14.0.0",
1318
+ "gray-matter": "^4.0.3",
1319
+ inquirer: "^13.0.0",
1320
+ ora: "^9.0.0"
1321
+ },
1322
+ devDependencies: {
1323
+ "@seedr/shared": "workspace:*",
1324
+ "@types/inquirer": "^9.0.7",
1325
+ "@types/node": "^25.0.0",
1326
+ "@vitest/coverage-v8": "^4.1.0",
1327
+ memfs: "^4.14.0",
1328
+ tsup: "^8.3.0",
1329
+ tsx: "^4.19.0",
1330
+ typescript: "^5.7.0",
1331
+ vitest: "^4.1.0"
1332
+ },
1333
+ keywords: [
1334
+ "ai",
1335
+ "claude",
1336
+ "copilot",
1337
+ "gemini",
1338
+ "codex",
1339
+ "opencode",
1340
+ "configuration",
1341
+ "seeder"
1342
+ ],
1343
+ repository: {
1344
+ type: "git",
1345
+ url: "git+https://github.com/danieldeusing/seedr.git"
1346
+ },
1347
+ homepage: "https://seedr.danieldeusing.de",
1348
+ bugs: {
1349
+ url: "https://github.com/danieldeusing/seedr/issues"
1350
+ },
1351
+ license: "MIT"
1352
+ };
1353
+
1225
1354
  // src/cli.ts
1226
1355
  var program = new Command5();
1227
- program.name("seedr").description("Seed your Coding Agents with capabilities").version("0.1.0").addCommand(addCommand).addCommand(listCommand).addCommand(removeCommand).addCommand(initCommand);
1356
+ program.name("seedr").description("Seed your Coding Agents with capabilities").version(package_default.version).addCommand(addCommand).addCommand(listCommand).addCommand(removeCommand).addCommand(initCommand);
1228
1357
  program.action(() => {
1229
1358
  printLogo();
1230
1359
  program.help();
package/dist/index.d.ts CHANGED
@@ -69,7 +69,7 @@ interface InstallResult {
69
69
  error?: string;
70
70
  }
71
71
 
72
- declare function installSkill(item: RegistryItem, agents: CodingAgent[], scope: InstallScope, method: InstallMethod, cwd?: string): Promise<InstallResult[]>;
72
+ declare function installSkill(item: RegistryItem, agents: CodingAgent[], scope: InstallScope, method: InstallMethod, force: boolean, cwd?: string): Promise<InstallResult[]>;
73
73
  declare function uninstallSkill(slug: string, agent: CodingAgent, scope: InstallScope, cwd?: string): Promise<boolean>;
74
74
  declare function getInstalledSkills(agent: CodingAgent, scope: InstallScope, cwd?: string): Promise<string[]>;
75
75
 
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  loadManifest,
16
16
  searchItems,
17
17
  uninstallSkill
18
- } from "./chunk-5XFKC3T7.js";
18
+ } from "./chunk-77M7PGDO.js";
19
19
 
20
20
  // src/converters/index.ts
21
21
  import matter from "gray-matter";
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@danieldeusing/seedr",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "Seed your projects with AI configurations",
5
5
  "type": "module",
6
6
  "bin": {
7
- "seedr": "./dist/cli.js"
7
+ "seedr": "dist/cli.js"
8
8
  },
9
9
  "exports": {
10
10
  ".": {
@@ -30,23 +30,20 @@
30
30
  "@clack/prompts": "^1.0.1",
31
31
  "chalk": "^5.3.0",
32
32
  "commander": "^14.0.0",
33
- "fs-extra": "^11.2.0",
34
33
  "gray-matter": "^4.0.3",
35
34
  "inquirer": "^13.0.0",
36
- "ora": "^9.0.0",
37
- "simple-git": "^3.32.3"
35
+ "ora": "^9.0.0"
38
36
  },
39
37
  "devDependencies": {
40
38
  "@seedr/shared": "workspace:*",
41
- "@types/fs-extra": "^11.0.4",
42
39
  "@types/inquirer": "^9.0.7",
43
40
  "@types/node": "^25.0.0",
44
- "@vitest/coverage-v8": "^4.0.0",
41
+ "@vitest/coverage-v8": "^4.1.0",
45
42
  "memfs": "^4.14.0",
46
43
  "tsup": "^8.3.0",
47
44
  "tsx": "^4.19.0",
48
45
  "typescript": "^5.7.0",
49
- "vitest": "^4.0.0"
46
+ "vitest": "^4.1.0"
50
47
  },
51
48
  "keywords": [
52
49
  "ai",
@@ -60,7 +57,7 @@
60
57
  ],
61
58
  "repository": {
62
59
  "type": "git",
63
- "url": "https://github.com/danieldeusing/seedr.git"
60
+ "url": "git+https://github.com/danieldeusing/seedr.git"
64
61
  },
65
62
  "homepage": "https://seedr.danieldeusing.de",
66
63
  "bugs": {