@toolr/seedr 0.1.13 → 0.1.15

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 +113 -29
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -248,6 +248,8 @@ var agentHandler = {
248
248
  };
249
249
 
250
250
  // src/handlers/hook.ts
251
+ import { join as join2 } from "path";
252
+ import { mkdir, copyFile, chmod } from "fs/promises";
251
253
  import chalk4 from "chalk";
252
254
  import ora2 from "ora";
253
255
 
@@ -283,14 +285,35 @@ function deepMerge(target, source) {
283
285
  }
284
286
 
285
287
  // src/handlers/hook.ts
286
- function parseHookDefinition(content) {
287
- try {
288
- return JSON.parse(content);
289
- } catch {
290
- throw new Error("Invalid hook definition: must be valid JSON");
288
+ function getHooksDir(scope, cwd) {
289
+ switch (scope) {
290
+ case "user":
291
+ return join2(process.env.HOME || "~", ".claude", "hooks");
292
+ case "project":
293
+ case "local":
294
+ return join2(cwd, ".claude", "hooks");
291
295
  }
292
296
  }
293
- async function installHookForTool(item, tool, scope, _method, cwd) {
297
+ function getScriptPath(scope, scriptName) {
298
+ switch (scope) {
299
+ case "user":
300
+ return `~/.claude/hooks/${scriptName}`;
301
+ case "project":
302
+ case "local":
303
+ return `.claude/hooks/${scriptName}`;
304
+ }
305
+ }
306
+ function findScriptFile(item) {
307
+ const files = item.contents?.files;
308
+ if (!files) return null;
309
+ for (const file of files) {
310
+ if (file.type === "file" && file.name.endsWith(".sh")) {
311
+ return file.name;
312
+ }
313
+ }
314
+ return null;
315
+ }
316
+ async function installHookForTool(item, tool, scope, method, cwd) {
294
317
  const spinner = ora2(
295
318
  `Installing ${item.name} for ${AI_TOOLS[tool].name}...`
296
319
  ).start();
@@ -298,19 +321,65 @@ async function installHookForTool(item, tool, scope, _method, cwd) {
298
321
  if (tool !== "claude") {
299
322
  throw new Error("Hooks are only supported for Claude Code");
300
323
  }
301
- const content = await getItemContent(item);
302
- const hookDef = parseHookDefinition(content);
324
+ const triggers = item.contents?.triggers;
325
+ if (!triggers || triggers.length === 0) {
326
+ throw new Error("No triggers defined for this hook");
327
+ }
328
+ const scriptFile = findScriptFile(item);
329
+ if (!scriptFile) {
330
+ throw new Error("No script file found in hook");
331
+ }
332
+ const hooksDir = getHooksDir(scope, cwd);
333
+ await mkdir(hooksDir, { recursive: true });
334
+ const sourcePath = getItemSourcePath(item);
335
+ const destScriptPath = join2(hooksDir, scriptFile);
336
+ if (sourcePath && await exists(sourcePath)) {
337
+ const sourceScriptPath = join2(sourcePath, scriptFile);
338
+ await copyFile(sourceScriptPath, destScriptPath);
339
+ } else {
340
+ const tempDir = join2(cwd, ".claude", ".tmp", item.slug);
341
+ await fetchItemToDestination(item, tempDir);
342
+ await copyFile(join2(tempDir, scriptFile), destScriptPath);
343
+ const { rm } = await import("fs/promises");
344
+ await rm(tempDir, { recursive: true, force: true });
345
+ }
346
+ await chmod(destScriptPath, 493);
303
347
  const settingsPath = getSettingsPath(scope, cwd);
304
348
  const settings = await readJson(settingsPath);
305
349
  settings.hooks = settings.hooks || {};
306
- const hookKey = hookDef.matcher ? `${hookDef.event}:${hookDef.matcher}` : hookDef.event;
307
- const existingHooks = settings.hooks[hookKey] || [];
308
- settings.hooks[hookKey] = [...existingHooks, ...hookDef.hooks];
350
+ const scriptPath = getScriptPath(scope, scriptFile);
351
+ for (const trigger of triggers) {
352
+ const event = trigger.event;
353
+ const matcher = trigger.matcher;
354
+ if (!settings.hooks[event]) {
355
+ settings.hooks[event] = [];
356
+ }
357
+ const existingEntry = settings.hooks[event].find(
358
+ (e) => e.matcher === matcher
359
+ );
360
+ const hookCommand = { type: "command", command: scriptPath };
361
+ if (existingEntry) {
362
+ const alreadyExists = existingEntry.hooks.some(
363
+ (h) => h.command === scriptPath
364
+ );
365
+ if (!alreadyExists) {
366
+ existingEntry.hooks.push(hookCommand);
367
+ }
368
+ } else {
369
+ const newEntry = {
370
+ hooks: [hookCommand]
371
+ };
372
+ if (matcher) {
373
+ newEntry.matcher = matcher;
374
+ }
375
+ settings.hooks[event].push(newEntry);
376
+ }
377
+ }
309
378
  await writeJson(settingsPath, settings);
310
379
  spinner.succeed(
311
380
  chalk4.green(`Installed ${item.name} for ${AI_TOOLS[tool].name}`)
312
381
  );
313
- return { tool, success: true, path: settingsPath };
382
+ return { tool, success: true, path: destScriptPath };
314
383
  } catch (error2) {
315
384
  const errorMsg = error2 instanceof Error ? error2.message : "Unknown error";
316
385
  spinner.fail(
@@ -493,14 +562,14 @@ var settingsHandler = {
493
562
 
494
563
  // src/handlers/plugin.ts
495
564
  import { homedir } from "os";
496
- import { join as join2 } from "path";
565
+ import { join as join3 } from "path";
497
566
  import chalk7 from "chalk";
498
567
  import ora5 from "ora";
499
568
  var home = homedir();
500
- var PLUGINS_CACHE_DIR = join2(home, ".claude/plugins/cache");
501
- var INSTALLED_PLUGINS_PATH = join2(home, ".claude/plugins/installed_plugins.json");
569
+ var PLUGINS_CACHE_DIR = join3(home, ".claude/plugins/cache");
570
+ var INSTALLED_PLUGINS_PATH = join3(home, ".claude/plugins/installed_plugins.json");
502
571
  function getPluginCachePath(marketplace, name, version) {
503
- return join2(PLUGINS_CACHE_DIR, marketplace, name, version);
572
+ return join3(PLUGINS_CACHE_DIR, marketplace, name, version);
504
573
  }
505
574
  function getPluginId(name, marketplace) {
506
575
  return `${name}@${marketplace}`;
@@ -513,24 +582,39 @@ async function installPluginForTool(item, tool, scope, method, cwd) {
513
582
  if (tool !== "claude") {
514
583
  throw new Error("Plugins are only supported for Claude Code");
515
584
  }
516
- const marketplace = item.author?.name || "seedr";
517
- const version = "1.0.0";
518
- const pluginId = getPluginId(item.slug, marketplace);
519
- const cachePath = getPluginCachePath(marketplace, item.slug, version);
585
+ const tmpPath = join3(PLUGINS_CACHE_DIR, ".tmp", item.slug);
520
586
  const sourcePath = getItemSourcePath(item);
521
587
  if (sourcePath) {
522
- await installDirectory(sourcePath, cachePath, method);
588
+ await installDirectory(sourcePath, tmpPath, method);
523
589
  } else {
524
- await fetchItemToDestination(item, cachePath);
590
+ await fetchItemToDestination(item, tmpPath);
525
591
  }
592
+ const pluginJson = await readJson(
593
+ join3(tmpPath, ".claude-plugin", "plugin.json")
594
+ );
595
+ const marketplaceJson = await readJson(
596
+ join3(tmpPath, ".claude-plugin", "marketplace.json")
597
+ );
598
+ const marketplace = marketplaceJson.name || item.author?.name || "seedr";
599
+ const pluginName = pluginJson.name || item.slug;
600
+ const version = pluginJson.version || "1.0.0";
601
+ const pluginId = getPluginId(pluginName, marketplace);
602
+ const cachePath = getPluginCachePath(marketplace, pluginName, version);
603
+ const { rm } = await import("fs/promises");
604
+ await installDirectory(tmpPath, cachePath, "copy");
605
+ await rm(tmpPath, { recursive: true, force: true });
606
+ const now = (/* @__PURE__ */ new Date()).toISOString();
526
607
  const registry = await readJson(INSTALLED_PLUGINS_PATH);
608
+ registry.version = registry.version || 2;
527
609
  registry.plugins = registry.plugins || {};
528
610
  const installInfo = {
529
611
  scope,
530
- projectPath: scope === "project" ? cwd : void 0,
612
+ ...scope === "project" ? { projectPath: cwd } : {},
531
613
  installPath: cachePath,
532
614
  version,
533
- installedAt: (/* @__PURE__ */ new Date()).toISOString()
615
+ installedAt: now,
616
+ lastUpdated: now,
617
+ gitCommitSha: ""
534
618
  };
535
619
  const existingEntries = registry.plugins[pluginId] || [];
536
620
  const filteredEntries = existingEntries.filter(
@@ -716,14 +800,14 @@ function printInstallSummary(results) {
716
800
  process.exit(1);
717
801
  }
718
802
  }
719
- var addCommand = new Command("add").description("Install a skill, agent, hook, or other configuration").argument("[name]", "Name of the item to install").option("-s, --skill <name>", "Skill name (alternative to positional arg)").option("-t, --type <type>", "Content type: skill, agent, hook, mcp, plugin, settings").option(
803
+ var addCommand = new Command("add").description("Install a skill, agent, hook, or other configuration").argument("[name]", "Name of the item to install").option("-t, --type <type>", "Content type: skill, agent, hook, mcp, plugin, settings").option(
720
804
  "-a, --agents <tools>",
721
805
  "Comma-separated AI tools or 'all' (claude,copilot,gemini,codex,opencode)"
722
- ).option("--scope <scope>", "Installation scope: project, user, or local").option("-m, --method <method>", "Installation method: symlink or copy").option("-y, --yes", "Skip confirmation prompts").option("-f, --force", "Overwrite existing files").option("-n, --dry-run", "Show what would be installed without making changes").action(async (name, options) => {
806
+ ).option("-s, --scope <scope>", "Installation scope: project, user, or local").option("-m, --method <method>", "Installation method: symlink or copy").option("-y, --yes", "Skip confirmation prompts").option("-f, --force", "Overwrite existing files").option("-n, --dry-run", "Show what would be installed without making changes").action(async (name, options) => {
723
807
  try {
724
808
  printLogo();
725
809
  intro2("Seedr");
726
- const itemName = name ?? options.skill;
810
+ const itemName = name;
727
811
  const contentType = options.type;
728
812
  const item = await resolveItem(itemName, contentType);
729
813
  if (!item) process.exit(1);
@@ -979,7 +1063,7 @@ var removeCommand = new Command3("remove").alias("rm").description("Remove an in
979
1063
  import { Command as Command4 } from "commander";
980
1064
  import chalk11 from "chalk";
981
1065
  import ora7 from "ora";
982
- import { join as join3 } from "path";
1066
+ import { join as join4 } from "path";
983
1067
  var initCommand = new Command4("init").description("Initialize AI tool configuration directories").option(
984
1068
  "-a, --agents <tools>",
985
1069
  "Comma-separated AI tools or 'all'",
@@ -1014,7 +1098,7 @@ var initCommand = new Command4("init").description("Initialize AI tool configura
1014
1098
  continue;
1015
1099
  }
1016
1100
  await ensureDir(path);
1017
- const readmePath = join3(path, "README.md");
1101
+ const readmePath = join4(path, "README.md");
1018
1102
  await writeTextFile(
1019
1103
  readmePath,
1020
1104
  `# ${AI_TOOLS[tool].name} Configuration
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolr/seedr",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Seed your projects with AI configurations",
5
5
  "type": "module",
6
6
  "bin": {