agentinit 1.20.0 → 1.20.1

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
@@ -17098,6 +17098,174 @@ var init_marketplaceRegistry = __esm(() => {
17098
17098
  CUSTOM_MARKETPLACE_CACHE_TTL_MS = 3600000;
17099
17099
  });
17100
17100
 
17101
+ // dist/core/skillSecurityScanner.js
17102
+ import {promises as fs22} from "fs";
17103
+ import {join as join4, relative as relative2} from "path";
17104
+
17105
+ class SkillSecurityScanner {
17106
+ async scanSkill(skill) {
17107
+ const findings = skill.generatedContent ? this.scanText(skill.generatedContent, "SKILL.md") : await this.scanDirectory(skill.path);
17108
+ const stats = findings.reduce((acc, finding) => {
17109
+ acc[finding.severity] += 1;
17110
+ return acc;
17111
+ }, { high: 0, medium: 0, low: 0 });
17112
+ return {
17113
+ blocked: findings.some((finding) => finding.blocking),
17114
+ findings,
17115
+ stats
17116
+ };
17117
+ }
17118
+ formatShortSummary(result) {
17119
+ const parts = [
17120
+ result.stats.high ? `${result.stats.high} high` : null,
17121
+ result.stats.medium ? `${result.stats.medium} medium` : null,
17122
+ result.stats.low ? `${result.stats.low} low` : null
17123
+ ].filter((value) => !!value);
17124
+ return parts.length > 0 ? parts.join(", ") : "no findings";
17125
+ }
17126
+ formatBlockingReason(result) {
17127
+ const finding = result.findings.find((entry) => entry.blocking) || result.findings.find((entry) => entry.severity === "high") || result.findings[0];
17128
+ if (!finding) {
17129
+ return "Security scan failed";
17130
+ }
17131
+ return `Security scan failed: ${finding.title} (${finding.ruleId}) at ${finding.filePath}:${finding.line}`;
17132
+ }
17133
+ async scanDirectory(rootPath) {
17134
+ const findings = [];
17135
+ await this.walk(rootPath, rootPath, findings);
17136
+ return findings;
17137
+ }
17138
+ async walk(rootPath, currentPath, findings) {
17139
+ const entries = await fs22.readdir(currentPath, { withFileTypes: true });
17140
+ for (const entry of entries) {
17141
+ if (entry.name === ".git" || entry.name === "node_modules") {
17142
+ continue;
17143
+ }
17144
+ const entryPath = join4(currentPath, entry.name);
17145
+ if (entry.isDirectory()) {
17146
+ await this.walk(rootPath, entryPath, findings);
17147
+ continue;
17148
+ }
17149
+ const relativePath = relative2(rootPath, entryPath).replace(/\\/g, "/");
17150
+ const content = await this.readTextFile(entryPath);
17151
+ if (content === null) {
17152
+ continue;
17153
+ }
17154
+ findings.push(...this.scanText(content, relativePath, {
17155
+ blockHighRisk: this.isExecutableTextFile(entryPath, content)
17156
+ }));
17157
+ }
17158
+ }
17159
+ async readTextFile(filePath) {
17160
+ const extension = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")).toLowerCase() : "";
17161
+ if (!SCANNED_TEXT_EXTENSIONS.has(extension)) {
17162
+ return null;
17163
+ }
17164
+ const buffer = await fs22.readFile(filePath);
17165
+ if (buffer.length > MAX_TEXT_FILE_BYTES || buffer.includes(0)) {
17166
+ return null;
17167
+ }
17168
+ return buffer.toString("utf8");
17169
+ }
17170
+ isExecutableTextFile(filePath, content) {
17171
+ const extension = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")).toLowerCase() : "";
17172
+ return EXECUTABLE_TEXT_EXTENSIONS.has(extension) || content.startsWith("#!");
17173
+ }
17174
+ scanText(content, filePath, options2 = {}) {
17175
+ const findings = [];
17176
+ const lines = content.split(/\r?\n/);
17177
+ lines.forEach((line, index) => {
17178
+ for (const rule of SCAN_RULES) {
17179
+ rule.regex.lastIndex = 0;
17180
+ if (!rule.regex.test(line)) {
17181
+ continue;
17182
+ }
17183
+ findings.push({
17184
+ ruleId: rule.id,
17185
+ title: rule.title,
17186
+ severity: rule.severity,
17187
+ filePath,
17188
+ line: index + 1,
17189
+ snippet: line.trim().slice(0, 160),
17190
+ blocking: rule.severity === "high" && options2.blockHighRisk === true
17191
+ });
17192
+ }
17193
+ });
17194
+ return findings;
17195
+ }
17196
+ }
17197
+ var SCAN_RULES, MAX_TEXT_FILE_BYTES, SCANNED_TEXT_EXTENSIONS, EXECUTABLE_TEXT_EXTENSIONS;
17198
+ var init_skillSecurityScanner = __esm(() => {
17199
+ SCAN_RULES = [
17200
+ {
17201
+ id: "AI001",
17202
+ title: "Prompt override language",
17203
+ severity: "medium",
17204
+ regex: /\b(ignore|disregard|override|bypass)\b.{0,40}\b(previous|prior|system|safety|guardrails?|instructions?)\b/i
17205
+ },
17206
+ {
17207
+ id: "AI002",
17208
+ title: "Secret or credential exfiltration",
17209
+ severity: "high",
17210
+ regex: /\b(exfiltrat\w*|upload|send|post|curl|wget)\b.{0,80}\b(secret|token|key|credential|cookie|session|\.ssh|id_rsa|env(?:ironment)? variables?)\b/i
17211
+ },
17212
+ {
17213
+ id: "AI003",
17214
+ title: "Destructive shell command",
17215
+ severity: "high",
17216
+ regex: /\b(rm\s+-rf\s+\/|sudo\s+rm\s+-rf|mkfs\b|dd\s+if=\/dev\/zero|chmod\s+-R\s+777\s+\/)\b/i
17217
+ },
17218
+ {
17219
+ id: "AI004",
17220
+ title: "Remote shell execution pipeline",
17221
+ severity: "high",
17222
+ regex: /\b(curl|wget)\b[^\n|]{0,160}\|\s*(sh|bash|zsh)\b/i
17223
+ },
17224
+ {
17225
+ id: "AI005",
17226
+ title: "Hardcoded credential material",
17227
+ severity: "low",
17228
+ regex: /\b(api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{8,}['"]/i
17229
+ },
17230
+ {
17231
+ id: "AI006",
17232
+ title: "Unicode bidi control characters",
17233
+ severity: "low",
17234
+ regex: /[\u202A-\u202E\u2066-\u2069]/
17235
+ }
17236
+ ];
17237
+ MAX_TEXT_FILE_BYTES = 1024 * 1024;
17238
+ SCANNED_TEXT_EXTENSIONS = new Set([
17239
+ "",
17240
+ ".md",
17241
+ ".txt",
17242
+ ".json",
17243
+ ".yaml",
17244
+ ".yml",
17245
+ ".toml",
17246
+ ".js",
17247
+ ".ts",
17248
+ ".mjs",
17249
+ ".cjs",
17250
+ ".sh",
17251
+ ".bash",
17252
+ ".zsh",
17253
+ ".py",
17254
+ ".ps1"
17255
+ ]);
17256
+ EXECUTABLE_TEXT_EXTENSIONS = new Set([
17257
+ ".js",
17258
+ ".ts",
17259
+ ".mjs",
17260
+ ".cjs",
17261
+ ".sh",
17262
+ ".bash",
17263
+ ".zsh",
17264
+ ".py",
17265
+ ".ps1"
17266
+ ]);
17267
+ });
17268
+
17101
17269
  // dist/types/skills.js
17102
17270
  var SHARED_SKILLS_TARGET_ID, SHARED_SKILLS_TARGET_NAME;
17103
17271
  var init_skills = __esm(() => {
@@ -17106,9 +17274,9 @@ var init_skills = __esm(() => {
17106
17274
  });
17107
17275
 
17108
17276
  // dist/core/installLock.js
17109
- import {promises as fs22} from "fs";
17277
+ import {promises as fs23} from "fs";
17110
17278
  import {homedir as homedir4} from "os";
17111
- import {dirname as dirname2, join as join4, resolve as resolve7} from "path";
17279
+ import {dirname as dirname2, join as join5, resolve as resolve7} from "path";
17112
17280
  import {createHash, randomUUID} from "crypto";
17113
17281
  function getLockEntryTargetLabel(entry) {
17114
17282
  return entry.scope === "global" ? "Global scope" : entry.projectPath;
@@ -17122,16 +17290,16 @@ async function hashDirectory(rootPath) {
17122
17290
  return null;
17123
17291
  const hash = createHash("sha256");
17124
17292
  const walk = async (currentPath, relativePath) => {
17125
- const stat = await fs22.stat(currentPath);
17293
+ const stat = await fs23.stat(currentPath);
17126
17294
  if (stat.isDirectory()) {
17127
17295
  hash.update(`dir:${relativePath}\n`);
17128
- const entries = await fs22.readdir(currentPath);
17296
+ const entries = await fs23.readdir(currentPath);
17129
17297
  entries.sort();
17130
17298
  for (const entry of entries) {
17131
- await walk(join4(currentPath, entry), `${relativePath}/${entry}`);
17299
+ await walk(join5(currentPath, entry), `${relativePath}/${entry}`);
17132
17300
  }
17133
17301
  } else {
17134
- const content = await fs22.readFile(currentPath);
17302
+ const content = await fs23.readFile(currentPath);
17135
17303
  hash.update(`file:${relativePath}\n`);
17136
17304
  hash.update(content);
17137
17305
  hash.update("\n");
@@ -17167,12 +17335,12 @@ class InstallLock {
17167
17335
  if (!this.state)
17168
17336
  return;
17169
17337
  const lockPath = getLockPath();
17170
- await fs22.mkdir(dirname2(lockPath), { recursive: true });
17171
- await fs22.writeFile(lockPath, JSON.stringify(this.state, null, 2) + "\n", {
17338
+ await fs23.mkdir(dirname2(lockPath), { recursive: true });
17339
+ await fs23.writeFile(lockPath, JSON.stringify(this.state, null, 2) + "\n", {
17172
17340
  encoding: "utf8",
17173
17341
  mode: 384
17174
17342
  });
17175
- await fs22.chmod(lockPath, 384).catch(() => {
17343
+ await fs23.chmod(lockPath, 384).catch(() => {
17176
17344
  });
17177
17345
  }
17178
17346
  async recordSkill(params) {
@@ -17351,7 +17519,7 @@ var init_installLock = __esm(() => {
17351
17519
  init_fs();
17352
17520
  init_logger();
17353
17521
  getLockPath = function() {
17354
- return join4(homedir4(), ".agentinit", LOCK_FILE);
17522
+ return join5(homedir4(), ".agentinit", LOCK_FILE);
17355
17523
  };
17356
17524
  getEntryTargetKey = function(entry) {
17357
17525
  return entry.scope === "global" ? GLOBAL_TARGET_KEY : entry.projectPath;
@@ -17493,8 +17661,8 @@ __export(exports_pluginManager, {
17493
17661
  }
17494
17662
  }
17495
17663
  });
17496
- import {resolve as resolve8, join as join5, basename as basename2, relative as relative2, dirname as dirname3} from "path";
17497
- import {promises as fs24} from "fs";
17664
+ import {resolve as resolve8, join as join6, basename as basename2, relative as relative3, dirname as dirname3} from "path";
17665
+ import {promises as fs25} from "fs";
17498
17666
  import {homedir as homedir5} from "os";
17499
17667
 
17500
17668
  class MultipleBundlePluginsError extends Error {
@@ -17539,7 +17707,7 @@ class PluginManager {
17539
17707
  }
17540
17708
  async cleanupLoadedPluginContext(context) {
17541
17709
  if (context?.tempDir) {
17542
- await fs24.rm(context.tempDir, { recursive: true, force: true }).catch(() => {
17710
+ await fs25.rm(context.tempDir, { recursive: true, force: true }).catch(() => {
17543
17711
  });
17544
17712
  }
17545
17713
  }
@@ -17617,7 +17785,7 @@ class PluginManager {
17617
17785
  return "unverified";
17618
17786
  }
17619
17787
  async resolvePreparedPluginDir(pluginDir, source) {
17620
- const claudeMarketplaceManifestPath = join5(pluginDir, ".claude-plugin", "marketplace.json");
17788
+ const claudeMarketplaceManifestPath = join6(pluginDir, ".claude-plugin", "marketplace.json");
17621
17789
  if (!await fileExists(claudeMarketplaceManifestPath)) {
17622
17790
  return { pluginDir, warnings: [] };
17623
17791
  }
@@ -17653,7 +17821,7 @@ class PluginManager {
17653
17821
  selectedEntry = matched;
17654
17822
  }
17655
17823
  const selectedPluginDir = resolve8(pluginDir, selectedEntry.source);
17656
- const relativePath = relative2(resolve8(pluginDir), selectedPluginDir);
17824
+ const relativePath = relative3(resolve8(pluginDir), selectedPluginDir);
17657
17825
  if (relativePath.startsWith("..") || relativePath.includes("/../") || relativePath.includes("\\..\\")) {
17658
17826
  throw new Error(`Invalid bundled plugin source path "${selectedEntry.source}" in ${claudeMarketplaceManifestPath}`);
17659
17827
  }
@@ -17765,15 +17933,15 @@ class PluginManager {
17765
17933
  }
17766
17934
  async getClaudeNativeFeatureKinds(pluginDir, manifest) {
17767
17935
  const featureChecks = await Promise.all([
17768
- (async () => !!manifest.commands || await isDirectory(join5(pluginDir, "commands")))(),
17769
- (async () => !!manifest.hooks || await isDirectory(join5(pluginDir, "hooks")))(),
17770
- (async () => !!manifest.agents || await isDirectory(join5(pluginDir, "agents")))(),
17771
- isDirectory(join5(pluginDir, "skills")),
17772
- (async () => !!manifest.mcpServers || await fileExists(join5(pluginDir, ".mcp.json")))(),
17773
- isDirectory(join5(pluginDir, "prompts")),
17774
- isDirectory(join5(pluginDir, "schemas")),
17775
- isDirectory(join5(pluginDir, "scripts")),
17776
- isDirectory(join5(pluginDir, "templates"))
17936
+ (async () => !!manifest.commands || await isDirectory(join6(pluginDir, "commands")))(),
17937
+ (async () => !!manifest.hooks || await isDirectory(join6(pluginDir, "hooks")))(),
17938
+ (async () => !!manifest.agents || await isDirectory(join6(pluginDir, "agents")))(),
17939
+ isDirectory(join6(pluginDir, "skills")),
17940
+ (async () => !!manifest.mcpServers || await fileExists(join6(pluginDir, ".mcp.json")))(),
17941
+ isDirectory(join6(pluginDir, "prompts")),
17942
+ isDirectory(join6(pluginDir, "schemas")),
17943
+ isDirectory(join6(pluginDir, "scripts")),
17944
+ isDirectory(join6(pluginDir, "templates"))
17777
17945
  ]);
17778
17946
  return [
17779
17947
  ...featureChecks[0] ? ["commands"] : [],
@@ -17791,7 +17959,7 @@ class PluginManager {
17791
17959
  if (plugin.format !== "claude") {
17792
17960
  return null;
17793
17961
  }
17794
- const manifestContent = await readFileIfExists(join5(pluginDir, ".claude-plugin", "plugin.json"));
17962
+ const manifestContent = await readFileIfExists(join6(pluginDir, ".claude-plugin", "plugin.json"));
17795
17963
  if (!manifestContent) {
17796
17964
  return null;
17797
17965
  }
@@ -17811,7 +17979,7 @@ class PluginManager {
17811
17979
  return {
17812
17980
  namespace,
17813
17981
  pluginKey: `${plugin.name}@${namespace}`,
17814
- installPath: join5(homedir5(), ".claude", "plugins", "cache", namespace, plugin.name, versionDir),
17982
+ installPath: join6(homedir5(), ".claude", "plugins", "cache", namespace, plugin.name, versionDir),
17815
17983
  marketplacePath: getClaudeMarketplaceInstallPath(namespace),
17816
17984
  features
17817
17985
  };
@@ -17877,7 +18045,7 @@ class PluginManager {
17877
18045
  async findClaudeMarketplaceRoot(pluginDir) {
17878
18046
  let currentDir = resolve8(pluginDir);
17879
18047
  while (true) {
17880
- if (await fileExists(join5(currentDir, ".claude-plugin", "marketplace.json"))) {
18048
+ if (await fileExists(join6(currentDir, ".claude-plugin", "marketplace.json"))) {
17881
18049
  return currentDir;
17882
18050
  }
17883
18051
  const parentDir = dirname3(currentDir);
@@ -17888,7 +18056,7 @@ class PluginManager {
17888
18056
  }
17889
18057
  }
17890
18058
  async readClaudeMarketplaceManifest(marketplaceDir) {
17891
- const manifestContent = await readFileIfExists(join5(marketplaceDir, ".claude-plugin", "marketplace.json"));
18059
+ const manifestContent = await readFileIfExists(join6(marketplaceDir, ".claude-plugin", "marketplace.json"));
17892
18060
  if (!manifestContent) {
17893
18061
  return null;
17894
18062
  }
@@ -17907,19 +18075,19 @@ class PluginManager {
17907
18075
  }
17908
18076
  }
17909
18077
  async saveClaudeMarketplaceManifest(marketplaceDir, manifest) {
17910
- await fs24.mkdir(join5(marketplaceDir, ".claude-plugin"), { recursive: true });
17911
- await writeFile(join5(marketplaceDir, ".claude-plugin", "marketplace.json"), JSON.stringify(manifest, null, 2));
18078
+ await fs25.mkdir(join6(marketplaceDir, ".claude-plugin"), { recursive: true });
18079
+ await writeFile(join6(marketplaceDir, ".claude-plugin", "marketplace.json"), JSON.stringify(manifest, null, 2));
17912
18080
  }
17913
18081
  resolveMarketplaceSourcePath(baseDir, relativePath) {
17914
18082
  const resolvedPath = resolve8(baseDir, relativePath);
17915
- const relativePathFromBase = relative2(resolve8(baseDir), resolvedPath);
18083
+ const relativePathFromBase = relative3(resolve8(baseDir), resolvedPath);
17916
18084
  if (relativePathFromBase.startsWith("..") || relativePathFromBase.includes("/../") || relativePathFromBase.includes("\\..\\")) {
17917
18085
  throw new Error(`Invalid marketplace source path "${relativePath}" in ${baseDir}`);
17918
18086
  }
17919
18087
  return resolvedPath;
17920
18088
  }
17921
18089
  async readClaudePluginMetadata(pluginDir) {
17922
- const manifestContent = await readFileIfExists(join5(pluginDir, ".claude-plugin", "plugin.json"));
18090
+ const manifestContent = await readFileIfExists(join6(pluginDir, ".claude-plugin", "plugin.json"));
17923
18091
  if (!manifestContent) {
17924
18092
  return {};
17925
18093
  }
@@ -17935,11 +18103,11 @@ class PluginManager {
17935
18103
  }
17936
18104
  }
17937
18105
  async copyClaudeMarketplacePlugin(sourcePluginDir, marketplacePath, pluginName) {
17938
- const marketplacePluginPath = join5(marketplacePath, "plugins", pluginName);
17939
- await fs24.mkdir(dirname3(marketplacePluginPath), { recursive: true });
17940
- await fs24.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
18106
+ const marketplacePluginPath = join6(marketplacePath, "plugins", pluginName);
18107
+ await fs25.mkdir(dirname3(marketplacePluginPath), { recursive: true });
18108
+ await fs25.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
17941
18109
  });
17942
- await fs24.cp(sourcePluginDir, marketplacePluginPath, { recursive: true, dereference: true });
18110
+ await fs25.cp(sourcePluginDir, marketplacePluginPath, { recursive: true, dereference: true });
17943
18111
  return marketplacePluginPath;
17944
18112
  }
17945
18113
  getClaudeMarketplaceSource(plugin, marketplaceRoot, marketplacePath) {
@@ -17971,7 +18139,7 @@ class PluginManager {
17971
18139
  async materializeClaudeMarketplace(plugin, pluginDir, target) {
17972
18140
  const marketplaceRoot = await this.findClaudeMarketplaceRoot(pluginDir);
17973
18141
  const marketplaceSource = this.getClaudeMarketplaceSource(plugin, marketplaceRoot, target.marketplacePath);
17974
- await fs24.mkdir(target.marketplacePath, { recursive: true });
18142
+ await fs25.mkdir(target.marketplacePath, { recursive: true });
17975
18143
  const existingManifest = await this.readClaudeMarketplaceManifest(target.marketplacePath);
17976
18144
  const sourceManifest = marketplaceRoot ? await this.readClaudeMarketplaceManifest(marketplaceRoot) : null;
17977
18145
  const mergedManifest = {
@@ -18043,10 +18211,10 @@ class PluginManager {
18043
18211
  warnings.push(`Skipped native Claude plugin install because Claude already has "${plugin.name}" installed as ${conflictingKey}.`);
18044
18212
  return { installed, skipped, warnings };
18045
18213
  }
18046
- await fs24.rm(nativeTarget.installPath, { recursive: true, force: true }).catch(() => {
18214
+ await fs25.rm(nativeTarget.installPath, { recursive: true, force: true }).catch(() => {
18047
18215
  });
18048
- await fs24.mkdir(dirname3(nativeTarget.installPath), { recursive: true });
18049
- await fs24.cp(pluginDir, nativeTarget.installPath, { recursive: true, dereference: true });
18216
+ await fs25.mkdir(dirname3(nativeTarget.installPath), { recursive: true });
18217
+ await fs25.cp(pluginDir, nativeTarget.installPath, { recursive: true, dereference: true });
18050
18218
  const marketplace = await this.materializeClaudeMarketplace(plugin, pluginDir, nativeTarget);
18051
18219
  const now = new Date().toISOString();
18052
18220
  claudeInstalled.plugins[nativeTarget.pluginKey] = [{
@@ -18086,7 +18254,7 @@ class PluginManager {
18086
18254
  };
18087
18255
  await this.saveClaudeKnownMarketplaces(knownMarketplaces);
18088
18256
  for (const entry of legacyEntries) {
18089
- await fs24.rm(entry.installPath, { recursive: true, force: true }).catch(() => {
18257
+ await fs25.rm(entry.installPath, { recursive: true, force: true }).catch(() => {
18090
18258
  });
18091
18259
  }
18092
18260
  for (const legacyKey of legacyKeys) {
@@ -18134,7 +18302,7 @@ class PluginManager {
18134
18302
  delete knownMarketplaces[marketplaceNamespace];
18135
18303
  await this.saveClaudeKnownMarketplaces(knownMarketplaces);
18136
18304
  }
18137
- await fs24.rm(getClaudeMarketplaceInstallPath(marketplaceNamespace), { recursive: true, force: true }).catch(() => {
18305
+ await fs25.rm(getClaudeMarketplaceInstallPath(marketplaceNamespace), { recursive: true, force: true }).catch(() => {
18138
18306
  });
18139
18307
  } else if (marketplaceNamespace) {
18140
18308
  const pluginName = component.pluginKey.split("@")[0] || "";
@@ -18142,14 +18310,14 @@ class PluginManager {
18142
18310
  const manifest = await this.readClaudeMarketplaceManifest(marketplacePath);
18143
18311
  if (pluginName && manifest) {
18144
18312
  manifest.plugins = (manifest.plugins || []).filter((entry) => entry.name !== pluginName);
18145
- const marketplacePluginPath = join5(marketplacePath, "plugins", pluginName);
18146
- await fs24.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
18313
+ const marketplacePluginPath = join6(marketplacePath, "plugins", pluginName);
18314
+ await fs25.rm(marketplacePluginPath, { recursive: true, force: true }).catch(() => {
18147
18315
  });
18148
18316
  await this.saveClaudeMarketplaceManifest(marketplacePath, manifest);
18149
18317
  }
18150
18318
  }
18151
18319
  await this.saveClaudeSettings(claudeSettings);
18152
- await fs24.rm(component.installPath, { recursive: true, force: true }).catch(() => {
18320
+ await fs25.rm(component.installPath, { recursive: true, force: true }).catch(() => {
18153
18321
  });
18154
18322
  return true;
18155
18323
  }
@@ -18222,7 +18390,7 @@ class PluginManager {
18222
18390
  throw new Error(`Unknown marketplace: ${registryId}. Available: ${this.getMarketplaceIds().join(", ")}`);
18223
18391
  }
18224
18392
  const cacheDir = getMarketplaceCacheDir(registryId);
18225
- const cacheMetaPath = join5(cacheDir, ".agentinit-cache-meta.json");
18393
+ const cacheMetaPath = join6(cacheDir, ".agentinit-cache-meta.json");
18226
18394
  const cacheMeta = await this.readMarketplaceCacheMeta(cacheMetaPath);
18227
18395
  if (cacheMeta?.repoUrl === registry.repoUrl) {
18228
18396
  const age = Date.now() - cacheMeta.fetchedAt;
@@ -18230,10 +18398,10 @@ class PluginManager {
18230
18398
  return cacheDir;
18231
18399
  }
18232
18400
  }
18233
- if (await fileExists(join5(cacheDir, ".git"))) {
18401
+ if (await fileExists(join6(cacheDir, ".git"))) {
18234
18402
  const originUrl = await this.getMarketplaceCacheOriginUrl(cacheDir);
18235
18403
  if (originUrl !== registry.repoUrl) {
18236
- await fs24.rm(cacheDir, { recursive: true, force: true });
18404
+ await fs25.rm(cacheDir, { recursive: true, force: true });
18237
18405
  await this.cloneMarketplace(registry.repoUrl, cacheDir);
18238
18406
  } else {
18239
18407
  const { execFile } = await import("child_process");
@@ -18242,15 +18410,15 @@ class PluginManager {
18242
18410
  try {
18243
18411
  await exec("git", ["pull", "--ff-only"], { cwd: cacheDir, timeout: 30000 });
18244
18412
  } catch {
18245
- await fs24.rm(cacheDir, { recursive: true, force: true });
18413
+ await fs25.rm(cacheDir, { recursive: true, force: true });
18246
18414
  await this.cloneMarketplace(registry.repoUrl, cacheDir);
18247
18415
  }
18248
18416
  }
18249
18417
  } else {
18250
18418
  await this.cloneMarketplace(registry.repoUrl, cacheDir);
18251
18419
  }
18252
- await fs24.mkdir(cacheDir, { recursive: true });
18253
- await fs24.writeFile(cacheMetaPath, JSON.stringify({
18420
+ await fs25.mkdir(cacheDir, { recursive: true });
18421
+ await fs25.writeFile(cacheMetaPath, JSON.stringify({
18254
18422
  fetchedAt: Date.now(),
18255
18423
  repoUrl: registry.repoUrl
18256
18424
  }));
@@ -18261,7 +18429,7 @@ class PluginManager {
18261
18429
  return null;
18262
18430
  }
18263
18431
  try {
18264
- const meta = JSON.parse(await fs24.readFile(cacheMetaPath, "utf8"));
18432
+ const meta = JSON.parse(await fs25.readFile(cacheMetaPath, "utf8"));
18265
18433
  if (typeof meta.fetchedAt !== "number") {
18266
18434
  return null;
18267
18435
  }
@@ -18289,11 +18457,11 @@ class PluginManager {
18289
18457
  }
18290
18458
  }
18291
18459
  async cloneMarketplace(repoUrl, dest) {
18292
- await fs24.mkdir(dest, { recursive: true });
18460
+ await fs25.mkdir(dest, { recursive: true });
18293
18461
  const { execFile } = await import("child_process");
18294
18462
  const { promisify } = await import("util");
18295
18463
  const exec = promisify(execFile);
18296
- await fs24.rm(dest, { recursive: true, force: true }).catch(() => {
18464
+ await fs25.rm(dest, { recursive: true, force: true }).catch(() => {
18297
18465
  });
18298
18466
  await exec("git", ["clone", "--depth", "1", repoUrl, dest], { timeout: 60000 });
18299
18467
  }
@@ -18303,7 +18471,7 @@ class PluginManager {
18303
18471
  throw new Error(`Unknown marketplace: ${registryId}`);
18304
18472
  const cacheDir = await this.ensureMarketplaceCache(registryId);
18305
18473
  for (const dir of registry.pluginDirs) {
18306
- const pluginPath = join5(cacheDir, dir, name);
18474
+ const pluginPath = join6(cacheDir, dir, name);
18307
18475
  if (await isDirectory(pluginPath)) {
18308
18476
  return pluginPath;
18309
18477
  }
@@ -18319,7 +18487,7 @@ class PluginManager {
18319
18487
  const cacheDir = await this.ensureMarketplaceCache(registryId);
18320
18488
  const results = [];
18321
18489
  for (const dir of registry.pluginDirs) {
18322
- const fullDir = join5(cacheDir, dir);
18490
+ const fullDir = join6(cacheDir, dir);
18323
18491
  if (!await isDirectory(fullDir))
18324
18492
  continue;
18325
18493
  const cat = getMarketplaceCategoryForDir(dir);
@@ -18329,26 +18497,26 @@ class PluginManager {
18329
18497
  for (const entry of entries) {
18330
18498
  if (entry.startsWith("."))
18331
18499
  continue;
18332
- const entryPath = join5(fullDir, entry);
18500
+ const entryPath = join6(fullDir, entry);
18333
18501
  if (!await isDirectory(entryPath))
18334
18502
  continue;
18335
- const manifestPath = join5(entryPath, ".claude-plugin", "plugin.json");
18503
+ const manifestPath = join6(entryPath, ".claude-plugin", "plugin.json");
18336
18504
  let name = entry;
18337
18505
  let description = "";
18338
18506
  let version = "0.0.0";
18339
18507
  if (await fileExists(manifestPath)) {
18340
18508
  try {
18341
- const manifest = JSON.parse(await fs24.readFile(manifestPath, "utf8"));
18509
+ const manifest = JSON.parse(await fs25.readFile(manifestPath, "utf8"));
18342
18510
  name = manifest.name || entry;
18343
18511
  description = manifest.description || "";
18344
18512
  version = manifest.version || "0.0.0";
18345
18513
  } catch {
18346
18514
  }
18347
18515
  } else {
18348
- const skillMdPath = join5(entryPath, "SKILL.md");
18516
+ const skillMdPath = join6(entryPath, "SKILL.md");
18349
18517
  if (await fileExists(skillMdPath)) {
18350
18518
  try {
18351
- const parsed = import_gray_matter.default(await fs24.readFile(skillMdPath, "utf8"));
18519
+ const parsed = import_gray_matter.default(await fs25.readFile(skillMdPath, "utf8"));
18352
18520
  if (parsed.data.name)
18353
18521
  name = parsed.data.name;
18354
18522
  if (parsed.data.description)
@@ -18356,10 +18524,10 @@ class PluginManager {
18356
18524
  } catch {
18357
18525
  }
18358
18526
  } else {
18359
- const mcpPath = join5(entryPath, ".mcp.json");
18527
+ const mcpPath = join6(entryPath, ".mcp.json");
18360
18528
  if (await fileExists(mcpPath)) {
18361
18529
  try {
18362
- const mcpConfig = JSON.parse(await fs24.readFile(mcpPath, "utf8"));
18530
+ const mcpConfig = JSON.parse(await fs25.readFile(mcpPath, "utf8"));
18363
18531
  const serverNames = Object.keys(mcpConfig.mcpServers || mcpConfig);
18364
18532
  if (serverNames.length > 0) {
18365
18533
  description = `MCP server(s): ${serverNames.join(", ")}`;
@@ -18381,10 +18549,10 @@ class PluginManager {
18381
18549
  return results.sort((a, b) => a.name.localeCompare(b.name));
18382
18550
  }
18383
18551
  async detectFormat(pluginDir) {
18384
- if (await fileExists(join5(pluginDir, ".claude-plugin", "plugin.json"))) {
18552
+ if (await fileExists(join6(pluginDir, ".claude-plugin", "plugin.json"))) {
18385
18553
  return "claude";
18386
18554
  }
18387
- if (await fileExists(join5(pluginDir, ".cursor-plugin", "plugin.json"))) {
18555
+ if (await fileExists(join6(pluginDir, ".cursor-plugin", "plugin.json"))) {
18388
18556
  return "cursor";
18389
18557
  }
18390
18558
  return "generic";
@@ -18401,7 +18569,7 @@ class PluginManager {
18401
18569
  }
18402
18570
  }
18403
18571
  async parseClaudePlugin(pluginDir, source) {
18404
- const manifestPath = join5(pluginDir, ".claude-plugin", "plugin.json");
18572
+ const manifestPath = join6(pluginDir, ".claude-plugin", "plugin.json");
18405
18573
  const manifestContent = await readFileIfExists(manifestPath);
18406
18574
  if (!manifestContent) {
18407
18575
  throw new Error(`Missing .claude-plugin/plugin.json in ${pluginDir}`);
@@ -18412,10 +18580,10 @@ class PluginManager {
18412
18580
  const convertedSkills = await this.convertCommandsToSkills(pluginDir, manifest);
18413
18581
  skills.push(...convertedSkills);
18414
18582
  const mcpServers = await this.parseMcpJson(pluginDir);
18415
- if (await isDirectory(join5(pluginDir, "hooks")) || manifest.hooks) {
18583
+ if (await isDirectory(join6(pluginDir, "hooks")) || manifest.hooks) {
18416
18584
  warnings.push("Hooks (hooks/) are Claude Code-specific");
18417
18585
  }
18418
- if (await isDirectory(join5(pluginDir, "agents")) || manifest.agents) {
18586
+ if (await isDirectory(join6(pluginDir, "agents")) || manifest.agents) {
18419
18587
  warnings.push("Agent definitions (agents/) are Claude Code-specific");
18420
18588
  }
18421
18589
  return {
@@ -18430,7 +18598,7 @@ class PluginManager {
18430
18598
  };
18431
18599
  }
18432
18600
  async parseCursorPlugin(pluginDir, source) {
18433
- const manifestPath = join5(pluginDir, ".cursor-plugin", "plugin.json");
18601
+ const manifestPath = join6(pluginDir, ".cursor-plugin", "plugin.json");
18434
18602
  const manifestContent = await readFileIfExists(manifestPath);
18435
18603
  if (!manifestContent) {
18436
18604
  throw new Error(`Missing .cursor-plugin/plugin.json in ${pluginDir}`);
@@ -18469,7 +18637,7 @@ class PluginManager {
18469
18637
  };
18470
18638
  }
18471
18639
  async parseMcpJson(pluginDir) {
18472
- const mcpPath = join5(pluginDir, ".mcp.json");
18640
+ const mcpPath = join6(pluginDir, ".mcp.json");
18473
18641
  const content = await readFileIfExists(mcpPath);
18474
18642
  if (!content)
18475
18643
  return [];
@@ -18516,7 +18684,7 @@ class PluginManager {
18516
18684
  commandsDirs.push(resolve8(pluginDir, cmd));
18517
18685
  }
18518
18686
  } else {
18519
- commandsDirs.push(join5(pluginDir, "commands"));
18687
+ commandsDirs.push(join6(pluginDir, "commands"));
18520
18688
  }
18521
18689
  for (const commandsDir of commandsDirs) {
18522
18690
  if (!await isDirectory(commandsDir))
@@ -18525,7 +18693,7 @@ class PluginManager {
18525
18693
  for (const entry of entries) {
18526
18694
  if (!entry.endsWith(".md"))
18527
18695
  continue;
18528
- const cmdPath = join5(commandsDir, entry);
18696
+ const cmdPath = join6(commandsDir, entry);
18529
18697
  const skill = await this.convertSingleCommandToSkill(cmdPath, manifest.name);
18530
18698
  if (skill)
18531
18699
  skills.push(skill);
@@ -18839,7 +19007,7 @@ ${body.trim()}
18839
19007
  }
18840
19008
  if (!removedSkillPaths.has(skill.path)) {
18841
19009
  try {
18842
- await fs24.rm(skill.path, { recursive: true, force: true });
19010
+ await fs25.rm(skill.path, { recursive: true, force: true });
18843
19011
  removedSkillPaths.add(skill.path);
18844
19012
  } catch {
18845
19013
  details.push(`Could not remove skill path: ${skill.path}`);
@@ -18848,7 +19016,7 @@ ${body.trim()}
18848
19016
  }
18849
19017
  if (skill.canonicalPath && skill.canonicalPath !== skill.path && !removedCanonicalPaths.has(skill.canonicalPath) && !sharedCanonicalPath) {
18850
19018
  try {
18851
- await fs24.rm(skill.canonicalPath, { recursive: true, force: true });
19019
+ await fs25.rm(skill.canonicalPath, { recursive: true, force: true });
18852
19020
  removedCanonicalPaths.add(skill.canonicalPath);
18853
19021
  } catch {
18854
19022
  details.push(`Could not remove canonical skill path: ${skill.canonicalPath}`);
@@ -18954,31 +19122,31 @@ var init_pluginManager = __esm(() => {
18954
19122
  init_skillsManager();
18955
19123
  init_userConfig();
18956
19124
  getMarketplaceCacheDir = function(registryId) {
18957
- return join5(homedir5(), ".agentinit", "marketplace-cache", registryId);
19125
+ return join6(homedir5(), ".agentinit", "marketplace-cache", registryId);
18958
19126
  };
18959
19127
  getRegistryPath = function(projectPath, global3) {
18960
19128
  if (global3) {
18961
- return join5(homedir5(), ".agentinit", "plugins.json");
19129
+ return join6(homedir5(), ".agentinit", "plugins.json");
18962
19130
  }
18963
- return join5(projectPath, ".agentinit", "plugins.json");
19131
+ return join6(projectPath, ".agentinit", "plugins.json");
18964
19132
  };
18965
19133
  getClaudeInstalledPluginsPath = function() {
18966
- return join5(homedir5(), ".claude", "plugins", "installed_plugins.json");
19134
+ return join6(homedir5(), ".claude", "plugins", "installed_plugins.json");
18967
19135
  };
18968
19136
  getClaudeKnownMarketplacesPath = function() {
18969
- return join5(homedir5(), ".claude", "plugins", "known_marketplaces.json");
19137
+ return join6(homedir5(), ".claude", "plugins", "known_marketplaces.json");
18970
19138
  };
18971
19139
  getClaudeMarketplaceInstallPath = function(namespace) {
18972
- return join5(homedir5(), ".claude", "plugins", "marketplaces", namespace);
19140
+ return join6(homedir5(), ".claude", "plugins", "marketplaces", namespace);
18973
19141
  };
18974
19142
  getClaudeSettingsPath = function() {
18975
- return join5(homedir5(), ".claude", "settings.json");
19143
+ return join6(homedir5(), ".claude", "settings.json");
18976
19144
  };
18977
19145
  });
18978
19146
 
18979
19147
  // dist/core/skillsManager.js
18980
- import {resolve as resolve9, join as join6, relative as relative3, basename as basename3, dirname as dirname4} from "path";
18981
- import {promises as fs26} from "fs";
19148
+ import {resolve as resolve9, join as join7, relative as relative4, basename as basename3, dirname as dirname4} from "path";
19149
+ import {promises as fs27} from "fs";
18982
19150
  import {homedir as homedir6, tmpdir} from "os";
18983
19151
  import {execFile} from "child_process";
18984
19152
  import {createHash as createHash2} from "crypto";
@@ -18987,6 +19155,7 @@ import {promisify} from "util";
18987
19155
  class SkillsManager {
18988
19156
  agentManager;
18989
19157
  preparedSourceContexts = new Map;
19158
+ skillScanner = new SkillSecurityScanner;
18990
19159
  constructor(agentManager3) {
18991
19160
  this.agentManager = agentManager3 || new AgentManager;
18992
19161
  }
@@ -18994,11 +19163,15 @@ class SkillsManager {
18994
19163
  if (source.startsWith(".") || source.startsWith("/") || source.startsWith("~")) {
18995
19164
  return { type: "local", path: source };
18996
19165
  }
18997
- const githubUrlSource = this.parseGitHubHttpSource(source);
18998
- if (githubUrlSource) {
18999
- return githubUrlSource;
19166
+ const httpSource = this.parseHttpRepositorySource(source);
19167
+ if (httpSource) {
19168
+ return httpSource;
19000
19169
  }
19001
- if (source.startsWith("git@") || source.endsWith(".git")) {
19170
+ const sshSource = this.parseSshRepositorySource(source);
19171
+ if (sshSource) {
19172
+ return sshSource;
19173
+ }
19174
+ if (source.endsWith(".git")) {
19002
19175
  return { type: "github", url: source };
19003
19176
  }
19004
19177
  if (options2?.from) {
@@ -19011,6 +19184,14 @@ class SkillsManager {
19011
19184
  pluginName: source
19012
19185
  };
19013
19186
  }
19187
+ const gitLabShorthandSource = this.parseGitLabShorthandSource(source);
19188
+ if (gitLabShorthandSource) {
19189
+ return gitLabShorthandSource;
19190
+ }
19191
+ const bitbucketShorthandSource = this.parseBitbucketShorthandSource(source);
19192
+ if (bitbucketShorthandSource) {
19193
+ return bitbucketShorthandSource;
19194
+ }
19014
19195
  const githubShorthandSource = this.parseGitHubShorthandSource(source);
19015
19196
  if (githubShorthandSource?.subpath) {
19016
19197
  return githubShorthandSource;
@@ -19100,6 +19281,106 @@ class SkillsManager {
19100
19281
  return null;
19101
19282
  }
19102
19283
  }
19284
+ parseGitLabHttpSource(source) {
19285
+ if (!source.startsWith("https://gitlab.com/") && !source.startsWith("http://gitlab.com/")) {
19286
+ return null;
19287
+ }
19288
+ try {
19289
+ const parsedUrl = new URL(source);
19290
+ const segments = parsedUrl.pathname.replace(/\/+$/, "").split("/").filter(Boolean);
19291
+ const dashIndex = segments.indexOf("-");
19292
+ const repoBoundary = dashIndex >= 0 ? dashIndex : segments.length;
19293
+ if (repoBoundary < 2) {
19294
+ return null;
19295
+ }
19296
+ const repo = segments[repoBoundary - 1];
19297
+ const owner = segments.slice(0, repoBoundary - 1).join("/");
19298
+ if (!owner || !repo) {
19299
+ return null;
19300
+ }
19301
+ let subpath;
19302
+ if (dashIndex >= 0) {
19303
+ const marker = segments[dashIndex + 1];
19304
+ if ((marker === "tree" || marker === "blob") && segments.length > dashIndex + 3) {
19305
+ subpath = segments.slice(dashIndex + 3).join("/");
19306
+ }
19307
+ }
19308
+ return {
19309
+ type: "gitlab",
19310
+ url: `https://gitlab.com/${owner}/${repo}.git`,
19311
+ owner,
19312
+ repo,
19313
+ ...subpath ? { subpath } : {}
19314
+ };
19315
+ } catch {
19316
+ return null;
19317
+ }
19318
+ }
19319
+ parseBitbucketHttpSource(source) {
19320
+ if (!source.startsWith("https://bitbucket.org/") && !source.startsWith("http://bitbucket.org/")) {
19321
+ return null;
19322
+ }
19323
+ try {
19324
+ const parsedUrl = new URL(source);
19325
+ const segments = parsedUrl.pathname.replace(/\/+$/, "").split("/").filter(Boolean);
19326
+ if (segments.length < 2) {
19327
+ return null;
19328
+ }
19329
+ const [owner, repo, marker, _commitish, ...rest] = segments;
19330
+ if (!owner || !repo) {
19331
+ return null;
19332
+ }
19333
+ let subpath;
19334
+ if (marker === "src" && rest.length > 0) {
19335
+ subpath = rest.join("/");
19336
+ }
19337
+ return {
19338
+ type: "bitbucket",
19339
+ url: `https://bitbucket.org/${owner}/${repo}.git`,
19340
+ owner,
19341
+ repo,
19342
+ ...subpath ? { subpath } : {}
19343
+ };
19344
+ } catch {
19345
+ return null;
19346
+ }
19347
+ }
19348
+ parseHttpRepositorySource(source) {
19349
+ return this.parseGitHubHttpSource(source) || this.parseGitLabHttpSource(source) || this.parseBitbucketHttpSource(source);
19350
+ }
19351
+ parseSshRepositorySource(source) {
19352
+ const githubMatch = source.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
19353
+ if (githubMatch) {
19354
+ const [, owner, repo] = githubMatch;
19355
+ return {
19356
+ type: "github",
19357
+ url: `git@github.com:${owner}/${repo}.git`,
19358
+ owner,
19359
+ repo
19360
+ };
19361
+ }
19362
+ const gitlabMatch = source.match(/^git@gitlab\.com:(.+)\/([^/]+?)(?:\.git)?$/);
19363
+ if (gitlabMatch) {
19364
+ const [, owner, repo] = gitlabMatch;
19365
+ return {
19366
+ type: "gitlab",
19367
+ url: `git@gitlab.com:${owner}/${repo}.git`,
19368
+ owner,
19369
+ repo
19370
+ };
19371
+ }
19372
+ const bitbucketMatch = source.match(/^git@bitbucket\.org:([^/]+)\/([^/]+?)(?:\.git)?$/);
19373
+ if (bitbucketMatch) {
19374
+ const [, owner, repo] = bitbucketMatch;
19375
+ return {
19376
+ type: "bitbucket",
19377
+ url: `git@bitbucket.org:${owner}/${repo}.git`,
19378
+ owner,
19379
+ repo
19380
+ };
19381
+ }
19382
+ return null;
19383
+ }
19103
19384
  parseGitHubShorthandSource(source) {
19104
19385
  const githubShorthandMatch = source.match(/^([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)(?:\/(.+))?$/);
19105
19386
  if (!githubShorthandMatch) {
@@ -19114,6 +19395,50 @@ class SkillsManager {
19114
19395
  ...subpath ? { subpath } : {}
19115
19396
  };
19116
19397
  }
19398
+ parseGitLabShorthandSource(source) {
19399
+ const normalized = source.startsWith("gitlab:") ? source.slice("gitlab:".length) : source.startsWith("gitlab.com/") ? source.slice("gitlab.com/".length) : null;
19400
+ if (!normalized) {
19401
+ return null;
19402
+ }
19403
+ const [repoSpec = normalized, subpathSpec] = normalized.split("//", 2);
19404
+ const segments = repoSpec.split("/").filter(Boolean);
19405
+ if (segments.length < 2) {
19406
+ return null;
19407
+ }
19408
+ const repo = segments[segments.length - 1];
19409
+ const owner = segments.slice(0, segments.length - 1).join("/");
19410
+ if (!owner || !repo) {
19411
+ return null;
19412
+ }
19413
+ return {
19414
+ type: "gitlab",
19415
+ url: `https://gitlab.com/${owner}/${repo}.git`,
19416
+ owner,
19417
+ repo,
19418
+ ...subpathSpec ? { subpath: subpathSpec } : {}
19419
+ };
19420
+ }
19421
+ parseBitbucketShorthandSource(source) {
19422
+ const normalized = source.startsWith("bitbucket:") ? source.slice("bitbucket:".length) : source.startsWith("bitbucket.org/") ? source.slice("bitbucket.org/".length) : null;
19423
+ if (!normalized) {
19424
+ return null;
19425
+ }
19426
+ const segments = normalized.split("/").filter(Boolean);
19427
+ if (segments.length < 2) {
19428
+ return null;
19429
+ }
19430
+ const [owner, repo, ...rest] = segments;
19431
+ if (!owner || !repo) {
19432
+ return null;
19433
+ }
19434
+ return {
19435
+ type: "bitbucket",
19436
+ url: `https://bitbucket.org/${owner}/${repo}.git`,
19437
+ owner,
19438
+ repo,
19439
+ ...rest.length > 0 ? { subpath: rest.join("/") } : {}
19440
+ };
19441
+ }
19117
19442
  async parseSkillMd(filePath) {
19118
19443
  const content = await readFileIfExists(filePath);
19119
19444
  if (!content)
@@ -19135,7 +19460,7 @@ class SkillsManager {
19135
19460
  const fullDir = resolve9(repoPath, searchDir);
19136
19461
  if (!await fileExists(fullDir))
19137
19462
  continue;
19138
- const directSkillMd = join6(fullDir, "SKILL.md");
19463
+ const directSkillMd = join7(fullDir, "SKILL.md");
19139
19464
  if (await fileExists(directSkillMd)) {
19140
19465
  const parsed = await this.parseSkillMd(directSkillMd);
19141
19466
  if (parsed && !seen.has(parsed.name)) {
@@ -19143,7 +19468,7 @@ class SkillsManager {
19143
19468
  skills2.push({ ...parsed, path: resolve9(fullDir) });
19144
19469
  }
19145
19470
  }
19146
- const directSkillMdLower = join6(fullDir, "skill.md");
19471
+ const directSkillMdLower = join7(fullDir, "skill.md");
19147
19472
  if (await fileExists(directSkillMdLower)) {
19148
19473
  const parsed = await this.parseSkillMd(directSkillMdLower);
19149
19474
  if (parsed && !seen.has(parsed.name)) {
@@ -19155,11 +19480,11 @@ class SkillsManager {
19155
19480
  continue;
19156
19481
  const entries = await listFiles(fullDir);
19157
19482
  for (const entry of entries) {
19158
- const entryPath = join6(fullDir, entry);
19483
+ const entryPath = join7(fullDir, entry);
19159
19484
  if (!await isDirectory(entryPath))
19160
19485
  continue;
19161
- const skillMdPath = join6(entryPath, "SKILL.md");
19162
- const skillMdPathLower = join6(entryPath, "skill.md");
19486
+ const skillMdPath = join7(entryPath, "SKILL.md");
19487
+ const skillMdPathLower = join7(entryPath, "skill.md");
19163
19488
  const skillFile = await fileExists(skillMdPath) ? skillMdPath : await fileExists(skillMdPathLower) ? skillMdPathLower : null;
19164
19489
  if (!skillFile)
19165
19490
  continue;
@@ -19173,14 +19498,14 @@ class SkillsManager {
19173
19498
  return skills2;
19174
19499
  }
19175
19500
  async cloneRepo(url) {
19176
- const tempDir = await fs26.mkdtemp(join6(tmpdir(), "agentinit-skills-"));
19177
- await fs26.rm(tempDir, { recursive: true, force: true });
19501
+ const tempDir = await fs27.mkdtemp(join7(tmpdir(), "agentinit-skills-"));
19502
+ await fs27.rm(tempDir, { recursive: true, force: true });
19178
19503
  try {
19179
19504
  await execFileAsync("git", ["clone", "--depth", "1", url, tempDir], {
19180
19505
  timeout: 60000
19181
19506
  });
19182
19507
  } catch (error) {
19183
- await fs26.rm(tempDir, { recursive: true, force: true }).catch(() => {
19508
+ await fs27.rm(tempDir, { recursive: true, force: true }).catch(() => {
19184
19509
  });
19185
19510
  throw new Error(`Failed to clone ${url}: ${error.message}`);
19186
19511
  }
@@ -19238,12 +19563,12 @@ class SkillsManager {
19238
19563
  if (!tempDir) {
19239
19564
  return;
19240
19565
  }
19241
- await fs26.rm(tempDir, { recursive: true, force: true }).catch(() => {
19566
+ await fs27.rm(tempDir, { recursive: true, force: true }).catch(() => {
19242
19567
  });
19243
19568
  }
19244
19569
  async resolveDiscoveryRoot(repoPath, source, sourceLabel) {
19245
19570
  const resolvedRepoPath = resolve9(repoPath);
19246
- if (source.type !== "github" || !source.subpath) {
19571
+ if (source.type !== "github" && source.type !== "gitlab" && source.type !== "bitbucket" || !source.subpath) {
19247
19572
  return resolvedRepoPath;
19248
19573
  }
19249
19574
  const discoveryRoot = resolve9(resolvedRepoPath, source.subpath);
@@ -19288,7 +19613,7 @@ class SkillsManager {
19288
19613
  };
19289
19614
  }
19290
19615
  let repoPath;
19291
- if (resolved.type === "github") {
19616
+ if (resolved.type === "github" || resolved.type === "gitlab" || resolved.type === "bitbucket") {
19292
19617
  if (!resolved.url) {
19293
19618
  throw new Error(`Invalid source: ${source}`);
19294
19619
  }
@@ -19382,26 +19707,26 @@ class SkillsManager {
19382
19707
  async installSkill(skillPath, skillName, targetDir, copy = false) {
19383
19708
  const normalizedSkillName = this.normalizeSkillName(skillName);
19384
19709
  const destPath = this.resolveInstallPath(targetDir, normalizedSkillName);
19385
- await fs26.mkdir(resolve9(targetDir), { recursive: true });
19710
+ await fs27.mkdir(resolve9(targetDir), { recursive: true });
19386
19711
  if (await fileExists(destPath)) {
19387
- await fs26.rm(destPath, { recursive: true, force: true });
19712
+ await fs27.rm(destPath, { recursive: true, force: true });
19388
19713
  }
19389
19714
  if (copy) {
19390
19715
  await this.copyDir(skillPath, destPath);
19391
19716
  } else {
19392
- await fs26.symlink(skillPath, destPath, "dir");
19717
+ await fs27.symlink(skillPath, destPath, "dir");
19393
19718
  }
19394
19719
  return destPath;
19395
19720
  }
19396
19721
  async installSkillFromContent(skillName, skillContent, targetDir) {
19397
19722
  const normalizedSkillName = this.normalizeSkillName(skillName);
19398
19723
  const destPath = this.resolveInstallPath(targetDir, normalizedSkillName);
19399
- await fs26.mkdir(resolve9(targetDir), { recursive: true });
19724
+ await fs27.mkdir(resolve9(targetDir), { recursive: true });
19400
19725
  if (await fileExists(destPath)) {
19401
- await fs26.rm(destPath, { recursive: true, force: true });
19726
+ await fs27.rm(destPath, { recursive: true, force: true });
19402
19727
  }
19403
- await fs26.mkdir(destPath, { recursive: true });
19404
- await fs26.writeFile(join6(destPath, "SKILL.md"), skillContent, "utf8");
19728
+ await fs27.mkdir(destPath, { recursive: true });
19729
+ await fs27.writeFile(join7(destPath, "SKILL.md"), skillContent, "utf8");
19405
19730
  return destPath;
19406
19731
  }
19407
19732
  getCanonicalSkillsDir(projectPath, global3 = false) {
@@ -19495,7 +19820,7 @@ class SkillsManager {
19495
19820
  throw new Error(`Missing canonical path for ${skillName}`);
19496
19821
  }
19497
19822
  await this.cleanAndCreateDirectory(canonicalPath);
19498
- await fs26.writeFile(join6(canonicalPath, "SKILL.md"), skillContent, "utf8");
19823
+ await fs27.writeFile(join7(canonicalPath, "SKILL.md"), skillContent, "utf8");
19499
19824
  if (plan.path === canonicalPath) {
19500
19825
  return plan;
19501
19826
  }
@@ -19518,7 +19843,7 @@ class SkillsManager {
19518
19843
  async installSkillFromContentToCanonicalStore(skillName, skillContent, projectPath, options2 = {}) {
19519
19844
  const plan = this.getCanonicalInstallPlan(skillName, projectPath, options2);
19520
19845
  await this.cleanAndCreateDirectory(plan.path);
19521
- await fs26.writeFile(join6(plan.path, "SKILL.md"), skillContent, "utf8");
19846
+ await fs27.writeFile(join7(plan.path, "SKILL.md"), skillContent, "utf8");
19522
19847
  return plan;
19523
19848
  }
19524
19849
  getCanonicalInstallPlan(skillName, projectPath, options2 = {}) {
@@ -19530,6 +19855,76 @@ class SkillsManager {
19530
19855
  mode: "symlink"
19531
19856
  };
19532
19857
  }
19858
+ normalizeSkillPrefix(prefix) {
19859
+ const normalized = prefix?.trim() ?? "";
19860
+ if (normalized.includes("/") || normalized.includes("\\")) {
19861
+ throw new Error(`Invalid skill prefix: ${prefix}`);
19862
+ }
19863
+ return normalized;
19864
+ }
19865
+ withSkillPrefix(skillName, prefix) {
19866
+ const normalizedPrefix = this.normalizeSkillPrefix(prefix);
19867
+ return normalizedPrefix ? `${normalizedPrefix}${skillName}` : skillName;
19868
+ }
19869
+ async rewriteSkillFileName(filePath, skillName) {
19870
+ const content = await fs27.readFile(filePath, "utf8");
19871
+ const parsed = import_gray_matter2.default(content);
19872
+ const nextContent = import_gray_matter2.default.stringify(parsed.content, {
19873
+ ...parsed.data,
19874
+ name: skillName
19875
+ });
19876
+ await fs27.writeFile(filePath, nextContent, "utf8");
19877
+ }
19878
+ async applyPrefixToSkills(skills2, prefix) {
19879
+ const normalizedPrefix = this.normalizeSkillPrefix(prefix);
19880
+ if (!normalizedPrefix) {
19881
+ return { skills: skills2, cleanup: async () => {
19882
+ } };
19883
+ }
19884
+ const tempDirs = [];
19885
+ try {
19886
+ const prefixedSkills = await Promise.all(skills2.map(async (skill) => {
19887
+ const name = this.withSkillPrefix(skill.name, normalizedPrefix);
19888
+ if (skill.generatedContent) {
19889
+ return {
19890
+ ...skill,
19891
+ name,
19892
+ generatedContent: import_gray_matter2.default.stringify(import_gray_matter2.default(skill.generatedContent).content, {
19893
+ ...import_gray_matter2.default(skill.generatedContent).data,
19894
+ name
19895
+ })
19896
+ };
19897
+ }
19898
+ const tempRoot = await fs27.mkdtemp(join7(tmpdir(), "agentinit-prefixed-skill-"));
19899
+ const tempSkillPath = join7(tempRoot, basename3(skill.path));
19900
+ tempDirs.push(tempRoot);
19901
+ await this.copyDir(skill.path, tempSkillPath);
19902
+ const skillMdPath = join7(tempSkillPath, "SKILL.md");
19903
+ const skillMdPathLower = join7(tempSkillPath, "skill.md");
19904
+ const skillFile = await fileExists(skillMdPath) ? skillMdPath : await fileExists(skillMdPathLower) ? skillMdPathLower : null;
19905
+ if (!skillFile) {
19906
+ throw new Error(`Skill "${skill.name}" is missing SKILL.md`);
19907
+ }
19908
+ await this.rewriteSkillFileName(skillFile, name);
19909
+ return {
19910
+ ...skill,
19911
+ name,
19912
+ path: tempSkillPath
19913
+ };
19914
+ }));
19915
+ return {
19916
+ skills: prefixedSkills,
19917
+ cleanup: async () => {
19918
+ await Promise.all(tempDirs.map((dir) => fs27.rm(dir, { recursive: true, force: true }).catch(() => {
19919
+ })));
19920
+ }
19921
+ };
19922
+ } catch (error) {
19923
+ await Promise.all(tempDirs.map((dir) => fs27.rm(dir, { recursive: true, force: true }).catch(() => {
19924
+ })));
19925
+ throw error;
19926
+ }
19927
+ }
19533
19928
  normalizeSkillName(skillName) {
19534
19929
  const normalized = skillName.trim();
19535
19930
  if (!normalized) {
@@ -19543,7 +19938,7 @@ class SkillsManager {
19543
19938
  resolveInstallPath(targetDir, skillName) {
19544
19939
  const resolvedTargetDir = resolve9(targetDir);
19545
19940
  const destPath = resolve9(resolvedTargetDir, skillName);
19546
- const relativePath = relative3(resolvedTargetDir, destPath);
19941
+ const relativePath = relative4(resolvedTargetDir, destPath);
19547
19942
  if (relativePath === "" || relativePath.startsWith("..") || relativePath.includes("/../") || relativePath.includes("\\..\\")) {
19548
19943
  throw new Error(`Refusing to install skill outside target directory: ${skillName}`);
19549
19944
  }
@@ -19562,16 +19957,16 @@ class SkillsManager {
19562
19957
  return null;
19563
19958
  const hash = createHash2("sha256");
19564
19959
  const walk = async (currentPath, relativePath) => {
19565
- const stat = await fs26.stat(currentPath);
19960
+ const stat = await fs27.stat(currentPath);
19566
19961
  if (!stat.isDirectory()) {
19567
- const content = await fs26.readFile(currentPath);
19962
+ const content = await fs27.readFile(currentPath);
19568
19963
  this.updateSnapshotWithFile(hash, relativePath, content);
19569
19964
  return;
19570
19965
  }
19571
19966
  hash.update(`dir:${relativePath || "."}\n`);
19572
- const entries = (await fs26.readdir(currentPath)).sort((left, right) => left.localeCompare(right));
19967
+ const entries = (await fs27.readdir(currentPath)).sort((left, right) => left.localeCompare(right));
19573
19968
  for (const entry of entries) {
19574
- await walk(join6(currentPath, entry), relativePath ? join6(relativePath, entry) : entry);
19969
+ await walk(join7(currentPath, entry), relativePath ? join7(relativePath, entry) : entry);
19575
19970
  }
19576
19971
  };
19577
19972
  await walk(rootPath, "");
@@ -19616,22 +20011,25 @@ class SkillsManager {
19616
20011
  return existingAtCanonical === incoming ? "new" : "changed";
19617
20012
  }
19618
20013
  async cleanAndCreateDirectory(path) {
19619
- await fs26.rm(path, { recursive: true, force: true }).catch(() => {
20014
+ await fs27.rm(path, { recursive: true, force: true }).catch(() => {
19620
20015
  });
19621
- await fs26.mkdir(path, { recursive: true });
20016
+ await fs27.mkdir(path, { recursive: true });
19622
20017
  }
19623
20018
  isWithinPath(basePath, targetPath) {
19624
- const relativePath = relative3(resolve9(basePath), resolve9(targetPath));
20019
+ const relativePath = relative4(resolve9(basePath), resolve9(targetPath));
19625
20020
  return relativePath === "" || !relativePath.startsWith("..") && !relativePath.includes("/../") && !relativePath.includes("\\..\\");
19626
20021
  }
19627
20022
  async copyDir(src, dest) {
19628
- await fs26.cp(src, dest, { recursive: true, dereference: true });
20023
+ await fs27.cp(src, dest, { recursive: true, dereference: true });
19629
20024
  }
19630
20025
  async addFromSource(source, projectPath, options2 = {}) {
20026
+ const normalizedPrefix = this.normalizeSkillPrefix(options2.prefix);
19631
20027
  const context = this.takePreparedSourceContext(source, projectPath, options2.from) || await this.loadDiscoveredSkillsContext(source, projectPath, {
19632
20028
  ...options2.from !== undefined ? { from: options2.from } : {},
19633
20029
  ...options2.pluginName !== undefined ? { pluginName: options2.pluginName } : {}
19634
20030
  });
20031
+ let prefixedCleanup = async () => {
20032
+ };
19635
20033
  try {
19636
20034
  let skills2 = context.skills;
19637
20035
  if (skills2.length === 0) {
@@ -19639,7 +20037,34 @@ class SkillsManager {
19639
20037
  }
19640
20038
  if (options2.skills && options2.skills.length > 0) {
19641
20039
  const names = new Set(options2.skills.map((skill) => skill.toLowerCase()));
19642
- skills2 = skills2.filter((skill) => names.has(skill.name.toLowerCase()));
20040
+ skills2 = skills2.filter((skill) => names.has(skill.name.toLowerCase()) || names.has(this.withSkillPrefix(skill.name, normalizedPrefix).toLowerCase()));
20041
+ }
20042
+ const prefixed = await this.applyPrefixToSkills(skills2, normalizedPrefix);
20043
+ skills2 = prefixed.skills;
20044
+ prefixedCleanup = prefixed.cleanup;
20045
+ const result = { installed: [], updated: [], unchanged: [], skipped: [], warnings: [...context.warnings] };
20046
+ if (options2.scan !== false) {
20047
+ const scannedWarnings = new Set;
20048
+ const scannableSkills = [];
20049
+ for (const skill of skills2) {
20050
+ const scan = await this.skillScanner.scanSkill(skill);
20051
+ if (scan.findings.length === 0) {
20052
+ scannableSkills.push(skill);
20053
+ continue;
20054
+ }
20055
+ if (scan.blocked && !options2.allowRisky) {
20056
+ result.skipped.push({
20057
+ skill,
20058
+ reason: this.skillScanner.formatBlockingReason(scan)
20059
+ });
20060
+ continue;
20061
+ }
20062
+ const summary = this.skillScanner.formatShortSummary(scan);
20063
+ scannedWarnings.add(scan.blocked ? `Proceeding with "${skill.name}" despite high-risk findings: ${summary}` : `Security warnings for "${skill.name}": ${summary}`);
20064
+ scannableSkills.push(skill);
20065
+ }
20066
+ skills2 = scannableSkills;
20067
+ result.warnings.push(...scannedWarnings);
19643
20068
  }
19644
20069
  const installToSharedStore = options2.agents?.includes(SHARED_SKILLS_TARGET_ID) ?? false;
19645
20070
  const agents = await this.getTargetAgents(projectPath, options2);
@@ -19648,11 +20073,13 @@ class SkillsManager {
19648
20073
  installed: [],
19649
20074
  updated: [],
19650
20075
  unchanged: [],
19651
- skipped: skills2.map((skill) => ({ skill, reason: "No target agents found" })),
19652
- warnings: context.warnings
20076
+ skipped: [
20077
+ ...result.skipped,
20078
+ ...skills2.map((skill) => ({ skill, reason: "No target agents found" }))
20079
+ ],
20080
+ warnings: result.warnings
19653
20081
  };
19654
20082
  }
19655
- const result = { installed: [], updated: [], unchanged: [], skipped: [], warnings: context.warnings };
19656
20083
  const installableAgents = [];
19657
20084
  const comparisonCache = new Map;
19658
20085
  const pendingUpdates = [];
@@ -19762,6 +20189,7 @@ class SkillsManager {
19762
20189
  type: resolvedSource.type,
19763
20190
  ...resolvedSource.marketplace ? { marketplace: resolvedSource.marketplace } : {},
19764
20191
  ...resolvedSource.pluginName ? { pluginName: resolvedSource.pluginName } : {},
20192
+ ...normalizedPrefix ? { prefix: normalizedPrefix } : {},
19765
20193
  ...resolvedSource.url ? { url: resolvedSource.url } : {},
19766
20194
  ...resolvedSource.path ? { path: resolve9(projectPath, expandTilde(resolvedSource.path)) } : {},
19767
20195
  ...resolvedSource.owner ? { owner: resolvedSource.owner } : {},
@@ -19793,6 +20221,7 @@ class SkillsManager {
19793
20221
  }
19794
20222
  return result;
19795
20223
  } finally {
20224
+ await prefixedCleanup();
19796
20225
  await context.cleanup();
19797
20226
  }
19798
20227
  }
@@ -19815,11 +20244,11 @@ class SkillsManager {
19815
20244
  continue;
19816
20245
  const entries = await listFiles(dir);
19817
20246
  for (const entry of entries) {
19818
- const entryPath = join6(dir, entry);
20247
+ const entryPath = join7(dir, entry);
19819
20248
  if (!await isDirectory(entryPath))
19820
20249
  continue;
19821
- const skillMdPath = join6(entryPath, "SKILL.md");
19822
- const skillMdPathLower = join6(entryPath, "skill.md");
20250
+ const skillMdPath = join7(entryPath, "SKILL.md");
20251
+ const skillMdPathLower = join7(entryPath, "skill.md");
19823
20252
  const skillFile = await fileExists(skillMdPath) ? skillMdPath : await fileExists(skillMdPathLower) ? skillMdPathLower : null;
19824
20253
  if (!skillFile)
19825
20254
  continue;
@@ -19829,7 +20258,7 @@ class SkillsManager {
19829
20258
  let isSymlink = false;
19830
20259
  let canonicalPath;
19831
20260
  try {
19832
- const stat = await fs26.lstat(entryPath);
20261
+ const stat = await fs27.lstat(entryPath);
19833
20262
  isSymlink = stat.isSymbolicLink();
19834
20263
  const canonicalBase = this.getCanonicalSkillsDir(projectPath, scope === "global");
19835
20264
  const [resolvedEntryPath, resolvedCanonicalBase] = await Promise.all([
@@ -19866,14 +20295,14 @@ class SkillsManager {
19866
20295
  continue;
19867
20296
  const entries = await listFiles(canonicalDir);
19868
20297
  for (const entry of entries) {
19869
- const entryPath = join6(canonicalDir, entry);
20298
+ const entryPath = join7(canonicalDir, entry);
19870
20299
  if (!await isDirectory(entryPath))
19871
20300
  continue;
19872
20301
  const resolvedEntryPath = resolve9(entryPath);
19873
20302
  if (referencedCanonicalPaths.has(resolvedEntryPath))
19874
20303
  continue;
19875
- const skillMdPath = join6(entryPath, "SKILL.md");
19876
- const skillMdPathLower = join6(entryPath, "skill.md");
20304
+ const skillMdPath = join7(entryPath, "SKILL.md");
20305
+ const skillMdPathLower = join7(entryPath, "skill.md");
19877
20306
  const skillFile = await fileExists(skillMdPath) ? skillMdPath : await fileExists(skillMdPathLower) ? skillMdPathLower : null;
19878
20307
  if (!skillFile)
19879
20308
  continue;
@@ -19940,7 +20369,7 @@ class SkillsManager {
19940
20369
  }
19941
20370
  if (!removedPaths.has(entry.path)) {
19942
20371
  try {
19943
- await fs26.rm(entry.path, { recursive: true, force: true });
20372
+ await fs27.rm(entry.path, { recursive: true, force: true });
19944
20373
  removedPaths.add(entry.path);
19945
20374
  } catch {
19946
20375
  skipped.push({
@@ -19953,7 +20382,7 @@ class SkillsManager {
19953
20382
  if (entry.canonicalPath && entry.canonicalPath !== entry.path && !removedCanonicalPaths.has(entry.canonicalPath)) {
19954
20383
  const stillReferenced = remainingEntries.some((other) => other.name.toLowerCase() === entry.name.toLowerCase() && other.canonicalPath === entry.canonicalPath);
19955
20384
  if (!stillReferenced) {
19956
- await fs26.rm(entry.canonicalPath, { recursive: true, force: true }).catch(() => {
20385
+ await fs27.rm(entry.canonicalPath, { recursive: true, force: true }).catch(() => {
19957
20386
  });
19958
20387
  removedCanonicalPaths.add(entry.canonicalPath);
19959
20388
  }
@@ -19999,6 +20428,7 @@ var init_skillsManager = __esm(() => {
19999
20428
  init_paths();
20000
20429
  init_agentManager();
20001
20430
  init_marketplaceRegistry();
20431
+ init_skillSecurityScanner();
20002
20432
  init_skills();
20003
20433
  init_installLock();
20004
20434
  execFileAsync = promisify(execFile);
@@ -20884,51 +21314,51 @@ var require_uri_all = __commonJS((exports, module) => {
20884
21314
  }
20885
21315
  return uriTokens.join("");
20886
21316
  }
20887
- function resolveComponents(base2, relative6) {
21317
+ function resolveComponents(base2, relative7) {
20888
21318
  var options2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
20889
21319
  var skipNormalization = arguments[3];
20890
21320
  var target = {};
20891
21321
  if (!skipNormalization) {
20892
21322
  base2 = parse4(serialize(base2, options2), options2);
20893
- relative6 = parse4(serialize(relative6, options2), options2);
21323
+ relative7 = parse4(serialize(relative7, options2), options2);
20894
21324
  }
20895
21325
  options2 = options2 || {};
20896
- if (!options2.tolerant && relative6.scheme) {
20897
- target.scheme = relative6.scheme;
20898
- target.userinfo = relative6.userinfo;
20899
- target.host = relative6.host;
20900
- target.port = relative6.port;
20901
- target.path = removeDotSegments(relative6.path || "");
20902
- target.query = relative6.query;
21326
+ if (!options2.tolerant && relative7.scheme) {
21327
+ target.scheme = relative7.scheme;
21328
+ target.userinfo = relative7.userinfo;
21329
+ target.host = relative7.host;
21330
+ target.port = relative7.port;
21331
+ target.path = removeDotSegments(relative7.path || "");
21332
+ target.query = relative7.query;
20903
21333
  } else {
20904
- if (relative6.userinfo !== undefined || relative6.host !== undefined || relative6.port !== undefined) {
20905
- target.userinfo = relative6.userinfo;
20906
- target.host = relative6.host;
20907
- target.port = relative6.port;
20908
- target.path = removeDotSegments(relative6.path || "");
20909
- target.query = relative6.query;
21334
+ if (relative7.userinfo !== undefined || relative7.host !== undefined || relative7.port !== undefined) {
21335
+ target.userinfo = relative7.userinfo;
21336
+ target.host = relative7.host;
21337
+ target.port = relative7.port;
21338
+ target.path = removeDotSegments(relative7.path || "");
21339
+ target.query = relative7.query;
20910
21340
  } else {
20911
- if (!relative6.path) {
21341
+ if (!relative7.path) {
20912
21342
  target.path = base2.path;
20913
- if (relative6.query !== undefined) {
20914
- target.query = relative6.query;
21343
+ if (relative7.query !== undefined) {
21344
+ target.query = relative7.query;
20915
21345
  } else {
20916
21346
  target.query = base2.query;
20917
21347
  }
20918
21348
  } else {
20919
- if (relative6.path.charAt(0) === "/") {
20920
- target.path = removeDotSegments(relative6.path);
21349
+ if (relative7.path.charAt(0) === "/") {
21350
+ target.path = removeDotSegments(relative7.path);
20921
21351
  } else {
20922
21352
  if ((base2.userinfo !== undefined || base2.host !== undefined || base2.port !== undefined) && !base2.path) {
20923
- target.path = "/" + relative6.path;
21353
+ target.path = "/" + relative7.path;
20924
21354
  } else if (!base2.path) {
20925
- target.path = relative6.path;
21355
+ target.path = relative7.path;
20926
21356
  } else {
20927
- target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative6.path;
21357
+ target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative7.path;
20928
21358
  }
20929
21359
  target.path = removeDotSegments(target.path);
20930
21360
  }
20931
- target.query = relative6.query;
21361
+ target.query = relative7.query;
20932
21362
  }
20933
21363
  target.userinfo = base2.userinfo;
20934
21364
  target.host = base2.host;
@@ -20936,7 +21366,7 @@ var require_uri_all = __commonJS((exports, module) => {
20936
21366
  }
20937
21367
  target.scheme = base2.scheme;
20938
21368
  }
20939
- target.fragment = relative6.fragment;
21369
+ target.fragment = relative7.fragment;
20940
21370
  return target;
20941
21371
  }
20942
21372
  function resolve13(baseURI, relativeURI, options2) {
@@ -26215,27 +26645,27 @@ var require_windows = __commonJS((exports, module) => {
26215
26645
  return checkPathExt(path, options2);
26216
26646
  };
26217
26647
  var isexe = function(path, options2, cb) {
26218
- fs33.stat(path, function(er, stat) {
26648
+ fs34.stat(path, function(er, stat) {
26219
26649
  cb(er, er ? false : checkStat(stat, path, options2));
26220
26650
  });
26221
26651
  };
26222
26652
  var sync = function(path, options2) {
26223
- return checkStat(fs33.statSync(path), path, options2);
26653
+ return checkStat(fs34.statSync(path), path, options2);
26224
26654
  };
26225
26655
  module.exports = isexe;
26226
26656
  isexe.sync = sync;
26227
- var fs33 = __require("fs");
26657
+ var fs34 = __require("fs");
26228
26658
  });
26229
26659
 
26230
26660
  // node_modules/isexe/mode.js
26231
26661
  var require_mode = __commonJS((exports, module) => {
26232
26662
  var isexe = function(path, options2, cb) {
26233
- fs33.stat(path, function(er, stat) {
26663
+ fs34.stat(path, function(er, stat) {
26234
26664
  cb(er, er ? false : checkStat(stat, options2));
26235
26665
  });
26236
26666
  };
26237
26667
  var sync = function(path, options2) {
26238
- return checkStat(fs33.statSync(path), options2);
26668
+ return checkStat(fs34.statSync(path), options2);
26239
26669
  };
26240
26670
  var checkStat = function(stat, options2) {
26241
26671
  return stat.isFile() && checkMode(stat, options2);
@@ -26255,7 +26685,7 @@ var require_mode = __commonJS((exports, module) => {
26255
26685
  };
26256
26686
  module.exports = isexe;
26257
26687
  isexe.sync = sync;
26258
- var fs33 = __require("fs");
26688
+ var fs34 = __require("fs");
26259
26689
  });
26260
26690
 
26261
26691
  // node_modules/isexe/index.js
@@ -26300,7 +26730,7 @@ var require_isexe = __commonJS((exports, module) => {
26300
26730
  }
26301
26731
  }
26302
26732
  };
26303
- var fs33 = __require("fs");
26733
+ var fs34 = __require("fs");
26304
26734
  var core2;
26305
26735
  if (process.platform === "win32" || global.TESTING_WINDOWS) {
26306
26736
  core2 = require_windows();
@@ -26507,14 +26937,14 @@ var require_readShebang = __commonJS((exports, module) => {
26507
26937
  const buffer = Buffer.alloc(size);
26508
26938
  let fd;
26509
26939
  try {
26510
- fd = fs33.openSync(command, "r");
26511
- fs33.readSync(fd, buffer, 0, size, 0);
26512
- fs33.closeSync(fd);
26940
+ fd = fs34.openSync(command, "r");
26941
+ fs34.readSync(fd, buffer, 0, size, 0);
26942
+ fs34.closeSync(fd);
26513
26943
  } catch (e) {
26514
26944
  }
26515
26945
  return shebangCommand(buffer.toString());
26516
26946
  };
26517
- var fs33 = __require("fs");
26947
+ var fs34 = __require("fs");
26518
26948
  var shebangCommand = require_shebang_command();
26519
26949
  module.exports = readShebang;
26520
26950
  });
@@ -28698,11 +29128,11 @@ async function detectCommand(options2) {
28698
29128
 
28699
29129
  // dist/commands/sync.js
28700
29130
  init_logger();
28701
- import {relative as relative5} from "path";
29131
+ import {relative as relative6} from "path";
28702
29132
 
28703
29133
  // dist/core/propagator.js
28704
29134
  var import_gray_matter3 = __toESM(require_gray_matter(), 1);
28705
- import {promises as fs28} from "fs";
29135
+ import {promises as fs29} from "fs";
28706
29136
  import {resolve as resolve10} from "path";
28707
29137
 
28708
29138
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -31470,7 +31900,7 @@ class Propagator {
31470
31900
  resolvedTargets: []
31471
31901
  };
31472
31902
  const exists = await fileExists(generatedFile.path);
31473
- const existingStats = exists ? await fs28.lstat(generatedFile.path).catch(() => null) : null;
31903
+ const existingStats = exists ? await fs29.lstat(generatedFile.path).catch(() => null) : null;
31474
31904
  const existingContent = exists && !existingStats?.isSymbolicLink() ? await readFileIfExists(generatedFile.path) : null;
31475
31905
  const existingTarget = existingStats?.isSymbolicLink() ? await readSymlinkTarget(generatedFile.path) : null;
31476
31906
  if (options2.managedState && !options2.dryRun) {
@@ -31501,7 +31931,7 @@ class Propagator {
31501
31931
  if (!options2.dryRun) {
31502
31932
  if (generatedFile.kind === "file") {
31503
31933
  if (existingStats?.isSymbolicLink()) {
31504
- await fs28.rm(generatedFile.path, { force: true }).catch(() => {
31934
+ await fs29.rm(generatedFile.path, { force: true }).catch(() => {
31505
31935
  });
31506
31936
  }
31507
31937
  await writeFile(generatedFile.path, generatedFile.content || "");
@@ -31678,14 +32108,14 @@ ${content}
31678
32108
 
31679
32109
  // dist/core/managedState.js
31680
32110
  init_fs();
31681
- import {promises as fs30} from "fs";
31682
- import {dirname as dirname5, join as join7, relative as relative4, resolve as resolve11} from "path";
32111
+ import {promises as fs31} from "fs";
32112
+ import {dirname as dirname5, join as join8, relative as relative5, resolve as resolve11} from "path";
31683
32113
  var toPosixPath = function(value) {
31684
32114
  return value.replace(/\\/g, "/");
31685
32115
  };
31686
32116
  async function pathType(targetPath) {
31687
32117
  try {
31688
- const stat = await fs30.lstat(targetPath);
32118
+ const stat = await fs31.lstat(targetPath);
31689
32119
  if (stat.isSymbolicLink()) {
31690
32120
  return "symlink";
31691
32121
  }
@@ -31695,20 +32125,20 @@ async function pathType(targetPath) {
31695
32125
  }
31696
32126
  }
31697
32127
  async function copyDirectory(src, dest) {
31698
- await fs30.mkdir(dest, { recursive: true });
31699
- const entries = await fs30.readdir(src, { withFileTypes: true });
32128
+ await fs31.mkdir(dest, { recursive: true });
32129
+ const entries = await fs31.readdir(src, { withFileTypes: true });
31700
32130
  for (const entry of entries) {
31701
- const srcPath = join7(src, entry.name);
31702
- const destPath = join7(dest, entry.name);
32131
+ const srcPath = join8(src, entry.name);
32132
+ const destPath = join8(dest, entry.name);
31703
32133
  if (entry.isDirectory()) {
31704
32134
  await copyDirectory(srcPath, destPath);
31705
32135
  } else if (entry.isSymbolicLink()) {
31706
- const target = await fs30.readlink(srcPath);
31707
- await fs30.mkdir(dirname5(destPath), { recursive: true });
31708
- await fs30.symlink(target, destPath);
32136
+ const target = await fs31.readlink(srcPath);
32137
+ await fs31.mkdir(dirname5(destPath), { recursive: true });
32138
+ await fs31.symlink(target, destPath);
31709
32139
  } else {
31710
- await fs30.mkdir(dirname5(destPath), { recursive: true });
31711
- await fs30.copyFile(srcPath, destPath);
32140
+ await fs31.mkdir(dirname5(destPath), { recursive: true });
32141
+ await fs31.copyFile(srcPath, destPath);
31712
32142
  }
31713
32143
  }
31714
32144
  }
@@ -31717,14 +32147,14 @@ async function copyPath(src, dest) {
31717
32147
  if (!type2) {
31718
32148
  return;
31719
32149
  }
31720
- await fs30.mkdir(dirname5(dest), { recursive: true });
32150
+ await fs31.mkdir(dirname5(dest), { recursive: true });
31721
32151
  if (type2 === "symlink") {
31722
- const target = await fs30.readlink(src);
31723
- await fs30.symlink(target, dest);
32152
+ const target = await fs31.readlink(src);
32153
+ await fs31.symlink(target, dest);
31724
32154
  } else if (type2 === "directory") {
31725
32155
  await copyDirectory(src, dest);
31726
32156
  } else {
31727
- await fs30.copyFile(src, dest);
32157
+ await fs31.copyFile(src, dest);
31728
32158
  }
31729
32159
  }
31730
32160
  var MANAGED_STATE_FILE = "managed-state.json";
@@ -31738,11 +32168,11 @@ class ManagedStateStore {
31738
32168
  this.state = state;
31739
32169
  }
31740
32170
  static async open(projectPath) {
31741
- const agentInitDir = join7(projectPath, ".agentinit");
31742
- const statePath = join7(agentInitDir, MANAGED_STATE_FILE);
32171
+ const agentInitDir = join8(projectPath, ".agentinit");
32172
+ const statePath = join8(agentInitDir, MANAGED_STATE_FILE);
31743
32173
  const emptyState = { version: 1, entries: [] };
31744
32174
  try {
31745
- const raw = await fs30.readFile(statePath, "utf8");
32175
+ const raw = await fs31.readFile(statePath, "utf8");
31746
32176
  const parsed = JSON.parse(raw);
31747
32177
  if (!parsed || parsed.version !== 1 || !Array.isArray(parsed.entries)) {
31748
32178
  return new ManagedStateStore(projectPath, emptyState);
@@ -31753,17 +32183,17 @@ class ManagedStateStore {
31753
32183
  }
31754
32184
  }
31755
32185
  get agentInitDir() {
31756
- return join7(this.projectPath, ".agentinit");
32186
+ return join8(this.projectPath, ".agentinit");
31757
32187
  }
31758
32188
  get stateFilePath() {
31759
- return join7(this.agentInitDir, MANAGED_STATE_FILE);
32189
+ return join8(this.agentInitDir, MANAGED_STATE_FILE);
31760
32190
  }
31761
32191
  get backupsDir() {
31762
- return join7(this.agentInitDir, BACKUPS_DIR);
32192
+ return join8(this.agentInitDir, BACKUPS_DIR);
31763
32193
  }
31764
32194
  normalizeRelativePath(targetPath, preserveTrailingSlash = false) {
31765
32195
  const hasTrailingSlash = preserveTrailingSlash && /[\\/]$/.test(targetPath);
31766
- const relativePath = relative4(this.projectPath, resolve11(targetPath));
32196
+ const relativePath = relative5(this.projectPath, resolve11(targetPath));
31767
32197
  const normalizedPath = toPosixPath(relativePath);
31768
32198
  if (hasTrailingSlash && normalizedPath) {
31769
32199
  return normalizedPath.endsWith("/") ? normalizedPath : `${normalizedPath}/`;
@@ -31781,14 +32211,14 @@ class ManagedStateStore {
31781
32211
  }
31782
32212
  if (type2 === "symlink") {
31783
32213
  try {
31784
- const backupLinkTarget = await fs30.readlink(targetPath);
32214
+ const backupLinkTarget = await fs31.readlink(targetPath);
31785
32215
  return { backupLinkTarget };
31786
32216
  } catch {
31787
32217
  return {};
31788
32218
  }
31789
32219
  }
31790
32220
  const relativeTargetPath = this.normalizeRelativePath(targetPath);
31791
- const backupPath = join7(this.backupsDir, relativeTargetPath);
32221
+ const backupPath = join8(this.backupsDir, relativeTargetPath);
31792
32222
  if (!await fileExists(backupPath)) {
31793
32223
  await copyPath(targetPath, backupPath);
31794
32224
  }
@@ -31835,8 +32265,8 @@ class ManagedStateStore {
31835
32265
  return [...paths6];
31836
32266
  }
31837
32267
  async save() {
31838
- await fs30.mkdir(this.agentInitDir, { recursive: true });
31839
- await fs30.writeFile(this.stateFilePath, JSON.stringify(this.state, null, 2), "utf8");
32268
+ await fs31.mkdir(this.agentInitDir, { recursive: true });
32269
+ await fs31.writeFile(this.stateFilePath, JSON.stringify(this.state, null, 2), "utf8");
31840
32270
  }
31841
32271
  async revertAll(options2 = {}) {
31842
32272
  const summary = {
@@ -31851,29 +32281,29 @@ class ManagedStateStore {
31851
32281
  const backupLinkTarget = entry.backupLinkTarget;
31852
32282
  if (entry.existedBefore && backupLinkTarget !== undefined) {
31853
32283
  if (!options2.dryRun) {
31854
- await fs30.rm(absolutePath, { recursive: true, force: true }).catch(() => {
32284
+ await fs31.rm(absolutePath, { recursive: true, force: true }).catch(() => {
31855
32285
  });
31856
- await fs30.mkdir(dirname5(absolutePath), { recursive: true });
31857
- await fs30.symlink(backupLinkTarget, absolutePath);
32286
+ await fs31.mkdir(dirname5(absolutePath), { recursive: true });
32287
+ await fs31.symlink(backupLinkTarget, absolutePath);
31858
32288
  }
31859
32289
  summary.restored++;
31860
32290
  } else if (entry.existedBefore && backupPath && await fileExists(backupPath)) {
31861
32291
  if (!options2.dryRun) {
31862
- await fs30.rm(absolutePath, { recursive: true, force: true }).catch(() => {
32292
+ await fs31.rm(absolutePath, { recursive: true, force: true }).catch(() => {
31863
32293
  });
31864
32294
  await copyPath(backupPath, absolutePath);
31865
32295
  }
31866
32296
  summary.restored++;
31867
32297
  } else {
31868
32298
  if (!options2.dryRun) {
31869
- await fs30.rm(absolutePath, { recursive: true, force: true }).catch(() => {
32299
+ await fs31.rm(absolutePath, { recursive: true, force: true }).catch(() => {
31870
32300
  });
31871
32301
  }
31872
32302
  summary.removed++;
31873
32303
  }
31874
32304
  if (!options2.keepBackups && backupPath && await fileExists(backupPath)) {
31875
32305
  if (!options2.dryRun) {
31876
- await fs30.rm(backupPath, { recursive: true, force: true }).catch(() => {
32306
+ await fs31.rm(backupPath, { recursive: true, force: true }).catch(() => {
31877
32307
  });
31878
32308
  }
31879
32309
  summary.backupsRemoved++;
@@ -31881,16 +32311,16 @@ class ManagedStateStore {
31881
32311
  }
31882
32312
  if (!options2.dryRun) {
31883
32313
  this.state.entries.length = 0;
31884
- await fs30.rm(this.stateFilePath, { force: true }).catch(() => {
32314
+ await fs31.rm(this.stateFilePath, { force: true }).catch(() => {
31885
32315
  });
31886
32316
  if (!options2.keepBackups) {
31887
- await fs30.rm(this.backupsDir, { recursive: true, force: true }).catch(() => {
32317
+ await fs31.rm(this.backupsDir, { recursive: true, force: true }).catch(() => {
31888
32318
  });
31889
32319
  }
31890
32320
  try {
31891
- const remainingEntries = await fs30.readdir(this.agentInitDir);
32321
+ const remainingEntries = await fs31.readdir(this.agentInitDir);
31892
32322
  if (remainingEntries.length === 0) {
31893
- await fs30.rm(this.agentInitDir, { recursive: true, force: true });
32323
+ await fs31.rm(this.agentInitDir, { recursive: true, force: true });
31894
32324
  }
31895
32325
  } catch {
31896
32326
  }
@@ -31940,7 +32370,7 @@ async function syncCommand(options2) {
31940
32370
  for (const change of result.changes) {
31941
32371
  const action = change.action === "created" ? "\u2795" : change.action === "updated" ? "\uD83D\uDCDD" : "\uD83D\uDCBE";
31942
32372
  const names = change.agents.map((id) => agentManager5.getAgentById(id)?.name || id).join(", ");
31943
- logger.info(` ${action} ${relative5(cwd, change.file) || change.file}`);
32373
+ logger.info(` ${action} ${relative6(cwd, change.file) || change.file}`);
31944
32374
  logger.info(` Agents: ${names}`);
31945
32375
  }
31946
32376
  if (options2.backup && result.changes.some((c) => c.action === "backed_up")) {
@@ -31971,7 +32401,7 @@ async function syncCommand(options2) {
31971
32401
  // dist/commands/apply.js
31972
32402
  init_colors();
31973
32403
  init_logger();
31974
- import {relative as relative6} from "path";
32404
+ import {relative as relative7} from "path";
31975
32405
 
31976
32406
  // dist/types/index.js
31977
32407
  var MCPServerType;
@@ -32237,12 +32667,12 @@ class MCPParser {
32237
32667
  // dist/constants/mcp.js
32238
32668
  import {readFileSync as readFileSync2} from "fs";
32239
32669
  import {fileURLToPath} from "url";
32240
- import {dirname as dirname6, join as join8} from "path";
32670
+ import {dirname as dirname6, join as join9} from "path";
32241
32671
  var getPackageVersion = function() {
32242
32672
  try {
32243
32673
  const __filename2 = fileURLToPath(import.meta.url);
32244
32674
  const __dirname2 = dirname6(__filename2);
32245
- const packageJsonPath = join8(__dirname2, "../../package.json");
32675
+ const packageJsonPath = join9(__dirname2, "../../package.json");
32246
32676
  const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
32247
32677
  return packageJson.version || "1.0.0";
32248
32678
  } catch {
@@ -40515,11 +40945,11 @@ class MCPVerifier {
40515
40945
  }
40516
40946
 
40517
40947
  // dist/core/gitignoreManager.js
40518
- import {promises as fs33} from "fs";
40519
- import {dirname as dirname8, join as join9} from "path";
40948
+ import {promises as fs34} from "fs";
40949
+ import {dirname as dirname8, join as join10} from "path";
40520
40950
  var normalizeIgnorePath = function(projectPath, value) {
40521
- const relative6 = value.startsWith(projectPath) ? value.slice(projectPath.length + 1) : value;
40522
- const normalized = relative6.replace(/\\/g, "/").replace(/^\/+/, "");
40951
+ const relative7 = value.startsWith(projectPath) ? value.slice(projectPath.length + 1) : value;
40952
+ const normalized = relative7.replace(/\\/g, "/").replace(/^\/+/, "");
40523
40953
  if (!normalized) {
40524
40954
  return normalized;
40525
40955
  }
@@ -40563,12 +40993,12 @@ var updateManagedBlock = function(existingContent, entries) {
40563
40993
  return content;
40564
40994
  };
40565
40995
  async function updateManagedIgnoreFile(projectPath, paths6, options2 = {}) {
40566
- const ignoreFile = options2.local ? join9(".git", "info", "exclude") : ".gitignore";
40567
- const ignoreFilePath = join9(projectPath, ignoreFile);
40996
+ const ignoreFile = options2.local ? join10(".git", "info", "exclude") : ".gitignore";
40997
+ const ignoreFilePath = join10(projectPath, ignoreFile);
40568
40998
  if (options2.local) {
40569
- const gitDir = join9(projectPath, ".git");
40999
+ const gitDir = join10(projectPath, ".git");
40570
41000
  try {
40571
- const stat = await fs33.stat(gitDir);
41001
+ const stat = await fs34.stat(gitDir);
40572
41002
  if (!stat.isDirectory()) {
40573
41003
  throw new Error;
40574
41004
  }
@@ -40579,21 +41009,21 @@ async function updateManagedIgnoreFile(projectPath, paths6, options2 = {}) {
40579
41009
  const normalizedPaths = [...new Set(paths6.map((path) => normalizeIgnorePath(projectPath, path)).filter(Boolean))].sort();
40580
41010
  let existingContent = "";
40581
41011
  try {
40582
- existingContent = await fs33.readFile(ignoreFilePath, "utf8");
41012
+ existingContent = await fs34.readFile(ignoreFilePath, "utf8");
40583
41013
  } catch {
40584
41014
  existingContent = "";
40585
41015
  }
40586
41016
  const updatedContent = updateManagedBlock(existingContent, normalizedPaths);
40587
- await fs33.mkdir(dirname8(ignoreFilePath), { recursive: true });
40588
- await fs33.writeFile(ignoreFilePath, updatedContent, "utf8");
41017
+ await fs34.mkdir(dirname8(ignoreFilePath), { recursive: true });
41018
+ await fs34.writeFile(ignoreFilePath, updatedContent, "utf8");
40589
41019
  return ignoreFilePath;
40590
41020
  }
40591
41021
  async function removeManagedIgnoreBlock(projectPath, options2 = {}) {
40592
- const ignoreFile = options2.local ? join9(".git", "info", "exclude") : ".gitignore";
40593
- const ignoreFilePath = join9(projectPath, ignoreFile);
41022
+ const ignoreFile = options2.local ? join10(".git", "info", "exclude") : ".gitignore";
41023
+ const ignoreFilePath = join10(projectPath, ignoreFile);
40594
41024
  let content;
40595
41025
  try {
40596
- content = await fs33.readFile(ignoreFilePath, "utf8");
41026
+ content = await fs34.readFile(ignoreFilePath, "utf8");
40597
41027
  } catch {
40598
41028
  return false;
40599
41029
  }
@@ -40609,13 +41039,13 @@ async function removeManagedIgnoreBlock(projectPath, options2 = {}) {
40609
41039
  const afterBlock = content.slice(endIndex + END_MARKER.length).replace(/^\n+/, "");
40610
41040
  let nextContent = `${beforeBlock}${afterBlock}`.replace(/\n{3,}/g, "\n\n").replace(/^\n+/, "");
40611
41041
  if (nextContent.trim() === "") {
40612
- await fs33.rm(ignoreFilePath, { force: true }).catch(() => {
41042
+ await fs34.rm(ignoreFilePath, { force: true }).catch(() => {
40613
41043
  });
40614
41044
  } else {
40615
41045
  if (!nextContent.endsWith("\n")) {
40616
41046
  nextContent += "\n";
40617
41047
  }
40618
- await fs33.writeFile(ignoreFilePath, nextContent, "utf8");
41048
+ await fs34.writeFile(ignoreFilePath, nextContent, "utf8");
40619
41049
  }
40620
41050
  return true;
40621
41051
  }
@@ -40626,12 +41056,12 @@ var END_MARKER = "# END AgentInit Generated Files";
40626
41056
  init_agentManager();
40627
41057
  init_skillsManager();
40628
41058
  init_fs();
40629
- import {dirname as dirname9, join as join10} from "path";
41059
+ import {dirname as dirname9, join as join11} from "path";
40630
41060
  async function discoverProjectSkills(projectPath, skillsManager4) {
40631
41061
  const sources = [];
40632
41062
  const skills2 = new Map;
40633
41063
  for (const sourceDir of PROJECT_SKILL_SOURCE_DIRS) {
40634
- const absoluteSourceDir = join10(projectPath, sourceDir);
41064
+ const absoluteSourceDir = join11(projectPath, sourceDir);
40635
41065
  if (!await fileExists(absoluteSourceDir)) {
40636
41066
  continue;
40637
41067
  }
@@ -40815,7 +41245,7 @@ async function applyProjectCommand(options2) {
40815
41245
  if (change.action === "backed_up")
40816
41246
  return;
40817
41247
  const names = change.agents.map((id) => agentManager7.getAgentById(id)?.name || id).join(", ");
40818
- logger.info(` ${relative6(cwd, change.file) || change.file}`);
41248
+ logger.info(` ${relative7(cwd, change.file) || change.file}`);
40819
41249
  logger.info(` Agents: ${names}`);
40820
41250
  });
40821
41251
  }
@@ -40833,7 +41263,7 @@ async function applyProjectCommand(options2) {
40833
41263
  installsByPath.set(item.path, existing);
40834
41264
  }
40835
41265
  for (const [path, details] of installsByPath) {
40836
- logger.info(` ${relative6(cwd, path) || path}`);
41266
+ logger.info(` ${relative7(cwd, path) || path}`);
40837
41267
  logger.info(` Agents: ${[...details.agents].join(", ")}`);
40838
41268
  logger.info(` Skills: ${[...details.skills].join(", ")}`);
40839
41269
  }
@@ -41720,7 +42150,7 @@ var import_prompts3 = __toESM(require_prompts3(), 1);
41720
42150
  init_colors();
41721
42151
  init_logger();
41722
42152
  import {homedir as homedir7} from "os";
41723
- import {relative as relative7, resolve as resolve13} from "path";
42153
+ import {relative as relative8, resolve as resolve13} from "path";
41724
42154
 
41725
42155
  // dist/utils/promptUtils.js
41726
42156
  var import_prompts2 = __toESM(require_prompts3(), 1);
@@ -41739,7 +42169,22 @@ function enableUppercaseToggleAllForMultiselectPrompt() {
41739
42169
  }
41740
42170
  const originalHandler = prototype._;
41741
42171
  prototype._ = function agentinitMultiselectHandler(input, key) {
41742
- return originalHandler.call(this, input === "A" ? "a" : input, key);
42172
+ if (this.__agentinitHotkeyBusy) {
42173
+ return;
42174
+ }
42175
+ const normalizedInput = input === "A" ? "a" : input;
42176
+ const hotkeyHandler = this.__agentinitHotkeys?.[normalizedInput] || this.__agentinitHotkeys?.[input];
42177
+ if (hotkeyHandler) {
42178
+ this.__agentinitHotkeyBusy = true;
42179
+ Promise.resolve(hotkeyHandler(this, normalizedInput, key)).finally(() => {
42180
+ this.__agentinitHotkeyBusy = false;
42181
+ if (typeof this.render === "function") {
42182
+ this.render();
42183
+ }
42184
+ });
42185
+ return;
42186
+ }
42187
+ return originalHandler.call(this, normalizedInput, key);
41743
42188
  };
41744
42189
  Object.defineProperty(prototype, "__agentinitUppercaseToggleAllPatched", {
41745
42190
  value: true,
@@ -41752,12 +42197,52 @@ function enableUppercaseToggleAllForMultiselectPrompt() {
41752
42197
  }
41753
42198
  async function promptMultiselect(options2) {
41754
42199
  enableUppercaseToggleAllForMultiselectPrompt();
41755
- return import_prompts2.default({
42200
+ const userOnRender = options2.onRenderPrompt;
42201
+ const hint = options2.hint;
42202
+ let requestedAction;
42203
+ let promptInstance;
42204
+ const closeWithCurrentSelection = (prompt) => {
42205
+ if (typeof prompt.close !== "function") {
42206
+ return false;
42207
+ }
42208
+ prompt.done = true;
42209
+ prompt.aborted = false;
42210
+ prompt.fire?.();
42211
+ prompt.render?.();
42212
+ prompt.out?.write("\n");
42213
+ prompt.close();
42214
+ return true;
42215
+ };
42216
+ const hotkeys = options2.hotkeys ? Object.fromEntries(Object.entries(options2.hotkeys).map(([key, handler]) => [
42217
+ key,
42218
+ (prompt, input, pressedKey) => handler({
42219
+ prompt,
42220
+ requestAction: (action) => {
42221
+ requestedAction = action;
42222
+ },
42223
+ closeWithCurrentSelection: () => closeWithCurrentSelection(prompt)
42224
+ }, input, pressedKey)
42225
+ ])) : undefined;
42226
+ const response = await import_prompts2.default({
41756
42227
  ...options2,
41757
42228
  type: "multiselect",
41758
42229
  instructions: options2.instructions ?? false,
41759
- hint: options2.hint ?? MULTISELECT_TOGGLE_ALL_HINT
42230
+ hint: typeof hint === "function" ? hint() : hint ?? MULTISELECT_TOGGLE_ALL_HINT,
42231
+ onRender() {
42232
+ promptInstance = this;
42233
+ if (hotkeys) {
42234
+ this.__agentinitHotkeys = hotkeys;
42235
+ } else {
42236
+ delete this.__agentinitHotkeys;
42237
+ }
42238
+ this.hint = typeof hint === "function" ? hint() : hint ?? MULTISELECT_TOGGLE_ALL_HINT;
42239
+ userOnRender?.(this);
42240
+ }
41760
42241
  });
42242
+ if (requestedAction) {
42243
+ response.__agentinitAction = requestedAction;
42244
+ }
42245
+ return response;
41761
42246
  }
41762
42247
  async function selectBundlePlugins(entries, actionLabel, options2 = {}) {
41763
42248
  if (options2.selectAll) {
@@ -41792,11 +42277,84 @@ init_marketplaceRegistry();
41792
42277
  init_agentManager();
41793
42278
  init_installLock();
41794
42279
  init_fs();
42280
+
42281
+ // dist/utils/lockSource.js
42282
+ function lockSourceToSpecifier(source) {
42283
+ if (source.type === "marketplace" && source.marketplace && source.pluginName) {
42284
+ return {
42285
+ source: source.pluginName,
42286
+ from: source.marketplace,
42287
+ ...source.prefix ? { prefix: source.prefix } : {}
42288
+ };
42289
+ }
42290
+ if (source.type === "github") {
42291
+ if (source.owner && source.repo) {
42292
+ return {
42293
+ source: source.subpath ? `${source.owner}/${source.repo}/${source.subpath}` : `${source.owner}/${source.repo}`,
42294
+ ...source.prefix ? { prefix: source.prefix } : {}
42295
+ };
42296
+ }
42297
+ if (source.url) {
42298
+ return { source: source.url, ...source.prefix ? { prefix: source.prefix } : {} };
42299
+ }
42300
+ }
42301
+ if (source.type === "gitlab") {
42302
+ if (source.owner && source.repo) {
42303
+ const prefix = `gitlab:${source.owner}/${source.repo}`;
42304
+ return {
42305
+ source: source.subpath ? `${prefix}//${source.subpath}` : prefix,
42306
+ ...source.prefix ? { prefix: source.prefix } : {}
42307
+ };
42308
+ }
42309
+ if (source.url) {
42310
+ return { source: source.url, ...source.prefix ? { prefix: source.prefix } : {} };
42311
+ }
42312
+ }
42313
+ if (source.type === "bitbucket") {
42314
+ if (source.owner && source.repo) {
42315
+ const prefix = `bitbucket:${source.owner}/${source.repo}`;
42316
+ return {
42317
+ source: source.subpath ? `${prefix}/${source.subpath}` : prefix,
42318
+ ...source.prefix ? { prefix: source.prefix } : {}
42319
+ };
42320
+ }
42321
+ if (source.url) {
42322
+ return { source: source.url, ...source.prefix ? { prefix: source.prefix } : {} };
42323
+ }
42324
+ }
42325
+ if (source.type === "local" && source.path) {
42326
+ return { source: source.path, ...source.prefix ? { prefix: source.prefix } : {} };
42327
+ }
42328
+ return null;
42329
+ }
42330
+ function formatLockSource(source) {
42331
+ const specifier = lockSourceToSpecifier(source);
42332
+ if (!specifier) {
42333
+ return source.type;
42334
+ }
42335
+ const formatted = specifier.from ? `${specifier.from}/${specifier.source}` : specifier.source;
42336
+ return specifier.prefix ? `${formatted} [prefix: ${specifier.prefix}]` : formatted;
42337
+ }
42338
+
42339
+ // dist/commands/skills.js
41795
42340
  init_skills();
42341
+ var normalizeSkillPrefix = function(prefix) {
42342
+ return prefix?.trim() ?? "";
42343
+ };
42344
+ var matchesSelectedSkillName = function(skillName, selectedNames, prefix) {
42345
+ if (selectedNames.has(skillName.toLowerCase())) {
42346
+ return true;
42347
+ }
42348
+ const normalizedPrefix = normalizeSkillPrefix(prefix);
42349
+ if (!normalizedPrefix) {
42350
+ return false;
42351
+ }
42352
+ return selectedNames.has(`${normalizedPrefix}${skillName}`.toLowerCase());
42353
+ };
41796
42354
  function registerSkillsCommand(program2) {
41797
42355
  const marketplaceHelp = getMarketplaceIds().join(", ");
41798
42356
  const skills3 = program2.command("skills").description("Manage agent skills");
41799
- skills3.command("add <source>").description("Add skills from a marketplace, GitHub repo, or local path").option("--from <marketplace>", `Marketplace source override (available: ${marketplaceHelp})`).option("-g, --global", "Install skills globally").option("-a, --agent <agents...>", "Target specific agent(s)").option("-s, --skill <names...>", "Install only specific skills by name").option("-l, --list", "List available skills from the source without installing").option("--all", "Select all bundled plugins when the source contains multiple plugins").option("--copy", "Copy skill files instead of symlinking").option("-y, --yes", "Skip prompts, auto-detect project-configured agents, and apply available skill updates").action(async (source, options2) => {
42357
+ skills3.command("add <source>").description("Add skills from a marketplace, hosted Git repo, or local path").option("--from <marketplace>", `Marketplace source override (available: ${marketplaceHelp})`).option("-g, --global", "Install skills globally").option("-a, --agent <agents...>", "Target specific agent(s)").option("-s, --skill <names...>", "Install only specific skills by name").option("-l, --list", "List available skills from the source without installing").option("--all", "Select all bundled plugins when the source contains multiple plugins").option("--copy", "Copy skill files instead of symlinking").option("--prefix <prefix>", "Prefix installed skill names").option("--no-scan", "Skip security scanning before installation").option("--allow-risky", "Install even if scanning finds high-risk patterns").option("-y, --yes", "Skip prompts, auto-detect project-configured agents, and apply available skill updates").action(async (source, options2) => {
41800
42358
  logger.titleBox("AgentInit Skills");
41801
42359
  const agentManager9 = new AgentManager;
41802
42360
  const skillsManager5 = new SkillsManager(agentManager9);
@@ -41875,11 +42433,12 @@ function registerSkillsCommand(program2) {
41875
42433
  let targetAgents = options2.agent;
41876
42434
  let targetGlobal = options2.global;
41877
42435
  let selectedSkillNames = options2.skill;
42436
+ let installPrefix = options2.prefix;
41878
42437
  if (!options2.yes && (!selectedSkillNames || selectedSkillNames.length === 0) && preparedSkills.length > 1) {
41879
42438
  if (selectedPluginNames && selectedPluginNames.length > 1) {
41880
42439
  logger.info("Multiple bundled plugins selected; installing all skills from each selected plugin. Use --skill to filter by skill name.");
41881
42440
  } else {
41882
- const skillSelection = await resolveInteractiveSkillSelection(preparedSkills);
42441
+ const skillSelection = await resolveInteractiveSkillSelection(preparedSkills, installPrefix);
41883
42442
  if (skillSelection.aborted) {
41884
42443
  await skillsManager5.discardPreparedSource(source, process.cwd(), {
41885
42444
  from: options2.from
@@ -41887,11 +42446,12 @@ function registerSkillsCommand(program2) {
41887
42446
  return;
41888
42447
  }
41889
42448
  selectedSkillNames = skillSelection.skills;
42449
+ installPrefix = skillSelection.prefix;
41890
42450
  }
41891
42451
  }
41892
42452
  if (!targetAgents && !options2.yes) {
41893
42453
  const selectedSkillNameSet = selectedSkillNames && selectedSkillNames.length > 0 ? new Set(selectedSkillNames.map((name) => name.toLowerCase())) : undefined;
41894
- const filteredPreviewSkills = selectedSkillNameSet ? preparedSkills.filter((skill) => selectedSkillNameSet.has(skill.name.toLowerCase())) : preparedSkills;
42454
+ const filteredPreviewSkills = selectedSkillNameSet ? preparedSkills.filter((skill) => matchesSelectedSkillName(skill.name, selectedSkillNameSet, installPrefix)) : preparedSkills;
41895
42455
  const selection = await resolveInteractiveSkillTargets(skillsManager5, agentManager9, source, process.cwd(), {
41896
42456
  from: options2.from,
41897
42457
  global: options2.global,
@@ -41924,7 +42484,10 @@ function registerSkillsCommand(program2) {
41924
42484
  ...targetGlobal !== undefined ? { global: targetGlobal } : {},
41925
42485
  ...targetAgents !== undefined ? { agents: targetAgents } : {},
41926
42486
  ...selectedSkillNames !== undefined ? { skills: selectedSkillNames } : {},
42487
+ ...installPrefix !== undefined ? { prefix: installPrefix } : {},
41927
42488
  ...options2.copy !== undefined ? { copy: options2.copy } : {},
42489
+ ...options2.scan !== undefined ? { scan: options2.scan } : {},
42490
+ ...options2.allowRisky !== undefined ? { allowRisky: options2.allowRisky } : {},
41928
42491
  ...pluginName !== undefined ? { pluginName } : {},
41929
42492
  ...options2.yes !== undefined ? { yes: options2.yes } : {},
41930
42493
  ...confirmUpdate !== undefined ? { confirmUpdate } : {}
@@ -41983,7 +42546,7 @@ function registerSkillsCommand(program2) {
41983
42546
  const mode = skill.mode === "symlink" ? " (canonical)" : "";
41984
42547
  const scope = skill.scope === "global" ? " [global]" : "";
41985
42548
  logger.info(`\n ${green(skill.name)} - ${skill.description}${scope}${mode}`);
41986
- logger.info(` Path: ${relative7(process.cwd(), skill.path) || skill.path}`);
42549
+ logger.info(` Path: ${relative8(process.cwd(), skill.path) || skill.path}`);
41987
42550
  logger.info(` Agents: ${[...skill.agents].join(", ")}`);
41988
42551
  }
41989
42552
  });
@@ -42081,6 +42644,7 @@ Dry run \u2014 no changes made.`));
42081
42644
  const skillsManager5 = new SkillsManager(agentManager9);
42082
42645
  const result = await skillsManager5.addFromSource(sourceString.source, entry.projectPath, {
42083
42646
  ...sourceString.from ? { from: sourceString.from } : {},
42647
+ ...sourceString.prefix ? { prefix: sourceString.prefix } : {},
42084
42648
  agents: entry.agents,
42085
42649
  global: entry.scope === "global",
42086
42650
  skills: [name],
@@ -42147,6 +42711,7 @@ Dry run \u2014 no changes made.`));
42147
42711
  try {
42148
42712
  const result = await skillsManager5.addFromSource(sourceString.source, cwd, {
42149
42713
  ...sourceString.from ? { from: sourceString.from } : {},
42714
+ ...sourceString.prefix ? { prefix: sourceString.prefix } : {},
42150
42715
  agents: entry.agents,
42151
42716
  global: entry.scope === "global",
42152
42717
  skills: [name],
@@ -42197,6 +42762,7 @@ Dry run \u2014 no changes made.`));
42197
42762
  try {
42198
42763
  const result = await skillsManager5.addFromSource(sourceString.source, cwd, {
42199
42764
  ...sourceString.from ? { from: sourceString.from } : {},
42765
+ ...sourceString.prefix ? { prefix: sourceString.prefix } : {},
42200
42766
  agents: entry.agents,
42201
42767
  global: entry.scope === "global",
42202
42768
  skills: [entry.name],
@@ -42218,24 +42784,7 @@ Dry run \u2014 no changes made.`));
42218
42784
  });
42219
42785
  }
42220
42786
  var lockSourceToString = function(source) {
42221
- if (source.type === "marketplace" && source.marketplace && source.pluginName) {
42222
- return { source: source.pluginName, from: source.marketplace };
42223
- }
42224
- if (source.type === "github") {
42225
- if (source.owner && source.repo) {
42226
- if (source.subpath) {
42227
- return { source: `${source.owner}/${source.repo}/${source.subpath}` };
42228
- }
42229
- return { source: `${source.owner}/${source.repo}` };
42230
- }
42231
- if (source.url) {
42232
- return { source: source.url };
42233
- }
42234
- }
42235
- if (source.type === "local" && source.path) {
42236
- return { source: source.path };
42237
- }
42238
- return null;
42787
+ return lockSourceToSpecifier(source);
42239
42788
  };
42240
42789
  async function resolveInteractiveSkillTargets(skillsManager5, agentManager9, source, projectPath, options2) {
42241
42790
  let installGlobal = !!options2.global;
@@ -42309,23 +42858,57 @@ async function resolveInteractiveSkillTargets(skillsManager5, agentManager9, sou
42309
42858
  }
42310
42859
  return selection;
42311
42860
  }
42312
- async function resolveInteractiveSkillSelection(skills3) {
42313
- const response = await promptMultiselect({
42314
- name: "skills",
42315
- message: `Select skills to install (${skills3.length} found):`,
42316
- min: 1,
42317
- choices: skills3.map((skill) => ({
42318
- title: skill.name,
42319
- value: skill.name,
42320
- description: skill.description,
42321
- selected: true
42322
- }))
42323
- });
42324
- if (!response.skills || response.skills.length === 0) {
42325
- logger.info("No skills selected. Aborting.");
42326
- return { aborted: true };
42861
+ var formatSkillSelectionHint = function(prefix) {
42862
+ return `Press Space to select, A to select or deselect all, p to edit prefix, then Enter to confirm. Prefix: "${prefix}"`;
42863
+ };
42864
+ async function resolveInteractiveSkillSelection(skills3, initialPrefix) {
42865
+ let prefix = initialPrefix ?? "";
42866
+ let selectedSkills = new Set(skills3.map((skill) => skill.name));
42867
+ const promptForPrefix = async () => {
42868
+ const response = await import_prompts3.default({
42869
+ type: "text",
42870
+ name: "prefix",
42871
+ message: "Prefix to prepend to installed skill names:",
42872
+ initial: prefix
42873
+ });
42874
+ if (typeof response.prefix === "string") {
42875
+ prefix = response.prefix;
42876
+ }
42877
+ };
42878
+ const requestPrefixEdit = (controls) => {
42879
+ controls.requestAction("edit-prefix");
42880
+ controls.closeWithCurrentSelection();
42881
+ };
42882
+ while (true) {
42883
+ const response = await promptMultiselect({
42884
+ name: "skills",
42885
+ message: `Select skills to install (${skills3.length} found):`,
42886
+ min: 1,
42887
+ hint: () => formatSkillSelectionHint(prefix),
42888
+ hotkeys: {
42889
+ p: requestPrefixEdit,
42890
+ P: requestPrefixEdit
42891
+ },
42892
+ choices: skills3.map((skill) => ({
42893
+ title: skill.name,
42894
+ value: skill.name,
42895
+ description: skill.description,
42896
+ selected: selectedSkills.has(skill.name)
42897
+ }))
42898
+ });
42899
+ if (response.skills && response.skills.length > 0) {
42900
+ selectedSkills = new Set(response.skills);
42901
+ }
42902
+ if (response.__agentinitAction === "edit-prefix") {
42903
+ await promptForPrefix();
42904
+ continue;
42905
+ }
42906
+ if (!response.skills || response.skills.length === 0) {
42907
+ logger.info("No skills selected. Aborting.");
42908
+ return { aborted: true };
42909
+ }
42910
+ return { skills: response.skills, prefix };
42327
42911
  }
42328
- return { skills: response.skills };
42329
42912
  }
42330
42913
  async function getDetectedSkillGroups(agentManager9, projectPath, global3) {
42331
42914
  const detectedAgents = (await agentManager9.detectAgents(projectPath)).map((entry) => entry.agent);
@@ -42421,7 +43004,7 @@ var formatSkillsDir = function(projectPath, dir) {
42421
43004
  const normalizedProjectPath = projectPath.replace(/\\/g, "/");
42422
43005
  const normalizedHome = homedir7().replace(/\\/g, "/");
42423
43006
  if (normalizedDir.startsWith(`${normalizedProjectPath}/`)) {
42424
- return `${relative7(projectPath, dir).replace(/\\/g, "/").replace(/\/?$/, "/")}`;
43007
+ return `${relative8(projectPath, dir).replace(/\\/g, "/").replace(/\/?$/, "/")}`;
42425
43008
  }
42426
43009
  if (normalizedDir.startsWith(`${normalizedHome}/`)) {
42427
43010
  return normalizedDir.replace(normalizedHome, "~");
@@ -42633,7 +43216,7 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
42633
43216
  byPath.set(path, existing);
42634
43217
  }
42635
43218
  for (const [path, details] of byPath) {
42636
- logger.info(` ${relative7(process.cwd(), path) || path}`);
43219
+ logger.info(` ${relative8(process.cwd(), path) || path}`);
42637
43220
  logger.info(` Agents: ${[...details.agents].join(", ")}`);
42638
43221
  logger.info(` Skills: ${green(String(details.skills.size))} installed (${[...details.skills].join(", ")})`);
42639
43222
  }
@@ -42649,7 +43232,7 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
42649
43232
  byPathUpdated.set(path, existing);
42650
43233
  }
42651
43234
  for (const [path, details] of byPathUpdated) {
42652
- logger.info(` ${relative7(process.cwd(), path) || path}`);
43235
+ logger.info(` ${relative8(process.cwd(), path) || path}`);
42653
43236
  logger.info(` Agents: ${[...details.agents].join(", ")}`);
42654
43237
  logger.info(` Skills: ${yellow(String(details.skills.size))} updated (${[...details.skills].join(", ")})`);
42655
43238
  }
@@ -42875,7 +43458,7 @@ var registerAddCommand = function(mcp) {
42875
43458
  });
42876
43459
  try {
42877
43460
  const lock = new InstallLock;
42878
- const lockSource = { type: "local" };
43461
+ const lockSource2 = { type: "local" };
42879
43462
  for (const plan of plans) {
42880
43463
  for (const server of plan.servers) {
42881
43464
  await lock.recordMcp({
@@ -42884,7 +43467,7 @@ var registerAddCommand = function(mcp) {
42884
43467
  projectPath: cwd,
42885
43468
  agents: [plan.agent.id],
42886
43469
  scope: plan.isGlobal ? "global" : "project",
42887
- source: lockSource,
43470
+ source: lockSource2,
42888
43471
  configPath: plan.configPath,
42889
43472
  serverType: server.type,
42890
43473
  ...server.command ? { command: server.command } : {},
@@ -43572,7 +44155,7 @@ function registerRulesCommand(program2) {
43572
44155
  // dist/commands/plugins.js
43573
44156
  var import_prompts4 = __toESM(require_prompts3(), 1);
43574
44157
  import {homedir as homedir8} from "os";
43575
- import {dirname as dirname10, relative as relative8, resolve as resolve14} from "path";
44158
+ import {dirname as dirname10, relative as relative9, resolve as resolve14} from "path";
43576
44159
  init_logger();
43577
44160
  init_pluginManager();
43578
44161
  init_agentManager();
@@ -43925,7 +44508,7 @@ function registerPluginsCommand(program2) {
43925
44508
  }
43926
44509
  var formatPathForDisplay = function(pathValue, projectPath) {
43927
44510
  if (pathValue.startsWith(`${projectPath}/`)) {
43928
- return relative8(projectPath, pathValue) || ".";
44511
+ return relative9(projectPath, pathValue) || ".";
43929
44512
  }
43930
44513
  const homePrefix = `${process.env.HOME || ""}/`;
43931
44514
  if (homePrefix !== "/" && pathValue.startsWith(homePrefix)) {
@@ -44327,20 +44910,7 @@ var formatKind = function(kind) {
44327
44910
  }
44328
44911
  };
44329
44912
  var formatSource = function(entry) {
44330
- const src = entry.source;
44331
- if (src.type === "marketplace" && src.marketplace) {
44332
- return `${src.marketplace}/${src.pluginName || ""}`;
44333
- }
44334
- if (src.type === "github") {
44335
- if (src.owner && src.repo) {
44336
- return src.subpath ? `${src.owner}/${src.repo}/${src.subpath}` : `${src.owner}/${src.repo}`;
44337
- }
44338
- return src.url || "github";
44339
- }
44340
- if (src.type === "local" && src.path) {
44341
- return src.path;
44342
- }
44343
- return src.type;
44913
+ return formatLockSource(entry.source);
44344
44914
  };
44345
44915
  function registerLockCommand(program2) {
44346
44916
  const lock = program2.command("lock").description("View and manage the global install lockfile");