@toolr/seedr 0.1.51 → 0.1.53

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/cli.js +135 -27
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -21,7 +21,6 @@ import {
21
21
  removeFile,
22
22
  searchItems,
23
23
  skillHandler,
24
- uninstallSkill,
25
24
  writeTextFile
26
25
  } from "./chunk-U2KZAN6F.js";
27
26
 
@@ -165,7 +164,7 @@ function trackInstalls(slug, type, results, scope) {
165
164
  type,
166
165
  tool: result.tool,
167
166
  scope,
168
- version: "0.1.51"
167
+ version: "0.1.53"
169
168
  }),
170
169
  signal: AbortSignal.timeout(4e3)
171
170
  }).catch(() => {
@@ -269,8 +268,8 @@ var agentHandler = {
269
268
  };
270
269
 
271
270
  // src/handlers/hook.ts
272
- import { join as join2 } from "path";
273
- import { mkdir, copyFile, chmod } from "fs/promises";
271
+ import { join as join2, basename } from "path";
272
+ import { mkdir, copyFile, chmod, rm } from "fs/promises";
274
273
  import chalk4 from "chalk";
275
274
  import ora2 from "ora";
276
275
 
@@ -361,8 +360,8 @@ async function installHookForTool(item, tool, scope, _method, cwd) {
361
360
  const tempDir = join2(cwd, ".claude", ".tmp", item.slug);
362
361
  await fetchItemToDestination(item, tempDir);
363
362
  await copyFile(join2(tempDir, scriptFile), destScriptPath);
364
- const { rm } = await import("fs/promises");
365
- await rm(tempDir, { recursive: true, force: true });
363
+ const { rm: rm2 } = await import("fs/promises");
364
+ await rm2(tempDir, { recursive: true, force: true });
366
365
  }
367
366
  await chmod(destScriptPath, 493);
368
367
  const settingsPath = getSettingsPath(scope, cwd);
@@ -417,18 +416,69 @@ async function installHook(item, tools, scope, method, cwd = process.cwd()) {
417
416
  }
418
417
  return results;
419
418
  }
420
- async function uninstallHook(_slug, tool, scope, cwd = process.cwd()) {
419
+ async function uninstallHook(slug, tool, scope, cwd = process.cwd()) {
421
420
  if (tool !== "claude") return false;
422
421
  const settingsPath = getSettingsPath(scope, cwd);
423
422
  if (!await exists(settingsPath)) return false;
424
- return false;
423
+ const item = await getItem(slug, "hook");
424
+ const scriptFile = item ? findScriptFile(item) : null;
425
+ const expectedPath = scriptFile ? getScriptPath(scope, scriptFile) : null;
426
+ const settings = await readJson(settingsPath);
427
+ if (!settings.hooks) return false;
428
+ let removed = false;
429
+ for (const event of Object.keys(settings.hooks)) {
430
+ const entries = settings.hooks[event];
431
+ if (!entries) continue;
432
+ for (let i = entries.length - 1; i >= 0; i--) {
433
+ const entry = entries[i];
434
+ const originalLength = entry.hooks.length;
435
+ entry.hooks = entry.hooks.filter((h) => {
436
+ if (expectedPath && h.command === expectedPath) return false;
437
+ const cmdBasename = basename(h.command, ".sh");
438
+ return cmdBasename !== slug;
439
+ });
440
+ if (entry.hooks.length < originalLength) {
441
+ removed = true;
442
+ }
443
+ if (entry.hooks.length === 0) {
444
+ entries.splice(i, 1);
445
+ }
446
+ }
447
+ if (entries.length === 0) {
448
+ delete settings.hooks[event];
449
+ }
450
+ }
451
+ if (Object.keys(settings.hooks).length === 0) {
452
+ delete settings.hooks;
453
+ }
454
+ if (removed) {
455
+ await writeJson(settingsPath, settings);
456
+ }
457
+ const hooksDir = getHooksDir(scope, cwd);
458
+ const scriptFileName = scriptFile || `${slug}.sh`;
459
+ const scriptFilePath = join2(hooksDir, scriptFileName);
460
+ if (await exists(scriptFilePath)) {
461
+ await rm(scriptFilePath);
462
+ removed = true;
463
+ }
464
+ return removed;
425
465
  }
426
466
  async function getInstalledHooks(tool, scope, cwd = process.cwd()) {
427
467
  if (tool !== "claude") return [];
428
468
  const settingsPath = getSettingsPath(scope, cwd);
429
469
  if (!await exists(settingsPath)) return [];
430
470
  const settings = await readJson(settingsPath);
431
- return Object.keys(settings.hooks || {});
471
+ if (!settings.hooks) return [];
472
+ const slugs = /* @__PURE__ */ new Set();
473
+ for (const entries of Object.values(settings.hooks)) {
474
+ for (const entry of entries) {
475
+ for (const hook of entry.hooks) {
476
+ const slug = basename(hook.command, ".sh");
477
+ slugs.add(slug);
478
+ }
479
+ }
480
+ }
481
+ return Array.from(slugs);
432
482
  }
433
483
  var hookHandler = {
434
484
  type: "hook",
@@ -559,8 +609,52 @@ async function installSettings(item, tools, scope, method, cwd = process.cwd())
559
609
  }
560
610
  return results;
561
611
  }
562
- async function uninstallSettings(_slug, _tool, _scope, _cwd = process.cwd()) {
563
- return false;
612
+ function deepUnmerge(target, source) {
613
+ let changed = false;
614
+ const result = { ...target };
615
+ for (const key of Object.keys(source)) {
616
+ if (!(key in result)) continue;
617
+ const sourceValue = source[key];
618
+ const targetValue = result[key];
619
+ if (sourceValue !== null && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue !== null && typeof targetValue === "object" && !Array.isArray(targetValue)) {
620
+ const nested = deepUnmerge(
621
+ targetValue,
622
+ sourceValue
623
+ );
624
+ if (nested.changed) {
625
+ changed = true;
626
+ if (Object.keys(nested.result).length === 0) {
627
+ delete result[key];
628
+ } else {
629
+ result[key] = nested.result;
630
+ }
631
+ }
632
+ } else {
633
+ delete result[key];
634
+ changed = true;
635
+ }
636
+ }
637
+ return { result, changed };
638
+ }
639
+ async function uninstallSettings(slug, tool, scope, cwd = process.cwd()) {
640
+ if (tool !== "claude") return false;
641
+ const item = await getItem(slug, "settings");
642
+ if (!item) return false;
643
+ let content;
644
+ try {
645
+ content = await getItemContent(item);
646
+ } catch {
647
+ return false;
648
+ }
649
+ const itemSettings = parseSettings(content);
650
+ const settingsPath = getSettingsPath(scope, cwd);
651
+ if (!await exists(settingsPath)) return false;
652
+ const currentSettings = await readJson(settingsPath);
653
+ const { result, changed } = deepUnmerge(currentSettings, itemSettings);
654
+ if (changed) {
655
+ await writeJson(settingsPath, result);
656
+ }
657
+ return changed;
564
658
  }
565
659
  async function getInstalledSettings(tool, scope, cwd = process.cwd()) {
566
660
  if (tool !== "claude") return [];
@@ -646,18 +740,15 @@ async function installPluginForTool(item, tool, scope, method, cwd) {
646
740
  const pluginJson = await readJson(
647
741
  join3(tmpPath, ".claude-plugin", "plugin.json")
648
742
  );
649
- const marketplaceJson = await readJson(
650
- join3(tmpPath, ".claude-plugin", "marketplace.json")
651
- );
652
- const marketplace = marketplaceJson.name || item.author?.name || "seedr";
743
+ const marketplace = item.marketplace || item.author?.name || "seedr";
653
744
  const pluginName = pluginJson.name || item.slug;
654
745
  const version = pluginJson.version || "1.0.0";
655
746
  const pluginId = getPluginId(pluginName, marketplace);
656
747
  await ensureMarketplaceRegistered(marketplace, item);
657
748
  const cachePath = getPluginCachePath(marketplace, pluginName, version);
658
- const { rm } = await import("fs/promises");
749
+ const { rm: rm2 } = await import("fs/promises");
659
750
  await installDirectory(tmpPath, cachePath, "copy");
660
- await rm(tmpPath, { recursive: true, force: true });
751
+ await rm2(tmpPath, { recursive: true, force: true });
661
752
  const now = (/* @__PURE__ */ new Date()).toISOString();
662
753
  const registry = await readJson(INSTALLED_PLUGINS_PATH);
663
754
  registry.version = registry.version || 2;
@@ -1061,21 +1152,25 @@ async function promptConfirm(message, defaultValue = true) {
1061
1152
  }
1062
1153
 
1063
1154
  // src/commands/remove.ts
1064
- async function findInstalledTools(name, scope) {
1155
+ async function findInstalledTools(slug, type, scope) {
1156
+ const handler = getHandler(type);
1157
+ if (!handler) return [];
1065
1158
  const tools = [];
1066
1159
  for (const tool of ALL_TOOLS) {
1067
- const installed = await getInstalledSkills(tool, scope);
1068
- if (installed.includes(name)) {
1160
+ const installed = await handler.listInstalled(tool, scope);
1161
+ if (installed.includes(slug)) {
1069
1162
  tools.push(tool);
1070
1163
  }
1071
1164
  }
1072
1165
  return tools;
1073
1166
  }
1074
- async function removeFromTools(name, tools, scope) {
1167
+ async function removeFromTools(slug, type, tools, scope) {
1168
+ const handler = getHandler(type);
1169
+ if (!handler) return 0;
1075
1170
  let successCount = 0;
1076
1171
  for (const tool of tools) {
1077
1172
  const spinner = ora6(`Removing from ${AI_TOOLS[tool].name}...`).start();
1078
- const removed = await uninstallSkill(name, tool, scope);
1173
+ const removed = await handler.uninstall(slug, tool, scope);
1079
1174
  if (removed) {
1080
1175
  spinner.succeed(chalk10.green(`Removed from ${AI_TOOLS[tool].name}`));
1081
1176
  successCount++;
@@ -1085,7 +1180,7 @@ async function removeFromTools(name, tools, scope) {
1085
1180
  }
1086
1181
  return successCount;
1087
1182
  }
1088
- var removeCommand = new Command3("remove").alias("rm").description("Remove an installed skill").argument("<name>", "Name of the skill to remove").option(
1183
+ var removeCommand = new Command3("remove").alias("rm").description("Remove an installed item (skill, plugin, agent, hook, mcp)").argument("<name>", "Name/slug of the item to remove").option("-t, --type <type>", "Content type: skill, agent, hook, mcp, plugin, settings").option(
1089
1184
  "-a, --agents <tools>",
1090
1185
  "Comma-separated AI tools or 'all'"
1091
1186
  ).option(
@@ -1095,15 +1190,28 @@ var removeCommand = new Command3("remove").alias("rm").description("Remove an in
1095
1190
  ).option("-y, --yes", "Skip confirmation prompts").action(async (name, options) => {
1096
1191
  try {
1097
1192
  const scope = options.scope;
1098
- const tools = options.agents ? parseToolsArg(options.agents, ALL_TOOLS) : await findInstalledTools(name, scope);
1193
+ const type = options.type;
1194
+ if (!type) {
1195
+ console.log(
1196
+ chalk10.yellow(`Please specify the content type with --type (skill, plugin, agent, hook, mcp, settings)`)
1197
+ );
1198
+ process.exit(1);
1199
+ }
1200
+ const handler = getHandler(type);
1201
+ if (!handler) {
1202
+ console.log(chalk10.red(`No handler found for type "${type}"`));
1203
+ process.exit(1);
1204
+ }
1205
+ const tools = options.agents ? parseToolsArg(options.agents, ALL_TOOLS) : await findInstalledTools(name, type, scope);
1099
1206
  if (tools.length === 0) {
1100
1207
  console.log(
1101
- chalk10.yellow(`Skill "${name}" is not installed in ${scope} scope`)
1208
+ chalk10.yellow(`${type} "${name}" is not installed in ${scope} scope`)
1102
1209
  );
1103
1210
  process.exit(0);
1104
1211
  }
1105
1212
  if (!options.yes) {
1106
- console.log(chalk10.cyan("\nWill remove from:"));
1213
+ console.log(chalk10.cyan(`
1214
+ Will remove ${type} "${name}" from:`));
1107
1215
  for (const tool of tools) {
1108
1216
  console.log(` - ${AI_TOOLS[tool].name}`);
1109
1217
  }
@@ -1114,7 +1222,7 @@ var removeCommand = new Command3("remove").alias("rm").description("Remove an in
1114
1222
  process.exit(0);
1115
1223
  }
1116
1224
  }
1117
- const successCount = await removeFromTools(name, tools, scope);
1225
+ const successCount = await removeFromTools(name, type, tools, scope);
1118
1226
  console.log("");
1119
1227
  if (successCount > 0) {
1120
1228
  console.log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolr/seedr",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "description": "Seed your projects with AI configurations",
5
5
  "type": "module",
6
6
  "bin": {