agentinit 1.16.0 → 1.16.2

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.
package/dist/cli.js CHANGED
@@ -17312,6 +17312,8 @@ class PluginManager {
17312
17312
  (async () => !!manifest.commands || await isDirectory(join4(pluginDir, "commands")))(),
17313
17313
  (async () => !!manifest.hooks || await isDirectory(join4(pluginDir, "hooks")))(),
17314
17314
  (async () => !!manifest.agents || await isDirectory(join4(pluginDir, "agents")))(),
17315
+ isDirectory(join4(pluginDir, "skills")),
17316
+ (async () => !!manifest.mcpServers || await fileExists(join4(pluginDir, ".mcp.json")))(),
17315
17317
  isDirectory(join4(pluginDir, "prompts")),
17316
17318
  isDirectory(join4(pluginDir, "schemas")),
17317
17319
  isDirectory(join4(pluginDir, "scripts")),
@@ -17321,10 +17323,12 @@ class PluginManager {
17321
17323
  ...featureChecks[0] ? ["commands"] : [],
17322
17324
  ...featureChecks[1] ? ["hooks"] : [],
17323
17325
  ...featureChecks[2] ? ["agents"] : [],
17324
- ...featureChecks[3] ? ["prompts"] : [],
17325
- ...featureChecks[4] ? ["schemas"] : [],
17326
- ...featureChecks[5] ? ["scripts"] : [],
17327
- ...featureChecks[6] ? ["templates"] : []
17326
+ ...featureChecks[3] ? ["skills"] : [],
17327
+ ...featureChecks[4] ? ["mcp servers"] : [],
17328
+ ...featureChecks[5] ? ["prompts"] : [],
17329
+ ...featureChecks[6] ? ["schemas"] : [],
17330
+ ...featureChecks[7] ? ["scripts"] : [],
17331
+ ...featureChecks[8] ? ["templates"] : []
17328
17332
  ];
17329
17333
  }
17330
17334
  async getClaudeNativeInstallTarget(plugin, pluginDir) {
@@ -17346,12 +17350,13 @@ class PluginManager {
17346
17350
  return null;
17347
17351
  }
17348
17352
  const baseNamespace = plugin.nativeClaudeBundle?.bundleName || plugin.source.marketplace || (plugin.source.owner && plugin.source.repo ? `${plugin.source.owner}-${plugin.source.repo}` : plugin.name);
17349
- const namespace = `agentinit-${this.slugifyPluginNamespace(baseNamespace)}`;
17353
+ const namespace = this.slugifyPluginNamespace(baseNamespace);
17350
17354
  const versionDir = this.slugifyPluginNamespace(plugin.version || "0.0.0");
17351
17355
  return {
17352
17356
  namespace,
17353
17357
  pluginKey: `${plugin.name}@${namespace}`,
17354
17358
  installPath: join4(homedir4(), ".claude", "plugins", "cache", namespace, plugin.name, versionDir),
17359
+ marketplacePath: getClaudeMarketplaceInstallPath(namespace),
17355
17360
  features
17356
17361
  };
17357
17362
  }
@@ -17374,6 +17379,24 @@ class PluginManager {
17374
17379
  async saveClaudeInstalledPlugins(state) {
17375
17380
  await writeFile(getClaudeInstalledPluginsPath(), JSON.stringify(state, null, 2));
17376
17381
  }
17382
+ async readClaudeKnownMarketplaces() {
17383
+ const content = await readFileIfExists(getClaudeKnownMarketplacesPath());
17384
+ if (!content) {
17385
+ return {};
17386
+ }
17387
+ try {
17388
+ const parsed = JSON.parse(content);
17389
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
17390
+ return {};
17391
+ }
17392
+ return parsed;
17393
+ } catch {
17394
+ return {};
17395
+ }
17396
+ }
17397
+ async saveClaudeKnownMarketplaces(state) {
17398
+ await writeFile(getClaudeKnownMarketplacesPath(), JSON.stringify(state, null, 2));
17399
+ }
17377
17400
  async readClaudeSettings() {
17378
17401
  const content = await readFileIfExists(getClaudeSettingsPath());
17379
17402
  if (!content) {
@@ -17395,6 +17418,145 @@ class PluginManager {
17395
17418
  async saveClaudeSettings(state) {
17396
17419
  await writeFile(getClaudeSettingsPath(), JSON.stringify(state, null, 2));
17397
17420
  }
17421
+ async findClaudeMarketplaceRoot(pluginDir) {
17422
+ let currentDir = resolve7(pluginDir);
17423
+ while (true) {
17424
+ if (await fileExists(join4(currentDir, ".claude-plugin", "marketplace.json"))) {
17425
+ return currentDir;
17426
+ }
17427
+ const parentDir = dirname2(currentDir);
17428
+ if (parentDir === currentDir) {
17429
+ return null;
17430
+ }
17431
+ currentDir = parentDir;
17432
+ }
17433
+ }
17434
+ async readClaudeMarketplaceManifest(marketplaceDir) {
17435
+ const manifestContent = await readFileIfExists(join4(marketplaceDir, ".claude-plugin", "marketplace.json"));
17436
+ if (!manifestContent) {
17437
+ return null;
17438
+ }
17439
+ try {
17440
+ const parsed = JSON.parse(manifestContent);
17441
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
17442
+ return null;
17443
+ }
17444
+ const plugins = Array.isArray(parsed.plugins) ? parsed.plugins.filter((entry) => !!entry && typeof entry === "object" && !Array.isArray(entry)) : [];
17445
+ return {
17446
+ ...parsed,
17447
+ plugins
17448
+ };
17449
+ } catch {
17450
+ return null;
17451
+ }
17452
+ }
17453
+ async saveClaudeMarketplaceManifest(marketplaceDir, manifest) {
17454
+ await fs22.mkdir(join4(marketplaceDir, ".claude-plugin"), { recursive: true });
17455
+ await writeFile(join4(marketplaceDir, ".claude-plugin", "marketplace.json"), JSON.stringify(manifest, null, 2));
17456
+ }
17457
+ resolveMarketplaceSourcePath(baseDir, relativePath) {
17458
+ const resolvedPath = resolve7(baseDir, relativePath);
17459
+ const relativePathFromBase = relative2(resolve7(baseDir), resolvedPath);
17460
+ if (relativePathFromBase.startsWith("..") || relativePathFromBase.includes("/../") || relativePathFromBase.includes("\\..\\")) {
17461
+ throw new Error(`Invalid marketplace source path "${relativePath}" in ${baseDir}`);
17462
+ }
17463
+ return resolvedPath;
17464
+ }
17465
+ async readClaudePluginMetadata(pluginDir) {
17466
+ const manifestContent = await readFileIfExists(join4(pluginDir, ".claude-plugin", "plugin.json"));
17467
+ if (!manifestContent) {
17468
+ return {};
17469
+ }
17470
+ try {
17471
+ const manifest = JSON.parse(manifestContent);
17472
+ return {
17473
+ ...typeof manifest.description === "string" ? { description: manifest.description } : {},
17474
+ ...typeof manifest.version === "string" ? { version: manifest.version } : {},
17475
+ ...manifest.author ? { author: manifest.author } : {}
17476
+ };
17477
+ } catch {
17478
+ return {};
17479
+ }
17480
+ }
17481
+ async copyClaudeMarketplacePlugin(sourcePluginDir, marketplacePath, pluginName) {
17482
+ const marketplacePluginPath = join4(marketplacePath, "plugins", pluginName);
17483
+ await fs22.mkdir(dirname2(marketplacePluginPath), { recursive: true });
17484
+ await fs22.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
17485
+ });
17486
+ await fs22.cp(sourcePluginDir, marketplacePluginPath, { recursive: true, dereference: true });
17487
+ return marketplacePluginPath;
17488
+ }
17489
+ getClaudeMarketplaceSource(plugin, marketplaceRoot, marketplacePath) {
17490
+ if (plugin.source.type === "github") {
17491
+ if (plugin.source.owner && plugin.source.repo) {
17492
+ return {
17493
+ source: "github",
17494
+ repo: `${plugin.source.owner}/${plugin.source.repo}`
17495
+ };
17496
+ }
17497
+ if (plugin.source.url) {
17498
+ return {
17499
+ source: "git",
17500
+ url: plugin.source.url
17501
+ };
17502
+ }
17503
+ }
17504
+ if (plugin.source.type === "local") {
17505
+ return {
17506
+ source: "directory",
17507
+ path: resolve7(marketplaceRoot || plugin.source.path || marketplacePath)
17508
+ };
17509
+ }
17510
+ return {
17511
+ source: "directory",
17512
+ path: marketplacePath
17513
+ };
17514
+ }
17515
+ async materializeClaudeMarketplace(plugin, pluginDir, target) {
17516
+ const marketplaceRoot = await this.findClaudeMarketplaceRoot(pluginDir);
17517
+ const marketplaceSource = this.getClaudeMarketplaceSource(plugin, marketplaceRoot, target.marketplacePath);
17518
+ await fs22.mkdir(target.marketplacePath, { recursive: true });
17519
+ const existingManifest = await this.readClaudeMarketplaceManifest(target.marketplacePath);
17520
+ const sourceManifest = marketplaceRoot ? await this.readClaudeMarketplaceManifest(marketplaceRoot) : null;
17521
+ const mergedManifest = {
17522
+ ...sourceManifest || {},
17523
+ ...existingManifest || {},
17524
+ name: target.namespace
17525
+ };
17526
+ const mergedEntries = new Map;
17527
+ for (const entry of existingManifest?.plugins || []) {
17528
+ if (typeof entry.name === "string" && entry.name) {
17529
+ mergedEntries.set(entry.name, entry);
17530
+ }
17531
+ }
17532
+ if (marketplaceRoot && sourceManifest) {
17533
+ for (const entry of sourceManifest.plugins || []) {
17534
+ if (typeof entry.name !== "string" || !entry.name || typeof entry.source !== "string" || !entry.source) {
17535
+ continue;
17536
+ }
17537
+ const sourcePluginDir = this.resolveMarketplaceSourcePath(marketplaceRoot, entry.source);
17538
+ await this.copyClaudeMarketplacePlugin(sourcePluginDir, target.marketplacePath, entry.name);
17539
+ mergedEntries.set(entry.name, {
17540
+ ...entry,
17541
+ source: `./plugins/${entry.name}`
17542
+ });
17543
+ }
17544
+ } else {
17545
+ await this.copyClaudeMarketplacePlugin(pluginDir, target.marketplacePath, plugin.name);
17546
+ const pluginMetadata = await this.readClaudePluginMetadata(pluginDir);
17547
+ mergedEntries.set(plugin.name, {
17548
+ ...mergedEntries.get(plugin.name) || {},
17549
+ name: plugin.name,
17550
+ source: `./plugins/${plugin.name}`,
17551
+ ...plugin.description ? { description: plugin.description } : {},
17552
+ ...plugin.version ? { version: plugin.version } : {},
17553
+ ...pluginMetadata
17554
+ });
17555
+ }
17556
+ mergedManifest.plugins = [...mergedEntries.values()].sort((left, right) => (left.name || "").localeCompare(right.name || ""));
17557
+ await this.saveClaudeMarketplaceManifest(target.marketplacePath, mergedManifest);
17558
+ return { source: marketplaceSource };
17559
+ }
17398
17560
  async installNativeClaudePlugin(plugin, pluginDir, agents) {
17399
17561
  const installed = [];
17400
17562
  const skipped = [];
@@ -17415,7 +17577,8 @@ class PluginManager {
17415
17577
  }
17416
17578
  warnings.push(`Claude Code-native plugin components detected (${featureLabel}); they will only work in Claude Code and install into ~/.claude/plugins.`);
17417
17579
  const claudeInstalled = await this.readClaudeInstalledPlugins();
17418
- const conflictingKey = Object.keys(claudeInstalled.plugins).find((key) => key !== nativeTarget.pluginKey && key.startsWith(`${plugin.name}@`));
17580
+ const legacyKeys = Object.keys(claudeInstalled.plugins).filter((key) => key !== nativeTarget.pluginKey && key.startsWith(`${plugin.name}@agentinit-`));
17581
+ const conflictingKey = Object.keys(claudeInstalled.plugins).find((key) => key !== nativeTarget.pluginKey && !legacyKeys.includes(key) && key.startsWith(`${plugin.name}@`));
17419
17582
  if (conflictingKey) {
17420
17583
  skipped.push({
17421
17584
  agent: "claude",
@@ -17428,6 +17591,7 @@ class PluginManager {
17428
17591
  });
17429
17592
  await fs22.mkdir(dirname2(nativeTarget.installPath), { recursive: true });
17430
17593
  await fs22.cp(pluginDir, nativeTarget.installPath, { recursive: true, dereference: true });
17594
+ const marketplace = await this.materializeClaudeMarketplace(plugin, pluginDir, nativeTarget);
17431
17595
  const now = new Date().toISOString();
17432
17596
  claudeInstalled.plugins[nativeTarget.pluginKey] = [{
17433
17597
  scope: "user",
@@ -17436,13 +17600,42 @@ class PluginManager {
17436
17600
  installedAt: now,
17437
17601
  lastUpdated: now
17438
17602
  }];
17603
+ const legacyEntries = legacyKeys.flatMap((key) => claudeInstalled.plugins[key] || []);
17604
+ for (const legacyKey of legacyKeys) {
17605
+ delete claudeInstalled.plugins[legacyKey];
17606
+ }
17439
17607
  await this.saveClaudeInstalledPlugins(claudeInstalled);
17440
17608
  const claudeSettings = await this.readClaudeSettings();
17441
17609
  claudeSettings.enabledPlugins = {
17442
17610
  ...claudeSettings.enabledPlugins || {},
17443
17611
  [nativeTarget.pluginKey]: true
17444
17612
  };
17613
+ claudeSettings.extraKnownMarketplaces = {
17614
+ ...claudeSettings.extraKnownMarketplaces || {},
17615
+ [nativeTarget.namespace]: {
17616
+ source: marketplace.source
17617
+ }
17618
+ };
17619
+ for (const legacyKey of legacyKeys) {
17620
+ if (claudeSettings.enabledPlugins && legacyKey in claudeSettings.enabledPlugins) {
17621
+ delete claudeSettings.enabledPlugins[legacyKey];
17622
+ }
17623
+ }
17445
17624
  await this.saveClaudeSettings(claudeSettings);
17625
+ const knownMarketplaces = await this.readClaudeKnownMarketplaces();
17626
+ knownMarketplaces[nativeTarget.namespace] = {
17627
+ source: marketplace.source,
17628
+ installLocation: nativeTarget.marketplacePath,
17629
+ lastUpdated: now
17630
+ };
17631
+ await this.saveClaudeKnownMarketplaces(knownMarketplaces);
17632
+ for (const entry of legacyEntries) {
17633
+ await fs22.rm(entry.installPath, { recursive: true, force: true }).catch(() => {
17634
+ });
17635
+ }
17636
+ for (const legacyKey of legacyKeys) {
17637
+ warnings.push(`Replaced legacy AgentInit Claude plugin install ${legacyKey} with ${nativeTarget.pluginKey}.`);
17638
+ }
17446
17639
  installed.push({
17447
17640
  agent: "claude",
17448
17641
  pluginKey: nativeTarget.pluginKey,
@@ -17463,6 +17656,7 @@ class PluginManager {
17463
17656
  const claudeInstalled = await this.readClaudeInstalledPlugins();
17464
17657
  const entries = claudeInstalled.plugins[component.pluginKey] || [];
17465
17658
  const remainingEntries = entries.filter((entry) => entry.installPath !== component.installPath);
17659
+ const marketplaceNamespace = component.pluginKey.includes("@") ? component.pluginKey.split("@").slice(1).join("@") : "";
17466
17660
  if (remainingEntries.length > 0) {
17467
17661
  claudeInstalled.plugins[component.pluginKey] = remainingEntries;
17468
17662
  } else {
@@ -17473,8 +17667,32 @@ class PluginManager {
17473
17667
  if (claudeSettings.enabledPlugins && component.pluginKey in claudeSettings.enabledPlugins) {
17474
17668
  const { [component.pluginKey]: _removed, ...remainingEnabledPlugins } = claudeSettings.enabledPlugins;
17475
17669
  claudeSettings.enabledPlugins = remainingEnabledPlugins;
17476
- await this.saveClaudeSettings(claudeSettings);
17477
17670
  }
17671
+ const namespaceStillInstalled = marketplaceNamespace ? Object.keys(claudeInstalled.plugins).some((pluginKey) => pluginKey.endsWith(`@${marketplaceNamespace}`)) : false;
17672
+ if (!namespaceStillInstalled && marketplaceNamespace) {
17673
+ if (claudeSettings.extraKnownMarketplaces && marketplaceNamespace in claudeSettings.extraKnownMarketplaces) {
17674
+ delete claudeSettings.extraKnownMarketplaces[marketplaceNamespace];
17675
+ }
17676
+ const knownMarketplaces = await this.readClaudeKnownMarketplaces();
17677
+ if (marketplaceNamespace in knownMarketplaces) {
17678
+ delete knownMarketplaces[marketplaceNamespace];
17679
+ await this.saveClaudeKnownMarketplaces(knownMarketplaces);
17680
+ }
17681
+ await fs22.rm(getClaudeMarketplaceInstallPath(marketplaceNamespace), { recursive: true, force: true }).catch(() => {
17682
+ });
17683
+ } else if (marketplaceNamespace) {
17684
+ const pluginName = component.pluginKey.split("@")[0] || "";
17685
+ const marketplacePath = getClaudeMarketplaceInstallPath(marketplaceNamespace);
17686
+ const manifest = await this.readClaudeMarketplaceManifest(marketplacePath);
17687
+ if (pluginName && manifest) {
17688
+ manifest.plugins = (manifest.plugins || []).filter((entry) => entry.name !== pluginName);
17689
+ const marketplacePluginPath = join4(marketplacePath, "plugins", pluginName);
17690
+ await fs22.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
17691
+ });
17692
+ await this.saveClaudeMarketplaceManifest(marketplacePath, manifest);
17693
+ }
17694
+ }
17695
+ await this.saveClaudeSettings(claudeSettings);
17478
17696
  await fs22.rm(component.installPath, { recursive: true, force: true }).catch(() => {
17479
17697
  });
17480
17698
  return true;
@@ -17915,8 +18133,11 @@ ${body.trim()}
17915
18133
  warnings: plugin.warnings
17916
18134
  };
17917
18135
  }
17918
- const skillResult = await this.installPluginSkills(plugin, projectPath, agents, options2);
17919
- const mcpResult = await this.applyPluginMcpServers(plugin, projectPath, agents, options2.global);
18136
+ const nativeClaudeTarget = await this.getClaudeNativeInstallTarget(plugin, plugin.resolvedPluginDir);
18137
+ const portableSkillAgents = nativeClaudeTarget ? this.getPortableSkillAgents(agents, projectPath, options2.global) : agents;
18138
+ const portableMcpAgents = nativeClaudeTarget ? agents.filter((agent) => agent.id !== "claude") : agents;
18139
+ const skillResult = await this.installPluginSkills(plugin, projectPath, portableSkillAgents, options2);
18140
+ const mcpResult = await this.applyPluginMcpServers(plugin, projectPath, portableMcpAgents, options2.global);
17920
18141
  const nativePluginResult = await this.installNativeClaudePlugin(plugin, plugin.resolvedPluginDir, agents);
17921
18142
  const installWarnings = [...plugin.warnings, ...nativePluginResult.warnings];
17922
18143
  if (skillResult.installed.length > 0 || mcpResult.applied.length > 0 || nativePluginResult.installed.length > 0) {
@@ -17981,6 +18202,25 @@ ${body.trim()}
17981
18202
  }
17982
18203
  return { installed, skipped };
17983
18204
  }
18205
+ getPortableSkillAgents(agents, projectPath, global3) {
18206
+ const claudeAgent = agents.find((agent) => agent.id === "claude");
18207
+ if (!claudeAgent || !claudeAgent.supportsSkills()) {
18208
+ return agents;
18209
+ }
18210
+ const claudeSkillsDir = claudeAgent.getSkillsDir(projectPath, global3);
18211
+ if (!claudeSkillsDir) {
18212
+ return agents.filter((agent) => agent.id !== "claude");
18213
+ }
18214
+ return agents.filter((agent) => {
18215
+ if (agent.id === "claude") {
18216
+ return false;
18217
+ }
18218
+ if (!agent.supportsSkills()) {
18219
+ return true;
18220
+ }
18221
+ return agent.getSkillsDir(projectPath, global3) !== claudeSkillsDir;
18222
+ });
18223
+ }
17984
18224
  async applyPluginMcpServers(plugin, projectPath, agents, global3) {
17985
18225
  const applied = [];
17986
18226
  const skipped = [];
@@ -18245,7 +18485,7 @@ ${body.trim()}
18245
18485
  return { removed: true, details };
18246
18486
  }
18247
18487
  }
18248
- var import_gray_matter, getMarketplaceCacheDir, getRegistryPath, getClaudeInstalledPluginsPath, getClaudeSettingsPath;
18488
+ var import_gray_matter, getMarketplaceCacheDir, getRegistryPath, getClaudeInstalledPluginsPath, getClaudeKnownMarketplacesPath, getClaudeMarketplaceInstallPath, getClaudeSettingsPath;
18249
18489
  var init_pluginManager = __esm(() => {
18250
18490
  import_gray_matter = __toESM(require_gray_matter(), 1);
18251
18491
  init_fs();
@@ -18266,13 +18506,19 @@ var init_pluginManager = __esm(() => {
18266
18506
  getClaudeInstalledPluginsPath = function() {
18267
18507
  return join4(homedir4(), ".claude", "plugins", "installed_plugins.json");
18268
18508
  };
18509
+ getClaudeKnownMarketplacesPath = function() {
18510
+ return join4(homedir4(), ".claude", "plugins", "known_marketplaces.json");
18511
+ };
18512
+ getClaudeMarketplaceInstallPath = function(namespace) {
18513
+ return join4(homedir4(), ".claude", "plugins", "marketplaces", namespace);
18514
+ };
18269
18515
  getClaudeSettingsPath = function() {
18270
18516
  return join4(homedir4(), ".claude", "settings.json");
18271
18517
  };
18272
18518
  });
18273
18519
 
18274
18520
  // dist/core/skillsManager.js
18275
- import {resolve as resolve8, join as join5, relative as relative3} from "path";
18521
+ import {resolve as resolve8, join as join5, relative as relative3, basename as basename3, dirname as dirname3} from "path";
18276
18522
  import {promises as fs24} from "fs";
18277
18523
  import {homedir as homedir5, tmpdir} from "os";
18278
18524
  import {execFile} from "child_process";
@@ -18287,15 +18533,9 @@ class SkillsManager {
18287
18533
  if (source.startsWith(".") || source.startsWith("/") || source.startsWith("~")) {
18288
18534
  return { type: "local", path: source };
18289
18535
  }
18290
- if (source.startsWith("https://github.com/") || source.startsWith("http://github.com/")) {
18291
- const url = source.replace(/\.git$/, "");
18292
- const match = url.match(/github\.com\/([^/]+)\/([^/]+)/);
18293
- return {
18294
- type: "github",
18295
- url: `https://github.com/${match?.[1]}/${match?.[2]}.git`,
18296
- owner: match?.[1],
18297
- repo: match?.[2]
18298
- };
18536
+ const githubUrlSource = this.parseGitHubHttpSource(source);
18537
+ if (githubUrlSource) {
18538
+ return githubUrlSource;
18299
18539
  }
18300
18540
  if (source.startsWith("git@") || source.endsWith(".git")) {
18301
18541
  return { type: "github", url: source };
@@ -18310,6 +18550,10 @@ class SkillsManager {
18310
18550
  pluginName: source
18311
18551
  };
18312
18552
  }
18553
+ const githubShorthandSource = this.parseGitHubShorthandSource(source);
18554
+ if (githubShorthandSource?.subpath) {
18555
+ return githubShorthandSource;
18556
+ }
18313
18557
  const marketplacePrefixMatch = source.match(/^([a-zA-Z0-9._-]+)\/(.+)$/);
18314
18558
  if (marketplacePrefixMatch) {
18315
18559
  const [, marketplaceId, pluginName] = marketplacePrefixMatch;
@@ -18321,14 +18565,8 @@ class SkillsManager {
18321
18565
  };
18322
18566
  }
18323
18567
  }
18324
- if (/^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/.test(source)) {
18325
- const [owner, repo] = source.split("/");
18326
- return {
18327
- type: "github",
18328
- url: `https://github.com/${owner}/${repo}.git`,
18329
- owner,
18330
- repo
18331
- };
18568
+ if (githubShorthandSource) {
18569
+ return githubShorthandSource;
18332
18570
  }
18333
18571
  return { type: "local", path: source };
18334
18572
  }
@@ -18367,6 +18605,54 @@ class SkillsManager {
18367
18605
  implicitSkills: []
18368
18606
  };
18369
18607
  }
18608
+ parseGitHubHttpSource(source) {
18609
+ if (!source.startsWith("https://github.com/") && !source.startsWith("http://github.com/")) {
18610
+ return null;
18611
+ }
18612
+ try {
18613
+ const parsedUrl = new URL(source);
18614
+ const segments = parsedUrl.pathname.replace(/\/+$/, "").split("/").filter(Boolean);
18615
+ if (segments.length < 2) {
18616
+ return null;
18617
+ }
18618
+ const [owner, rawRepo, ...rest] = segments;
18619
+ const repo = rawRepo?.replace(/\.git$/, "");
18620
+ if (!owner || !repo) {
18621
+ return null;
18622
+ }
18623
+ let subpath;
18624
+ if (rest[0] === "tree" || rest[0] === "blob") {
18625
+ if (rest.length > 2) {
18626
+ subpath = rest.slice(2).join("/");
18627
+ }
18628
+ } else if (rest.length > 0) {
18629
+ subpath = rest.join("/");
18630
+ }
18631
+ return {
18632
+ type: "github",
18633
+ url: `https://github.com/${owner}/${repo}.git`,
18634
+ owner,
18635
+ repo,
18636
+ ...subpath ? { subpath } : {}
18637
+ };
18638
+ } catch {
18639
+ return null;
18640
+ }
18641
+ }
18642
+ parseGitHubShorthandSource(source) {
18643
+ const githubShorthandMatch = source.match(/^([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)(?:\/(.+))?$/);
18644
+ if (!githubShorthandMatch) {
18645
+ return null;
18646
+ }
18647
+ const [, owner, repo, subpath] = githubShorthandMatch;
18648
+ return {
18649
+ type: "github",
18650
+ url: `https://github.com/${owner}/${repo}.git`,
18651
+ owner,
18652
+ repo,
18653
+ ...subpath ? { subpath } : {}
18654
+ };
18655
+ }
18370
18656
  async parseSkillMd(filePath) {
18371
18657
  const content = await readFileIfExists(filePath);
18372
18658
  if (!content)
@@ -18486,13 +18772,55 @@ class SkillsManager {
18486
18772
  warnings
18487
18773
  };
18488
18774
  }
18489
- async discoverFromSource(source, projectPath, options2 = {}) {
18775
+ async cleanupTempDir(tempDir) {
18776
+ if (!tempDir) {
18777
+ return;
18778
+ }
18779
+ await fs24.rm(tempDir, { recursive: true, force: true }).catch(() => {
18780
+ });
18781
+ }
18782
+ async resolveDiscoveryRoot(repoPath, source, sourceLabel) {
18783
+ const resolvedRepoPath = resolve8(repoPath);
18784
+ if (source.type !== "github" || !source.subpath) {
18785
+ return resolvedRepoPath;
18786
+ }
18787
+ const discoveryRoot = resolve8(resolvedRepoPath, source.subpath);
18788
+ if (!this.isWithinPath(resolvedRepoPath, discoveryRoot)) {
18789
+ throw new Error(`Invalid GitHub source path "${source.subpath}" in ${sourceLabel}`);
18790
+ }
18791
+ if (!await fileExists(discoveryRoot)) {
18792
+ throw new Error(`Source path not found in repository: ${sourceLabel}`);
18793
+ }
18794
+ const [realRepoPath, realDiscoveryRoot] = await Promise.all([
18795
+ resolveRealPathOrSelf(resolvedRepoPath),
18796
+ resolveRealPathOrSelf(discoveryRoot)
18797
+ ]);
18798
+ if (!this.isWithinPath(realRepoPath, realDiscoveryRoot)) {
18799
+ throw new Error(`Invalid GitHub source path "${source.subpath}" in ${sourceLabel}`);
18800
+ }
18801
+ if (await isDirectory(realDiscoveryRoot)) {
18802
+ return realDiscoveryRoot;
18803
+ }
18804
+ if (basename3(realDiscoveryRoot).toLowerCase() === "skill.md") {
18805
+ return dirname3(realDiscoveryRoot);
18806
+ }
18807
+ throw new Error(`GitHub source must reference a skill directory or SKILL.md: ${sourceLabel}`);
18808
+ }
18809
+ async loadDiscoveredSkillsContext(source, projectPath, options2 = {}) {
18490
18810
  const request = this.resolveSourceRequest(source, options2);
18491
18811
  const resolved = request.source;
18492
18812
  let tempDir = null;
18813
+ const cleanup = async () => {
18814
+ await this.cleanupTempDir(tempDir);
18815
+ tempDir = null;
18816
+ };
18493
18817
  try {
18494
18818
  if (resolved.type === "marketplace") {
18495
- return await this.discoverMarketplaceSkills(resolved, projectPath);
18819
+ const discovered = await this.discoverMarketplaceSkills(resolved, projectPath);
18820
+ return {
18821
+ ...discovered,
18822
+ cleanup
18823
+ };
18496
18824
  }
18497
18825
  let repoPath;
18498
18826
  if (resolved.type === "github") {
@@ -18507,10 +18835,11 @@ class SkillsManager {
18507
18835
  throw this.getMissingLocalPathError(source, repoPath);
18508
18836
  }
18509
18837
  }
18510
- let skills = await this.discoverSkills(repoPath);
18838
+ const discoveryRoot = await this.resolveDiscoveryRoot(repoPath, resolved, source);
18839
+ let skills = await this.discoverSkills(discoveryRoot);
18511
18840
  let pluginWarnings = [];
18512
18841
  if (skills.length === 0) {
18513
- const pluginBackedSkills = await this.discoverPortablePluginSkills(repoPath, source, resolved, projectPath);
18842
+ const pluginBackedSkills = await this.discoverPortablePluginSkills(discoveryRoot, source, resolved, projectPath);
18514
18843
  if (pluginBackedSkills) {
18515
18844
  skills = pluginBackedSkills.skills;
18516
18845
  pluginWarnings = pluginBackedSkills.warnings;
@@ -18524,12 +18853,21 @@ class SkillsManager {
18524
18853
  ...pluginWarnings,
18525
18854
  ...request.implicitSkills.length > 0 ? [`Resolved "${source}" from the default skills catalog ${DEFAULT_SKILLS_CATALOG.owner}/${DEFAULT_SKILLS_CATALOG.repo}. Use "./${source}" for a local path.`] : []
18526
18855
  ];
18527
- return { skills, warnings };
18856
+ return { skills, warnings, cleanup };
18857
+ } catch (error) {
18858
+ await cleanup();
18859
+ throw error;
18860
+ }
18861
+ }
18862
+ async discoverFromSource(source, projectPath, options2 = {}) {
18863
+ const context = await this.loadDiscoveredSkillsContext(source, projectPath, options2);
18864
+ try {
18865
+ return {
18866
+ skills: context.skills,
18867
+ warnings: context.warnings
18868
+ };
18528
18869
  } finally {
18529
- if (tempDir) {
18530
- await fs24.rm(tempDir, { recursive: true, force: true }).catch(() => {
18531
- });
18532
- }
18870
+ await context.cleanup();
18533
18871
  }
18534
18872
  }
18535
18873
  async getTargetAgents(projectPath, options2) {
@@ -18694,58 +19032,62 @@ class SkillsManager {
18694
19032
  await fs24.cp(src, dest, { recursive: true, dereference: true });
18695
19033
  }
18696
19034
  async addFromSource(source, projectPath, options2 = {}) {
18697
- const discovered = await this.discoverFromSource(source, projectPath, {
19035
+ const context = await this.loadDiscoveredSkillsContext(source, projectPath, {
18698
19036
  ...options2.from !== undefined ? { from: options2.from } : {}
18699
19037
  });
18700
- let skills = discovered.skills;
18701
- if (skills.length === 0) {
18702
- return { installed: [], skipped: [], warnings: discovered.warnings };
18703
- }
18704
- if (options2.skills && options2.skills.length > 0) {
18705
- const names = new Set(options2.skills.map((skill) => skill.toLowerCase()));
18706
- skills = skills.filter((skill) => names.has(skill.name.toLowerCase()));
18707
- }
18708
- const agents = await this.getTargetAgents(projectPath, options2);
18709
- if (agents.length === 0) {
18710
- return {
18711
- installed: [],
18712
- skipped: skills.map((skill) => ({ skill, reason: "No target agents found" })),
18713
- warnings: discovered.warnings
18714
- };
18715
- }
18716
- const result = { installed: [], skipped: [], warnings: discovered.warnings };
18717
- const installableAgents = [];
18718
- for (const agent of agents) {
18719
- if (!agent.supportsSkills()) {
18720
- for (const skill of skills) {
18721
- result.skipped.push({ skill, reason: `${agent.name} does not support skills` });
18722
- }
18723
- continue;
19038
+ try {
19039
+ let skills = context.skills;
19040
+ if (skills.length === 0) {
19041
+ return { installed: [], skipped: [], warnings: context.warnings };
18724
19042
  }
18725
- const skillsDir = agent.getSkillsDir(projectPath, options2.global);
18726
- if (!skillsDir) {
18727
- for (const skill of skills) {
18728
- result.skipped.push({ skill, reason: `No skills directory for ${agent.name}` });
19043
+ if (options2.skills && options2.skills.length > 0) {
19044
+ const names = new Set(options2.skills.map((skill) => skill.toLowerCase()));
19045
+ skills = skills.filter((skill) => names.has(skill.name.toLowerCase()));
19046
+ }
19047
+ const agents = await this.getTargetAgents(projectPath, options2);
19048
+ if (agents.length === 0) {
19049
+ return {
19050
+ installed: [],
19051
+ skipped: skills.map((skill) => ({ skill, reason: "No target agents found" })),
19052
+ warnings: context.warnings
19053
+ };
19054
+ }
19055
+ const result = { installed: [], skipped: [], warnings: context.warnings };
19056
+ const installableAgents = [];
19057
+ for (const agent of agents) {
19058
+ if (!agent.supportsSkills()) {
19059
+ for (const skill of skills) {
19060
+ result.skipped.push({ skill, reason: `${agent.name} does not support skills` });
19061
+ }
19062
+ continue;
18729
19063
  }
18730
- continue;
19064
+ const skillsDir = agent.getSkillsDir(projectPath, options2.global);
19065
+ if (!skillsDir) {
19066
+ for (const skill of skills) {
19067
+ result.skipped.push({ skill, reason: `No skills directory for ${agent.name}` });
19068
+ }
19069
+ continue;
19070
+ }
19071
+ installableAgents.push(agent);
18731
19072
  }
18732
- installableAgents.push(agent);
18733
- }
18734
- for (const skill of skills) {
18735
- for (const agent of installableAgents) {
18736
- try {
18737
- const installOptions = {
18738
- ...options2.global !== undefined ? { global: options2.global } : {},
18739
- ...options2.copy !== undefined ? { copy: options2.copy } : {}
18740
- };
18741
- const installed = skill.generatedContent ? await this.installSkillFromContentForAgent(skill.name, skill.generatedContent, agent, projectPath, installOptions) : await this.installSkillForAgent(skill.path, skill.name, agent, projectPath, installOptions);
18742
- result.installed.push({ skill, agent: agent.id, ...installed });
18743
- } catch (error) {
18744
- result.skipped.push({ skill, reason: error.message });
19073
+ for (const skill of skills) {
19074
+ for (const agent of installableAgents) {
19075
+ try {
19076
+ const installOptions = {
19077
+ ...options2.global !== undefined ? { global: options2.global } : {},
19078
+ ...options2.copy !== undefined ? { copy: options2.copy } : {}
19079
+ };
19080
+ const installed = skill.generatedContent ? await this.installSkillFromContentForAgent(skill.name, skill.generatedContent, agent, projectPath, installOptions) : await this.installSkillForAgent(skill.path, skill.name, agent, projectPath, installOptions);
19081
+ result.installed.push({ skill, agent: agent.id, ...installed });
19082
+ } catch (error) {
19083
+ result.skipped.push({ skill, reason: error.message });
19084
+ }
18745
19085
  }
18746
19086
  }
19087
+ return result;
19088
+ } finally {
19089
+ await context.cleanup();
18747
19090
  }
18748
- return result;
18749
19091
  }
18750
19092
  async listInstalled(projectPath, options2 = {}) {
18751
19093
  const agents = await this.getTargetAgents(projectPath, options2);
@@ -30705,7 +31047,7 @@ ${content}
30705
31047
  // dist/core/managedState.js
30706
31048
  init_fs();
30707
31049
  import {promises as fs28} from "fs";
30708
- import {dirname as dirname3, join as join6, relative as relative4, resolve as resolve10} from "path";
31050
+ import {dirname as dirname4, join as join6, relative as relative4, resolve as resolve10} from "path";
30709
31051
  var toPosixPath = function(value) {
30710
31052
  return value.replace(/\\/g, "/");
30711
31053
  };
@@ -30730,10 +31072,10 @@ async function copyDirectory(src, dest) {
30730
31072
  await copyDirectory(srcPath, destPath);
30731
31073
  } else if (entry.isSymbolicLink()) {
30732
31074
  const target = await fs28.readlink(srcPath);
30733
- await fs28.mkdir(dirname3(destPath), { recursive: true });
31075
+ await fs28.mkdir(dirname4(destPath), { recursive: true });
30734
31076
  await fs28.symlink(target, destPath);
30735
31077
  } else {
30736
- await fs28.mkdir(dirname3(destPath), { recursive: true });
31078
+ await fs28.mkdir(dirname4(destPath), { recursive: true });
30737
31079
  await fs28.copyFile(srcPath, destPath);
30738
31080
  }
30739
31081
  }
@@ -30743,7 +31085,7 @@ async function copyPath(src, dest) {
30743
31085
  if (!type2) {
30744
31086
  return;
30745
31087
  }
30746
- await fs28.mkdir(dirname3(dest), { recursive: true });
31088
+ await fs28.mkdir(dirname4(dest), { recursive: true });
30747
31089
  if (type2 === "symlink") {
30748
31090
  const target = await fs28.readlink(src);
30749
31091
  await fs28.symlink(target, dest);
@@ -30879,7 +31221,7 @@ class ManagedStateStore {
30879
31221
  if (!options2.dryRun) {
30880
31222
  await fs28.rm(absolutePath, { recursive: true, force: true }).catch(() => {
30881
31223
  });
30882
- await fs28.mkdir(dirname3(absolutePath), { recursive: true });
31224
+ await fs28.mkdir(dirname4(absolutePath), { recursive: true });
30883
31225
  await fs28.symlink(backupLinkTarget, absolutePath);
30884
31226
  }
30885
31227
  summary.restored++;
@@ -31261,11 +31603,11 @@ class MCPParser {
31261
31603
  // dist/constants/mcp.js
31262
31604
  import {readFileSync as readFileSync2} from "fs";
31263
31605
  import {fileURLToPath} from "url";
31264
- import {dirname as dirname4, join as join7} from "path";
31606
+ import {dirname as dirname5, join as join7} from "path";
31265
31607
  var getPackageVersion = function() {
31266
31608
  try {
31267
31609
  const __filename2 = fileURLToPath(import.meta.url);
31268
- const __dirname2 = dirname4(__filename2);
31610
+ const __dirname2 = dirname5(__filename2);
31269
31611
  const packageJsonPath = join7(__dirname2, "../../package.json");
31270
31612
  const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
31271
31613
  return packageJson.version || "1.0.0";
@@ -31297,11 +31639,11 @@ import {readFileSync as readFileSync4} from "fs";
31297
31639
 
31298
31640
  // dist/core/rulesTemplateLoader.js
31299
31641
  var toml = __toESM(require_toml(), 1);
31300
- import {resolve as resolve11, dirname as dirname5} from "path";
31642
+ import {resolve as resolve11, dirname as dirname6} from "path";
31301
31643
  import {fileURLToPath as fileURLToPath2} from "url";
31302
31644
  import {readFileSync as readFileSync3, readdirSync, existsSync as existsSync2} from "fs";
31303
31645
  var __filename2 = fileURLToPath2(import.meta.url);
31304
- var __dirname2 = dirname5(__filename2);
31646
+ var __dirname2 = dirname6(__filename2);
31305
31647
 
31306
31648
  class RulesTemplateLoader {
31307
31649
  templatesPath;
@@ -39537,7 +39879,7 @@ class MCPVerifier {
39537
39879
 
39538
39880
  // dist/core/gitignoreManager.js
39539
39881
  import {promises as fs31} from "fs";
39540
- import {dirname as dirname6, join as join8} from "path";
39882
+ import {dirname as dirname7, join as join8} from "path";
39541
39883
  var normalizeIgnorePath = function(projectPath, value) {
39542
39884
  const relative6 = value.startsWith(projectPath) ? value.slice(projectPath.length + 1) : value;
39543
39885
  const normalized = relative6.replace(/\\/g, "/").replace(/^\/+/, "");
@@ -39605,7 +39947,7 @@ async function updateManagedIgnoreFile(projectPath, paths5, options2 = {}) {
39605
39947
  existingContent = "";
39606
39948
  }
39607
39949
  const updatedContent = updateManagedBlock(existingContent, normalizedPaths);
39608
- await fs31.mkdir(dirname6(ignoreFilePath), { recursive: true });
39950
+ await fs31.mkdir(dirname7(ignoreFilePath), { recursive: true });
39609
39951
  await fs31.writeFile(ignoreFilePath, updatedContent, "utf8");
39610
39952
  return ignoreFilePath;
39611
39953
  }
@@ -39647,7 +39989,7 @@ var END_MARKER = "# END AgentInit Generated Files";
39647
39989
  init_agentManager();
39648
39990
  init_skillsManager();
39649
39991
  init_fs();
39650
- import {dirname as dirname7, join as join9} from "path";
39992
+ import {dirname as dirname8, join as join9} from "path";
39651
39993
  async function discoverProjectSkills(projectPath, skillsManager4) {
39652
39994
  const sources = [];
39653
39995
  const skills = new Map;
@@ -39725,7 +40067,7 @@ async function applyProjectSkills(projectPath, targetAgentIds, managedState2, op
39725
40067
  await managedState2.trackGeneratedPath(generatedPath, {
39726
40068
  kind: "directory",
39727
40069
  source: "skills",
39728
- ignorePath: `${dirname7(generatedPath)}/`
40070
+ ignorePath: `${dirname8(generatedPath)}/`
39729
40071
  });
39730
40072
  }
39731
40073
  }
@@ -41982,7 +42324,7 @@ function registerRulesCommand(program2) {
41982
42324
  // dist/commands/plugins.js
41983
42325
  var import_prompts3 = __toESM(require_prompts3(), 1);
41984
42326
  import {homedir as homedir7} from "os";
41985
- import {dirname as dirname8, relative as relative8, resolve as resolve13} from "path";
42327
+ import {dirname as dirname9, relative as relative8, resolve as resolve13} from "path";
41986
42328
  init_pluginManager();
41987
42329
  init_agentManager();
41988
42330
  init_marketplaceRegistry();
@@ -42370,7 +42712,7 @@ var renderPluginWarnings = function(previewOrResult, projectPath) {
42370
42712
  var renderInstalledComponents = function(result, agentManager12, projectPath) {
42371
42713
  const skillGroups = new Map;
42372
42714
  for (const item of result.skills.installed) {
42373
- const targetDir = dirname8(item.path);
42715
+ const targetDir = dirname9(item.path);
42374
42716
  const existing = skillGroups.get(targetDir) || { agents: new Set, skillNames: new Set };
42375
42717
  existing.agents.add(item.agent);
42376
42718
  existing.skillNames.add(item.name);