allagents 1.11.4-next.1 → 1.11.6-next.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.
Files changed (2) hide show
  1. package/dist/index.js +1978 -1623
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15693,13 +15693,14 @@ var init_esm = __esm(() => {
15693
15693
  });
15694
15694
 
15695
15695
  // src/core/git-errors.ts
15696
- function classifyError(error, url) {
15696
+ function classifyError(error, url, timeoutMs = 60000) {
15697
15697
  const errorMessage = error instanceof Error ? error.message : String(error);
15698
15698
  const isTimeout = errorMessage.includes("block timeout") || errorMessage.includes("timed out");
15699
15699
  const isAuthError = errorMessage.includes("Authentication failed") || errorMessage.includes("could not read Username") || errorMessage.includes("Permission denied") || errorMessage.includes("Repository not found");
15700
15700
  const isServerError = /returned error: 5\d\d/.test(errorMessage) || errorMessage.includes("Internal Server Error");
15701
15701
  if (isTimeout) {
15702
- return new GitCloneError(`Clone timed out after 60s for ${url}.
15702
+ const timeoutSeconds = Math.round(timeoutMs / 1000);
15703
+ return new GitCloneError(`Clone timed out after ${timeoutSeconds}s for ${url}.
15703
15704
  Check your network connection and repository access.
15704
15705
  For SSH: ssh-add -l (to check loaded keys)
15705
15706
  For HTTPS: Check your git credentials`, url, true, false);
@@ -15732,38 +15733,50 @@ var init_git_errors = __esm(() => {
15732
15733
  import { mkdtemp, rm } from "node:fs/promises";
15733
15734
  import { tmpdir } from "node:os";
15734
15735
  import { join, normalize as normalize2, resolve, sep } from "node:path";
15736
+ function createGit(baseDir) {
15737
+ return esm_default(baseDir, {
15738
+ timeout: { block: CLONE_TIMEOUT_MS },
15739
+ config: [
15740
+ "filter.lfs.required=false",
15741
+ "filter.lfs.smudge=",
15742
+ "filter.lfs.clean=",
15743
+ "filter.lfs.process="
15744
+ ]
15745
+ }).env({
15746
+ GIT_TERMINAL_PROMPT: "0",
15747
+ GIT_LFS_SKIP_SMUDGE: "1"
15748
+ });
15749
+ }
15735
15750
  function gitHubUrl(owner, repo) {
15736
15751
  return `https://github.com/${owner}/${repo}.git`;
15737
15752
  }
15738
15753
  async function cloneToTemp(url, ref) {
15739
15754
  const tempDir = await mkdtemp(join(tmpdir(), "allagents-"));
15740
- const git = esm_default({ timeout: { block: CLONE_TIMEOUT_MS } });
15755
+ const git = createGit();
15741
15756
  const cloneOptions = ref ? ["--depth", "1", "--branch", ref] : ["--depth", "1"];
15742
15757
  try {
15743
15758
  await git.clone(url, tempDir, cloneOptions);
15744
15759
  return tempDir;
15745
15760
  } catch (error) {
15746
15761
  await rm(tempDir, { recursive: true, force: true }).catch(() => {});
15747
- throw classifyError(error, url);
15762
+ throw classifyError(error, url, CLONE_TIMEOUT_MS);
15748
15763
  }
15749
15764
  }
15750
15765
  async function cloneTo(url, dest, ref) {
15751
- const git = esm_default({ timeout: { block: CLONE_TIMEOUT_MS } });
15766
+ const git = createGit();
15752
15767
  const cloneOptions = ref ? ["--depth", "1", "--branch", ref] : ["--depth", "1"];
15753
15768
  try {
15754
15769
  await git.clone(url, dest, cloneOptions);
15755
15770
  } catch (error) {
15756
- throw classifyError(error, url);
15771
+ throw classifyError(error, url, CLONE_TIMEOUT_MS);
15757
15772
  }
15758
15773
  }
15759
15774
  async function pull(repoPath) {
15760
- const git = esm_default(repoPath, {
15761
- timeout: { block: CLONE_TIMEOUT_MS }
15762
- });
15775
+ const git = createGit(repoPath);
15763
15776
  await git.pull();
15764
15777
  }
15765
15778
  async function repoExists(url) {
15766
- const git = esm_default({ timeout: { block: CLONE_TIMEOUT_MS } });
15779
+ const git = createGit();
15767
15780
  try {
15768
15781
  await git.listRemote([url]);
15769
15782
  return true;
@@ -15772,7 +15785,7 @@ async function repoExists(url) {
15772
15785
  }
15773
15786
  }
15774
15787
  async function refExists(url, ref) {
15775
- const git = esm_default({ timeout: { block: CLONE_TIMEOUT_MS } });
15788
+ const git = createGit();
15776
15789
  try {
15777
15790
  const result = await git.listRemote([
15778
15791
  "--refs",
@@ -15792,10 +15805,17 @@ async function cleanupTempDir(dir) {
15792
15805
  }
15793
15806
  await rm(dir, { recursive: true, force: true });
15794
15807
  }
15795
- var CLONE_TIMEOUT_MS = 60000;
15808
+ var DEFAULT_CLONE_TIMEOUT_MS = 300000, CLONE_TIMEOUT_MS;
15796
15809
  var init_git = __esm(() => {
15797
15810
  init_esm();
15798
15811
  init_git_errors();
15812
+ CLONE_TIMEOUT_MS = (() => {
15813
+ const raw = process.env.ALLAGENTS_CLONE_TIMEOUT_MS;
15814
+ if (!raw)
15815
+ return DEFAULT_CLONE_TIMEOUT_MS;
15816
+ const parsed = Number.parseInt(raw, 10);
15817
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CLONE_TIMEOUT_MS;
15818
+ })();
15799
15819
  });
15800
15820
 
15801
15821
  // src/utils/plugin-path.ts
@@ -25580,794 +25600,939 @@ var init_skill = __esm(() => {
25580
25600
  import_gray_matter = __toESM(require_gray_matter(), 1);
25581
25601
  });
25582
25602
 
25583
- // src/core/transform.ts
25584
- import { readFile as readFile3, writeFile, mkdir as mkdir3, cp, readdir as readdir2 } from "node:fs/promises";
25603
+ // src/models/marketplace-manifest.ts
25604
+ var UrlSourceSchema, GitHubSourceSchema, PluginSourceRefSchema, ContactSchema, LspServerSchema, MarketplacePluginEntrySchema, MarketplaceManifestSchema, MarketplaceManifestLenientSchema;
25605
+ var init_marketplace_manifest = __esm(() => {
25606
+ init_zod();
25607
+ UrlSourceSchema = exports_external.object({
25608
+ source: exports_external.literal("url"),
25609
+ url: exports_external.string().url()
25610
+ });
25611
+ GitHubSourceSchema = exports_external.object({
25612
+ source: exports_external.literal("github"),
25613
+ repo: exports_external.string().min(1)
25614
+ }).transform((val) => ({
25615
+ source: "url",
25616
+ url: `https://github.com/${val.repo}`
25617
+ }));
25618
+ PluginSourceRefSchema = exports_external.union([exports_external.string(), UrlSourceSchema, GitHubSourceSchema]);
25619
+ ContactSchema = exports_external.object({
25620
+ name: exports_external.string(),
25621
+ email: exports_external.string().optional()
25622
+ });
25623
+ LspServerSchema = exports_external.object({
25624
+ command: exports_external.string(),
25625
+ args: exports_external.array(exports_external.string()).optional(),
25626
+ extensionToLanguage: exports_external.record(exports_external.string()).optional(),
25627
+ startupTimeout: exports_external.number().optional()
25628
+ });
25629
+ MarketplacePluginEntrySchema = exports_external.object({
25630
+ name: exports_external.string().min(1),
25631
+ description: exports_external.string().min(1),
25632
+ source: PluginSourceRefSchema,
25633
+ version: exports_external.string().optional(),
25634
+ author: ContactSchema.optional(),
25635
+ category: exports_external.string().optional(),
25636
+ homepage: exports_external.string().optional(),
25637
+ strict: exports_external.boolean().optional(),
25638
+ tags: exports_external.array(exports_external.string()).optional(),
25639
+ skills: exports_external.array(exports_external.string()).optional(),
25640
+ lspServers: exports_external.record(LspServerSchema).optional()
25641
+ });
25642
+ MarketplaceManifestSchema = exports_external.object({
25643
+ $schema: exports_external.string().optional(),
25644
+ name: exports_external.string().min(1),
25645
+ version: exports_external.string().optional(),
25646
+ description: exports_external.string().min(1),
25647
+ owner: ContactSchema.optional(),
25648
+ plugins: exports_external.array(MarketplacePluginEntrySchema)
25649
+ });
25650
+ MarketplaceManifestLenientSchema = exports_external.object({
25651
+ name: exports_external.string().optional(),
25652
+ plugins: exports_external.array(exports_external.unknown())
25653
+ }).passthrough();
25654
+ });
25655
+
25656
+ // src/utils/marketplace-manifest-parser.ts
25657
+ import { readFile as readFile3 } from "node:fs/promises";
25585
25658
  import { existsSync as existsSync4 } from "node:fs";
25586
- import { join as join7, basename as basename2, dirname as dirname4, relative as relative2 } from "node:path";
25587
- async function ensureWorkspaceRules(filePath, repositories, skillsIndexRefs = []) {
25588
- const rulesContent = generateWorkspaceRules(repositories, skillsIndexRefs);
25589
- const startMarker = "<!-- WORKSPACE-RULES:START -->";
25590
- const endMarker = "<!-- WORKSPACE-RULES:END -->";
25591
- if (!existsSync4(filePath)) {
25592
- await writeFile(filePath, `${rulesContent.trim()}
25593
- `, "utf-8");
25594
- return;
25659
+ import { join as join7, resolve as resolve5 } from "node:path";
25660
+ async function parseMarketplaceManifest(marketplacePath) {
25661
+ const manifestPath = join7(marketplacePath, MANIFEST_PATH);
25662
+ if (!existsSync4(manifestPath)) {
25663
+ return {
25664
+ success: false,
25665
+ error: `Marketplace manifest not found: ${manifestPath}`
25666
+ };
25595
25667
  }
25596
- const content = await readFile3(filePath, "utf-8");
25597
- const startIndex = content.indexOf(startMarker);
25598
- const endIndex = content.indexOf(endMarker);
25599
- if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
25600
- const before = content.substring(0, startIndex);
25601
- const after = content.substring(endIndex + endMarker.length);
25602
- await writeFile(filePath, before + rulesContent.trim() + after, "utf-8");
25603
- } else {
25604
- await writeFile(filePath, content + rulesContent, "utf-8");
25668
+ let raw;
25669
+ try {
25670
+ raw = await readFile3(manifestPath, "utf-8");
25671
+ } catch (err) {
25672
+ return {
25673
+ success: false,
25674
+ error: `Failed to read marketplace manifest: ${err instanceof Error ? err.message : String(err)}`
25675
+ };
25605
25676
  }
25677
+ let json2;
25678
+ try {
25679
+ json2 = JSON.parse(raw);
25680
+ } catch {
25681
+ return {
25682
+ success: false,
25683
+ error: "Failed to parse marketplace.json as JSON: invalid syntax"
25684
+ };
25685
+ }
25686
+ const strictResult = MarketplaceManifestSchema.safeParse(json2);
25687
+ if (strictResult.success) {
25688
+ return { success: true, data: strictResult.data, warnings: [] };
25689
+ }
25690
+ return parseLeniently(json2);
25606
25691
  }
25607
- function isExcluded(pluginPath, filePath, exclude) {
25608
- if (!exclude || exclude.length === 0)
25609
- return false;
25610
- const relativePath = relative2(pluginPath, filePath).replaceAll("\\", "/");
25611
- return import_micromatch.default.isMatch(relativePath, exclude);
25612
- }
25613
- async function copyDirectoryWithExclusions(sourceDir, destDir, pluginPath, exclude) {
25614
- await mkdir3(destDir, { recursive: true });
25615
- const entries = await readdir2(sourceDir, { withFileTypes: true });
25616
- for (const entry of entries) {
25617
- const sourcePath = join7(sourceDir, entry.name);
25618
- const destPath = join7(destDir, entry.name);
25619
- if (isExcluded(pluginPath, sourcePath, exclude)) {
25692
+ function parseLeniently(json2) {
25693
+ const lenientResult = MarketplaceManifestLenientSchema.safeParse(json2);
25694
+ if (!lenientResult.success) {
25695
+ return {
25696
+ success: false,
25697
+ error: 'Marketplace manifest must contain a "plugins" array'
25698
+ };
25699
+ }
25700
+ const raw = lenientResult.data;
25701
+ const warnings = [];
25702
+ const obj = json2;
25703
+ const validPlugins = [];
25704
+ for (let i2 = 0;i2 < raw.plugins.length; i2++) {
25705
+ const entry = raw.plugins[i2];
25706
+ const entryResult = MarketplacePluginEntrySchema.safeParse(entry);
25707
+ if (entryResult.success) {
25708
+ validPlugins.push(entryResult.data);
25620
25709
  continue;
25621
25710
  }
25622
- if (entry.isDirectory()) {
25623
- await copyDirectoryWithExclusions(sourcePath, destPath, pluginPath, exclude);
25624
- } else {
25625
- await cp(sourcePath, destPath);
25711
+ const extracted = extractPluginEntry(entry, i2, warnings);
25712
+ if (extracted) {
25713
+ validPlugins.push(extracted);
25626
25714
  }
25627
25715
  }
25716
+ const data = {
25717
+ name: typeof raw.name === "string" ? raw.name : "unknown",
25718
+ description: typeof obj.description === "string" ? obj.description : "",
25719
+ plugins: validPlugins
25720
+ };
25721
+ return { success: true, data, warnings };
25628
25722
  }
25629
- function getMapping(client, options2) {
25630
- return options2?.clientMappings?.[client] ?? CLIENT_MAPPINGS[client];
25631
- }
25632
- async function copyCommands(pluginPath, workspacePath, client, options2 = {}) {
25633
- const { dryRun = false } = options2;
25634
- const mapping = getMapping(client, options2);
25635
- const results = [];
25636
- if (!mapping.commandsPath) {
25637
- return results;
25638
- }
25639
- const sourceDir = join7(pluginPath, "commands");
25640
- if (!existsSync4(sourceDir)) {
25641
- return results;
25642
- }
25643
- const destDir = join7(workspacePath, mapping.commandsPath);
25644
- if (!dryRun) {
25645
- await mkdir3(destDir, { recursive: true });
25723
+ function extractPluginEntry(entry, index, warnings) {
25724
+ if (!entry || typeof entry !== "object") {
25725
+ warnings.push(`plugins[${index}]: not an object, skipped`);
25726
+ return null;
25646
25727
  }
25647
- const files = await readdir2(sourceDir);
25648
- const mdFiles = files.filter((f) => f.endsWith(".md"));
25649
- const copyPromises = mdFiles.filter((file) => !isExcluded(pluginPath, join7(sourceDir, file), options2.exclude)).map(async (file) => {
25650
- const sourcePath = join7(sourceDir, file);
25651
- const destPath = join7(destDir, file);
25652
- if (dryRun) {
25653
- return { source: sourcePath, destination: destPath, action: "copied" };
25654
- }
25655
- try {
25656
- const content = await readFile3(sourcePath, "utf-8");
25657
- await writeFile(destPath, content, "utf-8");
25658
- return { source: sourcePath, destination: destPath, action: "copied" };
25659
- } catch (error) {
25660
- return {
25661
- source: sourcePath,
25662
- destination: destPath,
25663
- action: "failed",
25664
- error: error instanceof Error ? error.message : "Unknown error"
25665
- };
25666
- }
25667
- });
25668
- return Promise.all(copyPromises);
25669
- }
25670
- async function copySkills(pluginPath, workspacePath, client, options2 = {}) {
25671
- const { dryRun = false, skillNameMap, syncMode = "copy", canonicalSkillsPath } = options2;
25672
- const mapping = getMapping(client, options2);
25673
- const results = [];
25674
- if (!mapping.skillsPath) {
25675
- return results;
25728
+ const obj = entry;
25729
+ const name = typeof obj.name === "string" && obj.name ? obj.name : undefined;
25730
+ if (!name) {
25731
+ warnings.push(`plugins[${index}]: missing "name" field, skipped`);
25732
+ return null;
25676
25733
  }
25677
- const skillsDir = join7(pluginPath, "skills");
25678
- let skillSources;
25679
- if (existsSync4(skillsDir)) {
25680
- const entries = await readdir2(skillsDir, { withFileTypes: true });
25681
- skillSources = entries.filter((e) => e.isDirectory()).filter((e) => !isExcluded(pluginPath, join7(skillsDir, e.name), options2.exclude)).map((e) => ({ name: e.name, sourcePath: join7(skillsDir, e.name), isRootLevel: false }));
25734
+ let description = "";
25735
+ if (typeof obj.description === "string" && obj.description) {
25736
+ description = obj.description;
25737
+ } else if (obj.metadata && typeof obj.metadata === "object" && typeof obj.metadata.description === "string") {
25738
+ description = obj.metadata.description;
25739
+ warnings.push(`plugins[${index}] ("${name}"): "description" found in metadata instead of top level`);
25682
25740
  } else {
25683
- const entries = await readdir2(pluginPath, { withFileTypes: true });
25684
- const flatSkills = [];
25685
- for (const entry of entries) {
25686
- if (!entry.isDirectory())
25687
- continue;
25688
- const skillMdPath = join7(pluginPath, entry.name, "SKILL.md");
25689
- if (existsSync4(skillMdPath)) {
25690
- flatSkills.push({ name: entry.name, sourcePath: join7(pluginPath, entry.name), isRootLevel: false });
25691
- }
25692
- }
25693
- if (flatSkills.length > 0) {
25694
- skillSources = flatSkills;
25695
- } else {
25696
- const rootSkillMd = join7(pluginPath, "SKILL.md");
25697
- if (existsSync4(rootSkillMd)) {
25698
- const content = await readFile3(rootSkillMd, "utf-8");
25699
- const metadata = parseSkillMetadata(content);
25700
- const skillName = metadata?.name ?? basename2(pluginPath);
25701
- skillSources = [{ name: skillName, sourcePath: pluginPath, isRootLevel: true }];
25702
- } else {
25703
- return results;
25704
- }
25705
- }
25741
+ warnings.push(`plugins[${index}] ("${name}"): missing "description" field`);
25706
25742
  }
25707
- if (skillNameMap) {
25708
- skillSources = skillSources.filter((s) => skillNameMap.has(s.name));
25743
+ let source = "";
25744
+ const sourceResult = PluginSourceRefSchema.safeParse(obj.source);
25745
+ if (sourceResult.success) {
25746
+ source = sourceResult.data;
25747
+ } else {
25748
+ warnings.push(`plugins[${index}] ("${name}"): missing or invalid "source" field`);
25709
25749
  }
25710
- if (skillSources.length === 0) {
25711
- return results;
25750
+ return {
25751
+ name,
25752
+ description,
25753
+ source,
25754
+ ...typeof obj.version === "string" && { version: obj.version },
25755
+ ...typeof obj.category === "string" && { category: obj.category },
25756
+ ...typeof obj.homepage === "string" && { homepage: obj.homepage },
25757
+ ...Array.isArray(obj.skills) && obj.skills.every((s) => typeof s === "string") && { skills: obj.skills }
25758
+ };
25759
+ }
25760
+ function resolvePluginSourcePath(source, marketplacePath) {
25761
+ if (typeof source === "object") {
25762
+ return source.url;
25712
25763
  }
25713
- const destDir = join7(workspacePath, mapping.skillsPath);
25714
- if (!dryRun) {
25715
- await mkdir3(destDir, { recursive: true });
25764
+ return resolve5(marketplacePath, source);
25765
+ }
25766
+ var MANIFEST_PATH = ".claude-plugin/marketplace.json";
25767
+ var init_marketplace_manifest_parser = __esm(() => {
25768
+ init_marketplace_manifest();
25769
+ });
25770
+
25771
+ // src/core/workspace-modify.ts
25772
+ import { existsSync as existsSync5 } from "node:fs";
25773
+ import { mkdir as mkdir3, readFile as readFile4, writeFile } from "node:fs/promises";
25774
+ import { join as join8 } from "node:path";
25775
+ async function setClients(clients, workspacePath = process.cwd()) {
25776
+ try {
25777
+ await ensureWorkspace(workspacePath);
25778
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
25779
+ const content = await readFile4(configPath, "utf-8");
25780
+ const config = load(content);
25781
+ config.clients = clients;
25782
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
25783
+ return { success: true };
25784
+ } catch (error) {
25785
+ return {
25786
+ success: false,
25787
+ error: error instanceof Error ? error.message : String(error)
25788
+ };
25716
25789
  }
25717
- const useSymlinks = syncMode === "symlink" && !isUniversalClient(client) && canonicalSkillsPath;
25718
- const copyPromises = skillSources.map(async (skill) => {
25719
- const resolvedName = skillNameMap?.get(skill.name) ?? skill.name;
25720
- const skillDestPath = join7(destDir, resolvedName);
25721
- if (dryRun) {
25790
+ }
25791
+ async function ensureWorkspace(workspacePath, clients) {
25792
+ const configDir = join8(workspacePath, CONFIG_DIR);
25793
+ const configPath = join8(configDir, WORKSPACE_CONFIG_FILE);
25794
+ if (existsSync5(configPath))
25795
+ return;
25796
+ const defaultConfig = {
25797
+ repositories: [],
25798
+ plugins: [],
25799
+ clients: clients ?? [...DEFAULT_PROJECT_CLIENTS]
25800
+ };
25801
+ await mkdir3(configDir, { recursive: true });
25802
+ await writeFile(configPath, dump(defaultConfig, { lineWidth: -1 }), "utf-8");
25803
+ }
25804
+ async function addPlugin(plugin, workspacePath = process.cwd(), force) {
25805
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
25806
+ await ensureWorkspace(workspacePath);
25807
+ if (isPluginSpec(plugin)) {
25808
+ const resolved = await resolvePluginSpecWithAutoRegister(plugin, { workspacePath });
25809
+ if (!resolved.success) {
25722
25810
  return {
25723
- source: skill.sourcePath,
25724
- destination: skillDestPath,
25725
- action: "copied"
25811
+ success: false,
25812
+ error: resolved.error || "Unknown error"
25726
25813
  };
25727
25814
  }
25728
- if (useSymlinks) {
25729
- const canonicalSkillPath = join7(workspacePath, canonicalSkillsPath, resolvedName);
25730
- const symlinkCreated = await createSymlink(canonicalSkillPath, skillDestPath);
25731
- if (symlinkCreated) {
25732
- return {
25733
- source: canonicalSkillPath,
25734
- destination: skillDestPath,
25735
- action: "copied"
25736
- };
25737
- }
25815
+ return await addPluginToConfig(resolved.registeredAs ? plugin.replace(/@[^@]+$/, `@${resolved.registeredAs}`) : plugin, configPath, resolved.registeredAs, force);
25816
+ }
25817
+ if (isGitHubUrl(plugin)) {
25818
+ const validation = validatePluginSource(plugin);
25819
+ if (!validation.valid) {
25820
+ return {
25821
+ success: false,
25822
+ error: validation.error || "Invalid GitHub URL"
25823
+ };
25738
25824
  }
25739
- try {
25740
- if (skill.isRootLevel) {
25741
- await mkdir3(skillDestPath, { recursive: true });
25742
- await cp(join7(skill.sourcePath, "SKILL.md"), join7(skillDestPath, "SKILL.md"));
25743
- } else if (options2.exclude && options2.exclude.length > 0) {
25744
- await copyDirectoryWithExclusions(skill.sourcePath, skillDestPath, pluginPath, options2.exclude);
25745
- } else {
25746
- await cp(skill.sourcePath, skillDestPath, { recursive: true });
25747
- }
25825
+ const verifyResult = await verifyGitHubUrlExists(plugin);
25826
+ if (!verifyResult.exists) {
25748
25827
  return {
25749
- source: skill.sourcePath,
25750
- destination: skillDestPath,
25751
- action: "copied"
25828
+ success: false,
25829
+ error: verifyResult.error || `GitHub URL not found: ${plugin}`
25752
25830
  };
25753
- } catch (error) {
25831
+ }
25832
+ } else {
25833
+ const fullPath = join8(workspacePath, plugin);
25834
+ if (!existsSync5(fullPath) && !existsSync5(plugin)) {
25754
25835
  return {
25755
- source: skill.sourcePath,
25756
- destination: skillDestPath,
25757
- action: "failed",
25758
- error: error instanceof Error ? error.message : "Unknown error"
25836
+ success: false,
25837
+ error: `Plugin not found at ${plugin}`
25759
25838
  };
25760
25839
  }
25761
- });
25762
- return Promise.all(copyPromises);
25840
+ }
25841
+ return await addPluginToConfig(plugin, configPath, undefined, force);
25763
25842
  }
25764
- async function collectPluginSkills(pluginPath, pluginSource, disabledSkills, pluginName, enabledSkills, pluginSkillsConfig) {
25765
- const skillsDir = join7(pluginPath, "skills");
25766
- const hasEnabledEntries = !pluginSkillsConfig && enabledSkills && pluginName && [...enabledSkills].some((s) => s.startsWith(`${pluginName}:`));
25767
- let candidateDirs;
25768
- if (existsSync4(skillsDir)) {
25769
- const entries = await readdir2(skillsDir, { withFileTypes: true });
25770
- candidateDirs = entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name, path: join7(skillsDir, e.name) }));
25771
- } else {
25772
- const entries = await readdir2(pluginPath, { withFileTypes: true });
25773
- const flatDirs = [];
25774
- for (const entry of entries) {
25775
- if (!entry.isDirectory())
25776
- continue;
25777
- const skillMdPath = join7(pluginPath, entry.name, "SKILL.md");
25778
- if (existsSync4(skillMdPath)) {
25779
- flatDirs.push({ name: entry.name, path: join7(pluginPath, entry.name) });
25843
+ async function addPluginToConfig(plugin, configPath, autoRegistered, force) {
25844
+ try {
25845
+ const content = await readFile4(configPath, "utf-8");
25846
+ const config = load(content);
25847
+ const existingExactIndex = config.plugins.findIndex((entry) => getPluginSource(entry) === plugin);
25848
+ if (existingExactIndex !== -1) {
25849
+ if (!force) {
25850
+ return {
25851
+ success: false,
25852
+ error: `Plugin already exists in .allagents/workspace.yaml: ${plugin}`
25853
+ };
25780
25854
  }
25781
25855
  }
25782
- if (flatDirs.length > 0) {
25783
- candidateDirs = flatDirs;
25784
- } else {
25785
- const rootSkillMd = join7(pluginPath, "SKILL.md");
25786
- if (existsSync4(rootSkillMd)) {
25787
- const content = await readFile3(rootSkillMd, "utf-8");
25788
- const metadata = parseSkillMetadata(content);
25789
- const skillName = metadata?.name ?? basename2(pluginPath);
25790
- candidateDirs = [{ name: skillName, path: pluginPath }];
25791
- } else {
25792
- candidateDirs = [];
25856
+ if (!force) {
25857
+ const newIdentity = await resolveGitHubIdentity(plugin);
25858
+ if (newIdentity) {
25859
+ for (const existing of config.plugins) {
25860
+ const existingSource = getPluginSource(existing);
25861
+ const existingIdentity = await resolveGitHubIdentity(existingSource);
25862
+ if (existingIdentity === newIdentity) {
25863
+ return {
25864
+ success: false,
25865
+ error: `Plugin duplicates existing entry '${existingSource}': both resolve to ${newIdentity}`
25866
+ };
25867
+ }
25868
+ }
25793
25869
  }
25794
25870
  }
25795
- }
25796
- let filteredDirs;
25797
- if (pluginSkillsConfig !== undefined) {
25798
- if (Array.isArray(pluginSkillsConfig)) {
25799
- filteredDirs = candidateDirs.filter((e) => pluginSkillsConfig.includes(e.name));
25800
- } else {
25801
- filteredDirs = candidateDirs.filter((e) => !pluginSkillsConfig.exclude.includes(e.name));
25871
+ const wasReplaced = force && existingExactIndex !== -1;
25872
+ if (wasReplaced) {
25873
+ config.plugins.splice(existingExactIndex, 1);
25802
25874
  }
25803
- } else if (pluginName) {
25804
- if (hasEnabledEntries) {
25805
- filteredDirs = candidateDirs.filter((e) => enabledSkills?.has(`${pluginName}:${e.name}`));
25806
- } else if (disabledSkills) {
25807
- filteredDirs = candidateDirs.filter((e) => !disabledSkills.has(`${pluginName}:${e.name}`));
25808
- } else {
25809
- filteredDirs = candidateDirs;
25875
+ config.plugins.push(plugin);
25876
+ const newContent = dump(config, { lineWidth: -1 });
25877
+ await writeFile(configPath, newContent, "utf-8");
25878
+ const result = {
25879
+ success: true,
25880
+ normalizedPlugin: plugin
25881
+ };
25882
+ if (autoRegistered) {
25883
+ result.autoRegistered = autoRegistered;
25810
25884
  }
25811
- } else {
25812
- filteredDirs = candidateDirs;
25885
+ if (wasReplaced) {
25886
+ result.replaced = true;
25887
+ }
25888
+ return result;
25889
+ } catch (error) {
25890
+ return {
25891
+ success: false,
25892
+ error: error instanceof Error ? error.message : String(error)
25893
+ };
25813
25894
  }
25814
- return filteredDirs.map((entry) => ({
25815
- folderName: entry.name,
25816
- skillPath: entry.path,
25817
- pluginPath,
25818
- pluginSource
25819
- }));
25820
25895
  }
25821
- async function copyHooks(pluginPath, workspacePath, client, options2 = {}) {
25822
- const { dryRun = false } = options2;
25823
- const mapping = getMapping(client, options2);
25824
- const results = [];
25825
- if (!mapping.hooksPath) {
25826
- return results;
25827
- }
25828
- const sourceDir = join7(pluginPath, "hooks");
25829
- if (!existsSync4(sourceDir)) {
25830
- return results;
25831
- }
25832
- const destDir = join7(workspacePath, mapping.hooksPath);
25833
- if (dryRun) {
25834
- results.push({ source: sourceDir, destination: destDir, action: "copied" });
25835
- return results;
25836
- }
25837
- await mkdir3(destDir, { recursive: true });
25896
+ async function hasPlugin(plugin, workspacePath = process.cwd()) {
25897
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
25898
+ if (!existsSync5(configPath))
25899
+ return false;
25838
25900
  try {
25839
- if (options2.exclude && options2.exclude.length > 0) {
25840
- await copyDirectoryWithExclusions(sourceDir, destDir, pluginPath, options2.exclude);
25841
- } else {
25842
- await cp(sourceDir, destDir, { recursive: true });
25901
+ const content = await readFile4(configPath, "utf-8");
25902
+ const config = load(content);
25903
+ if (config.plugins.some((entry) => getPluginSource(entry) === plugin))
25904
+ return true;
25905
+ if (!isPluginSpec(plugin)) {
25906
+ return config.plugins.some((entry) => {
25907
+ const source = getPluginSource(entry);
25908
+ return source.startsWith(`${plugin}@`) || source === plugin;
25909
+ });
25843
25910
  }
25844
- results.push({ source: sourceDir, destination: destDir, action: "copied" });
25845
- } catch (error) {
25846
- results.push({
25847
- source: sourceDir,
25848
- destination: destDir,
25849
- action: "failed",
25850
- error: error instanceof Error ? error.message : "Unknown error"
25851
- });
25911
+ return false;
25912
+ } catch {
25913
+ return false;
25852
25914
  }
25853
- return results;
25854
25915
  }
25855
- async function copyAgents(pluginPath, workspacePath, client, options2 = {}) {
25856
- const { dryRun = false } = options2;
25857
- const mapping = getMapping(client, options2);
25858
- const results = [];
25859
- if (!mapping.agentsPath) {
25860
- return results;
25861
- }
25862
- const sourceDir = join7(pluginPath, "agents");
25863
- if (!existsSync4(sourceDir)) {
25864
- return results;
25865
- }
25866
- const destDir = join7(workspacePath, mapping.agentsPath);
25867
- if (!dryRun) {
25868
- await mkdir3(destDir, { recursive: true });
25916
+ async function removePlugin(plugin, workspacePath = process.cwd()) {
25917
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
25918
+ if (!existsSync5(configPath)) {
25919
+ return {
25920
+ success: false,
25921
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
25922
+ Run 'allagents workspace init <path>' to create a new workspace`
25923
+ };
25869
25924
  }
25870
- const files = await readdir2(sourceDir);
25871
- const mdFiles = files.filter((f) => f.endsWith(".md"));
25872
- const copyPromises = mdFiles.filter((file) => !isExcluded(pluginPath, join7(sourceDir, file), options2.exclude)).map(async (file) => {
25873
- const sourcePath = join7(sourceDir, file);
25874
- const destPath = join7(destDir, file);
25875
- if (dryRun) {
25876
- return { source: sourcePath, destination: destPath, action: "copied" };
25925
+ try {
25926
+ const content = await readFile4(configPath, "utf-8");
25927
+ const config = load(content);
25928
+ let index = config.plugins.findIndex((entry) => getPluginSource(entry) === plugin);
25929
+ if (index === -1 && isPluginSpec(plugin) === false) {
25930
+ index = config.plugins.findIndex((entry) => {
25931
+ const source = getPluginSource(entry);
25932
+ return source.startsWith(`${plugin}@`) || source === plugin;
25933
+ });
25877
25934
  }
25878
- try {
25879
- const content = await readFile3(sourcePath, "utf-8");
25880
- await writeFile(destPath, content, "utf-8");
25881
- return { source: sourcePath, destination: destPath, action: "copied" };
25882
- } catch (error) {
25935
+ if (index === -1) {
25936
+ const identity = await resolveGitHubIdentity(plugin);
25937
+ if (identity) {
25938
+ for (let i2 = 0;i2 < config.plugins.length; i2++) {
25939
+ const p = config.plugins[i2];
25940
+ if (!p)
25941
+ continue;
25942
+ const existing = await resolveGitHubIdentity(getPluginSource(p));
25943
+ if (existing === identity) {
25944
+ index = i2;
25945
+ break;
25946
+ }
25947
+ }
25948
+ }
25949
+ }
25950
+ if (index === -1) {
25883
25951
  return {
25884
- source: sourcePath,
25885
- destination: destPath,
25886
- action: "failed",
25887
- error: error instanceof Error ? error.message : "Unknown error"
25952
+ success: false,
25953
+ error: `Plugin not found in .allagents/workspace.yaml: ${plugin}`
25888
25954
  };
25889
25955
  }
25890
- });
25891
- return Promise.all(copyPromises);
25956
+ const removedEntry = getPluginSource(config.plugins[index]);
25957
+ config.plugins.splice(index, 1);
25958
+ pruneDisabledSkillsForPlugin(config, removedEntry);
25959
+ pruneEnabledSkillsForPlugin(config, removedEntry);
25960
+ const newContent = dump(config, { lineWidth: -1 });
25961
+ await writeFile(configPath, newContent, "utf-8");
25962
+ return { success: true };
25963
+ } catch (error) {
25964
+ return {
25965
+ success: false,
25966
+ error: error instanceof Error ? error.message : String(error)
25967
+ };
25968
+ }
25892
25969
  }
25893
- async function copyAndAdjustDirectory(sourceDir, destDir, sourceBase, pluginPath, skillsPath, skillNameMap, exclude) {
25894
- await mkdir3(destDir, { recursive: true });
25895
- const entries = await readdir2(sourceDir, { withFileTypes: true });
25896
- for (const entry of entries) {
25897
- const sourcePath = join7(sourceDir, entry.name);
25898
- const destPath = join7(destDir, entry.name);
25899
- if (isExcluded(pluginPath, sourcePath, exclude)) {
25900
- continue;
25901
- }
25902
- if (entry.isDirectory()) {
25903
- await copyAndAdjustDirectory(sourcePath, destPath, sourceBase, pluginPath, skillsPath, skillNameMap, exclude);
25904
- } else {
25905
- const relativePath = relative2(sourceBase, sourcePath).replaceAll("\\", "/");
25906
- const isMarkdown = entry.name.endsWith(".md") || entry.name.endsWith(".markdown");
25907
- if (isMarkdown) {
25908
- let content = await readFile3(sourcePath, "utf-8");
25909
- content = adjustLinksInContent(content, relativePath, {
25910
- ...skillNameMap && { skillNameMap },
25911
- workspaceSkillsPath: skillsPath
25912
- });
25913
- await writeFile(destPath, content, "utf-8");
25914
- } else {
25915
- await cp(sourcePath, destPath);
25916
- }
25970
+ function pruneDisabledSkillsForPlugin(config, pluginEntry) {
25971
+ if (!config.disabledSkills?.length)
25972
+ return;
25973
+ const names = extractPluginNames(pluginEntry);
25974
+ if (names.length === 0)
25975
+ return;
25976
+ const prefixes = names.map((n) => `${n}:`);
25977
+ config.disabledSkills = config.disabledSkills.filter((s) => !prefixes.some((p) => s.startsWith(p)));
25978
+ if (config.disabledSkills.length === 0) {
25979
+ config.disabledSkills = undefined;
25980
+ }
25981
+ }
25982
+ function extractPluginNames(pluginSource) {
25983
+ if (isPluginSpec(pluginSource)) {
25984
+ const parsed = parsePluginSpec(pluginSource);
25985
+ if (!parsed)
25986
+ return [];
25987
+ const names = [parsed.plugin];
25988
+ if (parsed.marketplaceName && parsed.marketplaceName !== parsed.plugin) {
25989
+ names.push(parsed.marketplaceName);
25917
25990
  }
25991
+ return names;
25918
25992
  }
25993
+ if (isGitHubUrl(pluginSource)) {
25994
+ const parsed = parseGitHubUrl(pluginSource);
25995
+ if (parsed) {
25996
+ const names = [];
25997
+ const ownerRepo = `${parsed.owner}-${parsed.repo}`;
25998
+ if (ownerRepo !== parsed.repo)
25999
+ names.push(ownerRepo);
26000
+ if (parsed.subpath) {
26001
+ const subpathName = parsed.subpath.split("/").filter(Boolean).pop();
26002
+ if (subpathName && !names.includes(subpathName))
26003
+ names.push(subpathName);
26004
+ }
26005
+ if (!names.includes(parsed.repo))
26006
+ names.push(parsed.repo);
26007
+ if (!names.includes(ownerRepo))
26008
+ names.push(ownerRepo);
26009
+ return names;
26010
+ }
26011
+ }
26012
+ const parts = pluginSource.split(/[/\\]/).filter(Boolean);
26013
+ const last2 = parts[parts.length - 1];
26014
+ if (!last2)
26015
+ return [];
26016
+ return [last2.replace(/\.git$/, "")];
25919
26017
  }
25920
- async function copyGitHubContent(pluginPath, workspacePath, client, options2 = {}) {
25921
- const { dryRun = false, skillNameMap } = options2;
25922
- const mapping = getMapping(client, options2);
25923
- const results = [];
25924
- if (!mapping.githubPath) {
25925
- return results;
26018
+ function findPluginEntryByName(config, pluginName) {
26019
+ return config.plugins.findIndex((entry) => extractPluginNames(getPluginSource(entry)).includes(pluginName));
26020
+ }
26021
+ function ensureObjectPluginEntry(config, index) {
26022
+ const entry = config.plugins[index];
26023
+ if (entry === undefined)
26024
+ throw new Error(`Plugin entry at index ${index} not found`);
26025
+ if (typeof entry === "string") {
26026
+ const objectEntry = { source: entry };
26027
+ config.plugins[index] = objectEntry;
26028
+ return objectEntry;
25926
26029
  }
25927
- const sourceDir = join7(pluginPath, ".github");
25928
- if (!existsSync4(sourceDir)) {
25929
- return results;
26030
+ return entry;
26031
+ }
26032
+ function uniqueSkillNames(skillNames) {
26033
+ const unique = [];
26034
+ const seen = new Set;
26035
+ for (const skillName of skillNames) {
26036
+ if (seen.has(skillName))
26037
+ continue;
26038
+ seen.add(skillName);
26039
+ unique.push(skillName);
26040
+ }
26041
+ return unique;
26042
+ }
26043
+ function formatGitHubSource(parsed, styleSource) {
26044
+ const basePath = `${parsed.owner}/${parsed.repo}`;
26045
+ if (styleSource.startsWith("http://") || styleSource.startsWith("https://") || styleSource.startsWith("github.com/")) {
26046
+ const baseUrl = `https://github.com/${basePath}`;
26047
+ if (!parsed.branch)
26048
+ return baseUrl;
26049
+ return parsed.subpath ? `${baseUrl}/tree/${parsed.branch}/${parsed.subpath}` : `${baseUrl}/tree/${parsed.branch}`;
26050
+ }
26051
+ if (!parsed.branch) {
26052
+ return parsed.subpath ? `${basePath}/${parsed.subpath}` : basePath;
26053
+ }
26054
+ return parsed.subpath ? `${basePath}@${parsed.branch}/${parsed.subpath}` : `${basePath}@${parsed.branch}`;
26055
+ }
26056
+ function canonicalizeGitHubPluginSource(currentSource, nextSource) {
26057
+ const current = parseGitHubUrl(currentSource);
26058
+ const next = parseGitHubUrl(nextSource);
26059
+ if (!current || !next)
26060
+ return nextSource;
26061
+ if (current.owner.toLowerCase() !== next.owner.toLowerCase() || current.repo.toLowerCase() !== next.repo.toLowerCase()) {
26062
+ return nextSource;
26063
+ }
26064
+ if (current.branch && next.branch && current.branch !== next.branch) {
26065
+ return currentSource;
26066
+ }
26067
+ const currentParts = current.subpath?.split("/").filter(Boolean) ?? [];
26068
+ const nextParts = next.subpath?.split("/").filter(Boolean) ?? [];
26069
+ const sharedParts = [];
26070
+ const sharedLength = Math.min(currentParts.length, nextParts.length);
26071
+ for (let i2 = 0;i2 < sharedLength; i2++) {
26072
+ if (currentParts[i2] !== nextParts[i2])
26073
+ break;
26074
+ sharedParts.push(currentParts[i2]);
26075
+ }
26076
+ return formatGitHubSource({
26077
+ owner: current.owner,
26078
+ repo: current.repo,
26079
+ ...current.branch || next.branch ? { branch: current.branch ?? next.branch } : {},
26080
+ ...sharedParts.length > 0 ? { subpath: sharedParts.join("/") } : {}
26081
+ }, currentSource);
26082
+ }
26083
+ async function findPluginEntryByGitHubIdentity(config, source) {
26084
+ const identity = await resolveGitHubIdentity(source);
26085
+ if (!identity)
26086
+ return -1;
26087
+ for (let i2 = 0;i2 < config.plugins.length; i2++) {
26088
+ const entry = config.plugins[i2];
26089
+ if (!entry)
26090
+ continue;
26091
+ const existingIdentity = await resolveGitHubIdentity(getPluginSource(entry));
26092
+ if (existingIdentity === identity)
26093
+ return i2;
25930
26094
  }
25931
- const destDir = join7(workspacePath, mapping.githubPath);
25932
- if (dryRun) {
25933
- results.push({ source: sourceDir, destination: destDir, action: "copied" });
25934
- return results;
26095
+ return -1;
26096
+ }
26097
+ async function upsertGitHubPluginSourceAllowlistInConfig(config, source, skillNames) {
26098
+ const normalizedSkills = uniqueSkillNames(skillNames);
26099
+ const exactIndex = config.plugins.findIndex((entry2) => getPluginSource(entry2) === source);
26100
+ if (exactIndex !== -1) {
26101
+ const entry2 = ensureObjectPluginEntry(config, exactIndex);
26102
+ entry2.source = source;
26103
+ entry2.skills = normalizedSkills;
26104
+ return { success: true, normalizedPlugin: source };
26105
+ }
26106
+ const semanticIndex = await findPluginEntryByGitHubIdentity(config, source);
26107
+ if (semanticIndex === -1) {
26108
+ config.plugins.push({ source, skills: normalizedSkills });
26109
+ return { success: true, normalizedPlugin: source };
26110
+ }
26111
+ const entry = ensureObjectPluginEntry(config, semanticIndex);
26112
+ const normalizedSource = canonicalizeGitHubPluginSource(entry.source, source);
26113
+ entry.source = normalizedSource;
26114
+ entry.skills = normalizedSkills;
26115
+ return { success: true, normalizedPlugin: normalizedSource };
26116
+ }
26117
+ function parseSkillKey(skillKey) {
26118
+ const colonIdx = skillKey.indexOf(":");
26119
+ if (colonIdx === -1)
26120
+ return null;
26121
+ return {
26122
+ pluginName: skillKey.slice(0, colonIdx),
26123
+ skillName: skillKey.slice(colonIdx + 1)
26124
+ };
26125
+ }
26126
+ async function addDisabledSkill(skillKey, workspacePath = process.cwd()) {
26127
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26128
+ if (!existsSync5(configPath)) {
26129
+ return {
26130
+ success: false,
26131
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
26132
+ };
25935
26133
  }
26134
+ const parsed = parseSkillKey(skillKey);
26135
+ if (!parsed) {
26136
+ return {
26137
+ success: false,
26138
+ error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
26139
+ };
26140
+ }
26141
+ const { pluginName, skillName } = parsed;
25936
26142
  try {
25937
- if (mapping.skillsPath || options2.exclude && options2.exclude.length > 0) {
25938
- await copyAndAdjustDirectory(sourceDir, destDir, sourceDir, pluginPath, mapping.skillsPath ?? "", skillNameMap, options2.exclude);
25939
- } else {
25940
- await cp(sourceDir, destDir, { recursive: true });
26143
+ const content = await readFile4(configPath, "utf-8");
26144
+ const config = load(content);
26145
+ const index = findPluginEntryByName(config, pluginName);
26146
+ if (index === -1) {
26147
+ return {
26148
+ success: false,
26149
+ error: `Plugin '${pluginName}' not found in workspace config`
26150
+ };
25941
26151
  }
25942
- results.push({ source: sourceDir, destination: destDir, action: "copied" });
26152
+ const entry = ensureObjectPluginEntry(config, index);
26153
+ if (Array.isArray(entry.skills)) {
26154
+ return {
26155
+ success: false,
26156
+ error: `Plugin '${pluginName}' is in allowlist mode; use removeEnabledSkill to disable a skill`
26157
+ };
26158
+ }
26159
+ const existing = entry.skills?.exclude ?? [];
26160
+ if (existing.includes(skillName)) {
26161
+ return {
26162
+ success: false,
26163
+ error: `Skill '${skillKey}' is already disabled`
26164
+ };
26165
+ }
26166
+ entry.skills = { exclude: [...existing, skillName] };
26167
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26168
+ return { success: true };
25943
26169
  } catch (error) {
25944
- results.push({
25945
- source: sourceDir,
25946
- destination: destDir,
25947
- action: "failed",
25948
- error: error instanceof Error ? error.message : "Unknown error"
25949
- });
26170
+ return {
26171
+ success: false,
26172
+ error: error instanceof Error ? error.message : String(error)
26173
+ };
25950
26174
  }
25951
- return results;
25952
26175
  }
25953
- async function copyPluginToWorkspace(pluginPath, workspacePath, client, options2 = {}) {
25954
- const { skillNameMap, syncMode, canonicalSkillsPath, ...baseOptions } = options2;
25955
- const [commandResults, skillResults, hookResults, agentResults] = await Promise.all([
25956
- copyCommands(pluginPath, workspacePath, client, baseOptions),
25957
- copySkills(pluginPath, workspacePath, client, {
25958
- ...baseOptions,
25959
- ...skillNameMap && { skillNameMap },
25960
- ...syncMode && { syncMode },
25961
- ...canonicalSkillsPath && { canonicalSkillsPath }
25962
- }),
25963
- copyHooks(pluginPath, workspacePath, client, baseOptions),
25964
- copyAgents(pluginPath, workspacePath, client, baseOptions)
25965
- ]);
25966
- const githubResults = await copyGitHubContent(pluginPath, workspacePath, client, {
25967
- ...baseOptions,
25968
- ...skillNameMap && { skillNameMap }
25969
- });
25970
- return [...commandResults, ...skillResults, ...hookResults, ...agentResults, ...githubResults];
25971
- }
25972
- function isExplicitGitHubSource(source) {
25973
- if (source.startsWith("https://github.com/") || source.startsWith("http://github.com/") || source.startsWith("github.com/") || source.startsWith("gh:")) {
25974
- return true;
26176
+ async function removeDisabledSkill(skillKey, workspacePath = process.cwd()) {
26177
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26178
+ if (!existsSync5(configPath)) {
26179
+ return {
26180
+ success: false,
26181
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
26182
+ };
25975
26183
  }
25976
- if (!source.startsWith(".") && !source.startsWith("/") && source.includes("/")) {
25977
- const parts = source.split("/");
25978
- if (parts.length >= 3) {
25979
- const validOwnerRepo = /^[a-zA-Z0-9_.-]+$/;
25980
- if (parts[0] && parts[1] && validOwnerRepo.test(parts[0]) && validOwnerRepo.test(parts[1])) {
25981
- return true;
25982
- }
25983
- }
26184
+ const parsed = parseSkillKey(skillKey);
26185
+ if (!parsed) {
26186
+ return {
26187
+ success: false,
26188
+ error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
26189
+ };
25984
26190
  }
25985
- return false;
25986
- }
25987
- function resolveFileSourcePath(source, defaultSourcePath, githubCache) {
25988
- if (!isExplicitGitHubSource(source)) {
25989
- if (source.startsWith("/")) {
25990
- return { path: source };
26191
+ const { pluginName, skillName } = parsed;
26192
+ try {
26193
+ const content = await readFile4(configPath, "utf-8");
26194
+ const config = load(content);
26195
+ const index = findPluginEntryByName(config, pluginName);
26196
+ if (index === -1) {
26197
+ return {
26198
+ success: false,
26199
+ error: `Plugin '${pluginName}' not found in workspace config`
26200
+ };
25991
26201
  }
25992
- if (source.startsWith("../")) {
25993
- return { path: join7(process.cwd(), source) };
26202
+ const entry = config.plugins[index];
26203
+ if (!entry) {
26204
+ return { success: false, error: `Plugin '${pluginName}' not found in workspace config` };
25994
26205
  }
25995
- if (defaultSourcePath) {
25996
- return { path: join7(defaultSourcePath, source) };
26206
+ if (typeof entry === "string" || !entry.skills || Array.isArray(entry.skills)) {
26207
+ return {
26208
+ success: false,
26209
+ error: `Skill '${skillKey}' is already enabled`
26210
+ };
25997
26211
  }
25998
- return { path: join7(process.cwd(), source) };
25999
- }
26000
- const parsed = parseFileSource(source);
26001
- if (parsed.type === "github" && parsed.owner && parsed.repo && parsed.filePath) {
26002
- const cacheKey = `${parsed.owner}/${parsed.repo}`;
26003
- const cachePath = githubCache?.get(cacheKey);
26004
- if (!cachePath) {
26212
+ if (!entry.skills.exclude.includes(skillName)) {
26005
26213
  return {
26006
- path: "",
26007
- error: `GitHub cache not found for ${cacheKey}. Ensure the repo is fetched.`
26214
+ success: false,
26215
+ error: `Skill '${skillKey}' is already enabled`
26008
26216
  };
26009
26217
  }
26010
- return { path: join7(cachePath, parsed.filePath) };
26011
- }
26012
- if (parsed.type === "github") {
26218
+ const newExclude = entry.skills.exclude.filter((s) => s !== skillName);
26219
+ entry.skills = newExclude.length > 0 ? { exclude: newExclude } : undefined;
26220
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26221
+ return { success: true };
26222
+ } catch (error) {
26013
26223
  return {
26014
- path: "",
26015
- error: `Invalid GitHub file source: ${source}. Must include path to file (e.g., owner/repo/path/to/file.md)`
26224
+ success: false,
26225
+ error: error instanceof Error ? error.message : String(error)
26016
26226
  };
26017
26227
  }
26018
- return null;
26019
26228
  }
26020
- async function copyWorkspaceFiles(sourcePath, workspacePath, files, options2 = {}) {
26021
- const { dryRun = false, githubCache, repositories = [], skillsIndexRefs = [] } = options2;
26022
- const results = [];
26023
- const stringPatterns = [];
26024
- const objectEntries = [];
26025
- const copiedAgentFiles = [];
26026
- for (const file of files) {
26027
- if (typeof file === "string") {
26028
- stringPatterns.push(file);
26029
- } else {
26030
- let dest = file.dest;
26031
- if (!dest && file.source) {
26032
- const parts = file.source.split("/");
26033
- dest = parts[parts.length - 1] || file.source;
26034
- }
26035
- if (!dest) {
26036
- results.push({
26037
- source: "unknown",
26038
- destination: join7(workspacePath, "unknown"),
26039
- action: "failed",
26040
- error: "File entry must have at least source or dest specified"
26041
- });
26042
- continue;
26043
- }
26044
- objectEntries.push(file.source ? { source: file.source, dest } : { dest });
26045
- }
26229
+ async function addEnabledSkill(skillKey, workspacePath = process.cwd()) {
26230
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26231
+ if (!existsSync5(configPath)) {
26232
+ return {
26233
+ success: false,
26234
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
26235
+ };
26046
26236
  }
26047
- if (stringPatterns.length > 0) {
26048
- if (!sourcePath) {
26049
- for (const pattern of stringPatterns) {
26050
- if (!isGlobPattern(pattern) && !pattern.startsWith("!")) {
26051
- results.push({
26052
- source: pattern,
26053
- destination: join7(workspacePath, pattern),
26054
- action: "failed",
26055
- error: `Cannot resolve file '${pattern}' - no workspace.source configured and no explicit source provided`
26056
- });
26057
- }
26058
- }
26059
- } else {
26060
- const resolvedFiles = await resolveGlobPatterns(sourcePath, stringPatterns);
26061
- for (const resolved of resolvedFiles) {
26062
- const destPath = join7(workspacePath, resolved.relativePath);
26063
- if (!existsSync4(resolved.sourcePath)) {
26064
- const wasLiteral = stringPatterns.some((p) => !isGlobPattern(p) && !p.startsWith("!") && p === resolved.relativePath);
26065
- if (wasLiteral) {
26066
- results.push({
26067
- source: resolved.sourcePath,
26068
- destination: destPath,
26069
- action: "failed",
26070
- error: `Source file not found: ${resolved.sourcePath}`
26071
- });
26072
- }
26073
- continue;
26074
- }
26075
- if (dryRun) {
26076
- results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
26077
- if (AGENT_FILES2.includes(resolved.relativePath)) {
26078
- copiedAgentFiles.push(resolved.relativePath);
26079
- }
26080
- continue;
26081
- }
26082
- try {
26083
- await mkdir3(dirname4(destPath), { recursive: true });
26084
- const content = await readFile3(resolved.sourcePath, "utf-8");
26085
- await writeFile(destPath, content, "utf-8");
26086
- results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
26087
- if (AGENT_FILES2.includes(resolved.relativePath)) {
26088
- copiedAgentFiles.push(resolved.relativePath);
26089
- }
26090
- } catch (error) {
26091
- results.push({
26092
- source: resolved.sourcePath,
26093
- destination: destPath,
26094
- action: "failed",
26095
- error: error instanceof Error ? error.message : "Unknown error"
26096
- });
26097
- }
26098
- }
26099
- }
26237
+ const parsed = parseSkillKey(skillKey);
26238
+ if (!parsed) {
26239
+ return {
26240
+ success: false,
26241
+ error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
26242
+ };
26100
26243
  }
26101
- for (const entry of objectEntries) {
26102
- const destPath = join7(workspacePath, entry.dest);
26103
- let srcPath;
26104
- if (entry.source) {
26105
- const resolved = resolveFileSourcePath(entry.source, sourcePath, githubCache);
26106
- if (!resolved) {
26107
- results.push({
26108
- source: entry.source,
26109
- destination: destPath,
26110
- action: "failed",
26111
- error: `Failed to resolve source: ${entry.source}`
26112
- });
26113
- continue;
26114
- }
26115
- if (resolved.error) {
26116
- results.push({
26117
- source: entry.source,
26118
- destination: destPath,
26119
- action: "failed",
26120
- error: resolved.error
26121
- });
26122
- continue;
26123
- }
26124
- srcPath = resolved.path;
26125
- } else {
26126
- if (!sourcePath) {
26127
- results.push({
26128
- source: entry.dest,
26129
- destination: destPath,
26130
- action: "failed",
26131
- error: `Cannot resolve file '${entry.dest}' - no workspace.source configured and no explicit source provided`
26132
- });
26133
- continue;
26134
- }
26135
- srcPath = join7(sourcePath, entry.dest);
26136
- }
26137
- if (!existsSync4(srcPath)) {
26138
- results.push({
26139
- source: srcPath,
26140
- destination: destPath,
26141
- action: "failed",
26142
- error: `Source file not found: ${srcPath}`
26143
- });
26144
- continue;
26145
- }
26146
- if (dryRun) {
26147
- results.push({ source: srcPath, destination: destPath, action: "copied" });
26148
- if (AGENT_FILES2.includes(entry.dest)) {
26149
- copiedAgentFiles.push(entry.dest);
26150
- }
26151
- continue;
26244
+ const { pluginName, skillName } = parsed;
26245
+ try {
26246
+ const content = await readFile4(configPath, "utf-8");
26247
+ const config = load(content);
26248
+ const index = findPluginEntryByName(config, pluginName);
26249
+ if (index === -1) {
26250
+ return {
26251
+ success: false,
26252
+ error: `Plugin '${pluginName}' not found in workspace config`
26253
+ };
26152
26254
  }
26153
- try {
26154
- await mkdir3(dirname4(destPath), { recursive: true });
26155
- const content = await readFile3(srcPath, "utf-8");
26156
- await writeFile(destPath, content, "utf-8");
26157
- results.push({ source: srcPath, destination: destPath, action: "copied" });
26158
- if (AGENT_FILES2.includes(entry.dest)) {
26159
- copiedAgentFiles.push(entry.dest);
26160
- }
26161
- } catch (error) {
26162
- results.push({
26163
- source: srcPath,
26164
- destination: destPath,
26165
- action: "failed",
26166
- error: error instanceof Error ? error.message : "Unknown error"
26167
- });
26255
+ const entry = ensureObjectPluginEntry(config, index);
26256
+ if (entry.skills && !Array.isArray(entry.skills)) {
26257
+ return {
26258
+ success: false,
26259
+ error: `Plugin '${pluginName}' is in blocklist mode; use removeDisabledSkill to enable a skill`
26260
+ };
26168
26261
  }
26169
- }
26170
- if (!dryRun && repositories.length > 0) {
26171
- for (const agentFile of copiedAgentFiles) {
26172
- const targetPath = join7(workspacePath, agentFile);
26173
- try {
26174
- await ensureWorkspaceRules(targetPath, repositories, skillsIndexRefs);
26175
- } catch (error) {
26176
- results.push({
26177
- source: "WORKSPACE-RULES",
26178
- destination: targetPath,
26179
- action: "failed",
26180
- error: error instanceof Error ? error.message : "Failed to inject WORKSPACE-RULES"
26181
- });
26182
- }
26262
+ const existing = entry.skills ?? [];
26263
+ if (existing.includes(skillName)) {
26264
+ return {
26265
+ success: false,
26266
+ error: `Skill '${skillKey}' is already enabled`
26267
+ };
26183
26268
  }
26269
+ entry.skills = [...existing, skillName];
26270
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26271
+ return { success: true };
26272
+ } catch (error) {
26273
+ return {
26274
+ success: false,
26275
+ error: error instanceof Error ? error.message : String(error)
26276
+ };
26184
26277
  }
26185
- return results;
26186
26278
  }
26187
- var import_micromatch, AGENT_FILES2;
26188
- var init_transform = __esm(() => {
26189
- init_glob_patterns();
26190
- init_client_mapping();
26191
- init_constants();
26192
- init_plugin_path();
26193
- init_symlink();
26194
- init_link_adjuster();
26195
- init_skill();
26196
- import_micromatch = __toESM(require_micromatch(), 1);
26197
- AGENT_FILES2 = ["AGENTS.md", "CLAUDE.md"];
26198
- });
26199
-
26200
- // src/models/marketplace-manifest.ts
26201
- var UrlSourceSchema, GitHubSourceSchema, PluginSourceRefSchema, ContactSchema, LspServerSchema, MarketplacePluginEntrySchema, MarketplaceManifestSchema, MarketplaceManifestLenientSchema;
26202
- var init_marketplace_manifest = __esm(() => {
26203
- init_zod();
26204
- UrlSourceSchema = exports_external.object({
26205
- source: exports_external.literal("url"),
26206
- url: exports_external.string().url()
26207
- });
26208
- GitHubSourceSchema = exports_external.object({
26209
- source: exports_external.literal("github"),
26210
- repo: exports_external.string().min(1)
26211
- }).transform((val) => ({
26212
- source: "url",
26213
- url: `https://github.com/${val.repo}`
26214
- }));
26215
- PluginSourceRefSchema = exports_external.union([exports_external.string(), UrlSourceSchema, GitHubSourceSchema]);
26216
- ContactSchema = exports_external.object({
26217
- name: exports_external.string(),
26218
- email: exports_external.string().optional()
26219
- });
26220
- LspServerSchema = exports_external.object({
26221
- command: exports_external.string(),
26222
- args: exports_external.array(exports_external.string()).optional(),
26223
- extensionToLanguage: exports_external.record(exports_external.string()).optional(),
26224
- startupTimeout: exports_external.number().optional()
26225
- });
26226
- MarketplacePluginEntrySchema = exports_external.object({
26227
- name: exports_external.string().min(1),
26228
- description: exports_external.string().min(1),
26229
- source: PluginSourceRefSchema,
26230
- version: exports_external.string().optional(),
26231
- author: ContactSchema.optional(),
26232
- category: exports_external.string().optional(),
26233
- homepage: exports_external.string().optional(),
26234
- strict: exports_external.boolean().optional(),
26235
- tags: exports_external.array(exports_external.string()).optional(),
26236
- skills: exports_external.array(exports_external.string()).optional(),
26237
- lspServers: exports_external.record(LspServerSchema).optional()
26238
- });
26239
- MarketplaceManifestSchema = exports_external.object({
26240
- $schema: exports_external.string().optional(),
26241
- name: exports_external.string().min(1),
26242
- version: exports_external.string().optional(),
26243
- description: exports_external.string().min(1),
26244
- owner: ContactSchema.optional(),
26245
- plugins: exports_external.array(MarketplacePluginEntrySchema)
26246
- });
26247
- MarketplaceManifestLenientSchema = exports_external.object({
26248
- name: exports_external.string().optional(),
26249
- plugins: exports_external.array(exports_external.unknown())
26250
- }).passthrough();
26251
- });
26252
-
26253
- // src/utils/marketplace-manifest-parser.ts
26254
- import { readFile as readFile4 } from "node:fs/promises";
26255
- import { existsSync as existsSync5 } from "node:fs";
26256
- import { join as join8, resolve as resolve5 } from "node:path";
26257
- async function parseMarketplaceManifest(marketplacePath) {
26258
- const manifestPath = join8(marketplacePath, MANIFEST_PATH);
26259
- if (!existsSync5(manifestPath)) {
26279
+ async function removeEnabledSkill(skillKey, workspacePath = process.cwd()) {
26280
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26281
+ if (!existsSync5(configPath)) {
26260
26282
  return {
26261
26283
  success: false,
26262
- error: `Marketplace manifest not found: ${manifestPath}`
26284
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
26263
26285
  };
26264
26286
  }
26265
- let raw;
26266
- try {
26267
- raw = await readFile4(manifestPath, "utf-8");
26268
- } catch (err) {
26287
+ const parsed = parseSkillKey(skillKey);
26288
+ if (!parsed) {
26269
26289
  return {
26270
26290
  success: false,
26271
- error: `Failed to read marketplace manifest: ${err instanceof Error ? err.message : String(err)}`
26291
+ error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
26272
26292
  };
26273
26293
  }
26274
- let json2;
26294
+ const { pluginName, skillName } = parsed;
26275
26295
  try {
26276
- json2 = JSON.parse(raw);
26277
- } catch {
26296
+ const content = await readFile4(configPath, "utf-8");
26297
+ const config = load(content);
26298
+ const index = findPluginEntryByName(config, pluginName);
26299
+ if (index === -1) {
26300
+ return {
26301
+ success: false,
26302
+ error: `Plugin '${pluginName}' not found in workspace config`
26303
+ };
26304
+ }
26305
+ const entry = config.plugins[index];
26306
+ if (!entry) {
26307
+ return { success: false, error: `Plugin '${pluginName}' not found in workspace config` };
26308
+ }
26309
+ if (typeof entry === "string" || !entry.skills || !Array.isArray(entry.skills)) {
26310
+ return {
26311
+ success: false,
26312
+ error: `Skill '${skillKey}' is already disabled`
26313
+ };
26314
+ }
26315
+ if (!entry.skills.includes(skillName)) {
26316
+ return {
26317
+ success: false,
26318
+ error: `Skill '${skillKey}' is already disabled`
26319
+ };
26320
+ }
26321
+ const newSkills = entry.skills.filter((s) => s !== skillName);
26322
+ entry.skills = newSkills;
26323
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26324
+ return { success: true };
26325
+ } catch (error) {
26278
26326
  return {
26279
26327
  success: false,
26280
- error: "Failed to parse marketplace.json as JSON: invalid syntax"
26328
+ error: error instanceof Error ? error.message : String(error)
26281
26329
  };
26282
26330
  }
26283
- const strictResult = MarketplaceManifestSchema.safeParse(json2);
26284
- if (strictResult.success) {
26285
- return { success: true, data: strictResult.data, warnings: [] };
26286
- }
26287
- return parseLeniently(json2);
26288
26331
  }
26289
- function parseLeniently(json2) {
26290
- const lenientResult = MarketplaceManifestLenientSchema.safeParse(json2);
26291
- if (!lenientResult.success) {
26332
+ async function setPluginSkillsMode(pluginName, mode, skillNames, workspacePath = process.cwd()) {
26333
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26334
+ if (!existsSync5(configPath)) {
26292
26335
  return {
26293
26336
  success: false,
26294
- error: 'Marketplace manifest must contain a "plugins" array'
26337
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
26295
26338
  };
26296
26339
  }
26297
- const raw = lenientResult.data;
26298
- const warnings = [];
26299
- const obj = json2;
26300
- const validPlugins = [];
26301
- for (let i2 = 0;i2 < raw.plugins.length; i2++) {
26302
- const entry = raw.plugins[i2];
26303
- const entryResult = MarketplacePluginEntrySchema.safeParse(entry);
26304
- if (entryResult.success) {
26305
- validPlugins.push(entryResult.data);
26306
- continue;
26340
+ try {
26341
+ const content = await readFile4(configPath, "utf-8");
26342
+ const config = load(content);
26343
+ const index = findPluginEntryByName(config, pluginName);
26344
+ if (index === -1) {
26345
+ return {
26346
+ success: false,
26347
+ error: `Plugin '${pluginName}' not found in workspace config`
26348
+ };
26307
26349
  }
26308
- const extracted = extractPluginEntry(entry, i2, warnings);
26309
- if (extracted) {
26310
- validPlugins.push(extracted);
26350
+ const entry = ensureObjectPluginEntry(config, index);
26351
+ if (mode === "allowlist") {
26352
+ entry.skills = [...skillNames];
26353
+ } else {
26354
+ entry.skills = skillNames.length > 0 ? { exclude: [...skillNames] } : undefined;
26311
26355
  }
26356
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26357
+ return { success: true };
26358
+ } catch (error) {
26359
+ return {
26360
+ success: false,
26361
+ error: error instanceof Error ? error.message : String(error)
26362
+ };
26312
26363
  }
26313
- const data = {
26314
- name: typeof raw.name === "string" ? raw.name : "unknown",
26315
- description: typeof obj.description === "string" ? obj.description : "",
26316
- plugins: validPlugins
26317
- };
26318
- return { success: true, data, warnings };
26319
26364
  }
26320
- function extractPluginEntry(entry, index, warnings) {
26321
- if (!entry || typeof entry !== "object") {
26322
- warnings.push(`plugins[${index}]: not an object, skipped`);
26323
- return null;
26365
+ async function upsertGitHubPluginSourceAllowlist(source, skillNames, workspacePath = process.cwd()) {
26366
+ await ensureWorkspace(workspacePath);
26367
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26368
+ if (!existsSync5(configPath)) {
26369
+ return {
26370
+ success: false,
26371
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
26372
+ };
26324
26373
  }
26325
- const obj = entry;
26326
- const name = typeof obj.name === "string" && obj.name ? obj.name : undefined;
26327
- if (!name) {
26328
- warnings.push(`plugins[${index}]: missing "name" field, skipped`);
26329
- return null;
26374
+ try {
26375
+ const content = await readFile4(configPath, "utf-8");
26376
+ const config = load(content);
26377
+ const result = await upsertGitHubPluginSourceAllowlistInConfig(config, source, skillNames);
26378
+ if (!result.success)
26379
+ return result;
26380
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26381
+ return result;
26382
+ } catch (error) {
26383
+ return {
26384
+ success: false,
26385
+ error: error instanceof Error ? error.message : String(error)
26386
+ };
26330
26387
  }
26331
- let description = "";
26332
- if (typeof obj.description === "string" && obj.description) {
26333
- description = obj.description;
26334
- } else if (obj.metadata && typeof obj.metadata === "object" && typeof obj.metadata.description === "string") {
26335
- description = obj.metadata.description;
26336
- warnings.push(`plugins[${index}] ("${name}"): "description" found in metadata instead of top level`);
26337
- } else {
26338
- warnings.push(`plugins[${index}] ("${name}"): missing "description" field`);
26388
+ }
26389
+ function pruneEnabledSkillsForPlugin(config, pluginEntry) {
26390
+ if (!config.enabledSkills?.length)
26391
+ return;
26392
+ const names = extractPluginNames(pluginEntry);
26393
+ if (names.length === 0)
26394
+ return;
26395
+ const prefixes = names.map((n) => `${n}:`);
26396
+ config.enabledSkills = config.enabledSkills.filter((s) => !prefixes.some((p) => s.startsWith(p)));
26397
+ if (config.enabledSkills.length === 0) {
26398
+ config.enabledSkills = undefined;
26339
26399
  }
26340
- let source = "";
26341
- const sourceResult = PluginSourceRefSchema.safeParse(obj.source);
26342
- if (sourceResult.success) {
26343
- source = sourceResult.data;
26344
- } else {
26345
- warnings.push(`plugins[${index}] ("${name}"): missing or invalid "source" field`);
26400
+ }
26401
+ async function resolveGitHubIdentity(pluginSource) {
26402
+ if (isGitHubUrl(pluginSource)) {
26403
+ const parsed = parseGitHubUrl(pluginSource);
26404
+ return parsed ? `${parsed.owner}/${parsed.repo}`.toLowerCase() : null;
26346
26405
  }
26347
- return {
26348
- name,
26349
- description,
26350
- source,
26351
- ...typeof obj.version === "string" && { version: obj.version },
26352
- ...typeof obj.category === "string" && { category: obj.category },
26353
- ...typeof obj.homepage === "string" && { homepage: obj.homepage },
26354
- ...Array.isArray(obj.skills) && obj.skills.every((s) => typeof s === "string") && { skills: obj.skills }
26355
- };
26406
+ if (isPluginSpec(pluginSource)) {
26407
+ const parsed = parsePluginSpec(pluginSource);
26408
+ if (!parsed)
26409
+ return null;
26410
+ const marketplace = await getMarketplace(parsed.marketplaceName);
26411
+ if (!marketplace)
26412
+ return null;
26413
+ const manifestResult = await parseMarketplaceManifest(marketplace.path);
26414
+ if (!manifestResult.success)
26415
+ return null;
26416
+ const entry = manifestResult.data.plugins.find((p) => p.name === parsed.plugin);
26417
+ if (!entry || typeof entry.source === "string")
26418
+ return null;
26419
+ const parsedUrl = parseGitHubUrl(entry.source.url);
26420
+ return parsedUrl ? `${parsedUrl.owner}/${parsedUrl.repo}`.toLowerCase() : null;
26421
+ }
26422
+ return null;
26356
26423
  }
26357
- function resolvePluginSourcePath(source, marketplacePath) {
26358
- if (typeof source === "object") {
26359
- return source.url;
26424
+ async function migrateWorkspaceSkillsV1toV2(workspacePath) {
26425
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26426
+ if (!existsSync5(configPath))
26427
+ return;
26428
+ let config;
26429
+ try {
26430
+ const content = await readFile4(configPath, "utf-8");
26431
+ config = load(content);
26432
+ } catch {
26433
+ return;
26434
+ }
26435
+ if (!config || config.version !== undefined && config.version >= 2)
26436
+ return;
26437
+ const enabledSkills = config.enabledSkills ?? [];
26438
+ const disabledSkills = config.disabledSkills ?? [];
26439
+ const enabledByPlugin = new Map;
26440
+ for (const skillKey of enabledSkills) {
26441
+ const parsed = parseSkillKey(skillKey);
26442
+ if (!parsed)
26443
+ continue;
26444
+ const list = enabledByPlugin.get(parsed.pluginName) ?? [];
26445
+ list.push(parsed.skillName);
26446
+ enabledByPlugin.set(parsed.pluginName, list);
26447
+ }
26448
+ const disabledByPlugin = new Map;
26449
+ for (const skillKey of disabledSkills) {
26450
+ const parsed = parseSkillKey(skillKey);
26451
+ if (!parsed)
26452
+ continue;
26453
+ const list = disabledByPlugin.get(parsed.pluginName) ?? [];
26454
+ list.push(parsed.skillName);
26455
+ disabledByPlugin.set(parsed.pluginName, list);
26456
+ }
26457
+ for (const [pluginName, skillNames] of enabledByPlugin) {
26458
+ const index = findPluginEntryByName(config, pluginName);
26459
+ if (index === -1) {
26460
+ console.warn(`[migrate v1→v2] No plugin found for '${pluginName}', skipping`);
26461
+ continue;
26462
+ }
26463
+ const entry = ensureObjectPluginEntry(config, index);
26464
+ entry.skills = skillNames;
26465
+ }
26466
+ for (const [pluginName, skillNames] of disabledByPlugin) {
26467
+ const index = findPluginEntryByName(config, pluginName);
26468
+ if (index === -1) {
26469
+ console.warn(`[migrate v1→v2] No plugin found for '${pluginName}', skipping`);
26470
+ continue;
26471
+ }
26472
+ const entry = ensureObjectPluginEntry(config, index);
26473
+ if (!Array.isArray(entry.skills)) {
26474
+ entry.skills = { exclude: skillNames };
26475
+ }
26476
+ }
26477
+ config.enabledSkills = undefined;
26478
+ config.disabledSkills = undefined;
26479
+ config.version = 2;
26480
+ try {
26481
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26482
+ } catch (error) {
26483
+ console.warn(`[migrate v1→v2] Failed to write migrated config: ${error instanceof Error ? error.message : String(error)}`);
26360
26484
  }
26361
- return resolve5(marketplacePath, source);
26362
26485
  }
26363
- var MANIFEST_PATH = ".claude-plugin/marketplace.json";
26364
- var init_marketplace_manifest_parser = __esm(() => {
26365
- init_marketplace_manifest();
26486
+ async function updateRepositories(changes, workspacePath = process.cwd()) {
26487
+ if (changes.remove.length === 0 && changes.add.length === 0) {
26488
+ return { success: true };
26489
+ }
26490
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26491
+ try {
26492
+ const content = await readFile4(configPath, "utf-8");
26493
+ const config = load(content);
26494
+ const removeSet = new Set(changes.remove);
26495
+ config.repositories = config.repositories.filter((repo) => !removeSet.has(repo.path));
26496
+ config.repositories.push(...changes.add);
26497
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26498
+ return { success: true };
26499
+ } catch (error) {
26500
+ return {
26501
+ success: false,
26502
+ error: error instanceof Error ? error.message : String(error)
26503
+ };
26504
+ }
26505
+ }
26506
+ async function setRepositories(repositories, workspacePath = process.cwd()) {
26507
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26508
+ try {
26509
+ const content = await readFile4(configPath, "utf-8");
26510
+ const config = load(content);
26511
+ config.repositories = repositories;
26512
+ await writeFile(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26513
+ return { success: true };
26514
+ } catch (error) {
26515
+ return {
26516
+ success: false,
26517
+ error: error instanceof Error ? error.message : String(error)
26518
+ };
26519
+ }
26520
+ }
26521
+ var DEFAULT_PROJECT_CLIENTS;
26522
+ var init_workspace_modify = __esm(() => {
26523
+ init_js_yaml();
26524
+ init_constants();
26525
+ init_workspace_config();
26526
+ init_marketplace_manifest_parser();
26527
+ init_plugin_path();
26528
+ init_marketplace();
26529
+ DEFAULT_PROJECT_CLIENTS = ["universal"];
26366
26530
  });
26367
26531
 
26368
26532
  // src/core/user-workspace.ts
26369
26533
  var exports_user_workspace = {};
26370
26534
  __export(exports_user_workspace, {
26535
+ upsertUserGitHubPluginSourceAllowlist: () => upsertUserGitHubPluginSourceAllowlist,
26371
26536
  setUserPluginSkillsMode: () => setUserPluginSkillsMode,
26372
26537
  setUserClients: () => setUserClients,
26373
26538
  removeUserPluginsForMarketplace: () => removeUserPluginsForMarketplace,
@@ -26391,7 +26556,7 @@ __export(exports_user_workspace, {
26391
26556
  });
26392
26557
  import { existsSync as existsSync6 } from "node:fs";
26393
26558
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile2 } from "node:fs/promises";
26394
- import { basename as basename3, join as join9, resolve as resolve6 } from "node:path";
26559
+ import { basename as basename2, join as join9, resolve as resolve6 } from "node:path";
26395
26560
  function getUserWorkspaceConfigPath() {
26396
26561
  return join9(getAllagentsDir(), WORKSPACE_CONFIG_FILE);
26397
26562
  }
@@ -26611,7 +26776,7 @@ async function setUserClients(clients) {
26611
26776
  };
26612
26777
  }
26613
26778
  }
26614
- function parseSkillKey(skillKey) {
26779
+ function parseSkillKey2(skillKey) {
26615
26780
  const colonIdx = skillKey.indexOf(":");
26616
26781
  if (colonIdx === -1)
26617
26782
  return null;
@@ -26644,7 +26809,7 @@ async function getUserDisabledSkills() {
26644
26809
  async function addUserDisabledSkill(skillKey) {
26645
26810
  await ensureUserWorkspace();
26646
26811
  const configPath = getUserWorkspaceConfigPath();
26647
- const parsed = parseSkillKey(skillKey);
26812
+ const parsed = parseSkillKey2(skillKey);
26648
26813
  if (!parsed) {
26649
26814
  return {
26650
26815
  success: false,
@@ -26689,7 +26854,7 @@ async function addUserDisabledSkill(skillKey) {
26689
26854
  async function removeUserDisabledSkill(skillKey) {
26690
26855
  await ensureUserWorkspace();
26691
26856
  const configPath = getUserWorkspaceConfigPath();
26692
- const parsed = parseSkillKey(skillKey);
26857
+ const parsed = parseSkillKey2(skillKey);
26693
26858
  if (!parsed) {
26694
26859
  return {
26695
26860
  success: false,
@@ -26758,7 +26923,7 @@ async function getUserEnabledSkills() {
26758
26923
  async function addUserEnabledSkill(skillKey) {
26759
26924
  await ensureUserWorkspace();
26760
26925
  const configPath = getUserWorkspaceConfigPath();
26761
- const parsed = parseSkillKey(skillKey);
26926
+ const parsed = parseSkillKey2(skillKey);
26762
26927
  if (!parsed) {
26763
26928
  return {
26764
26929
  success: false,
@@ -26803,7 +26968,7 @@ async function addUserEnabledSkill(skillKey) {
26803
26968
  async function removeUserEnabledSkill(skillKey) {
26804
26969
  await ensureUserWorkspace();
26805
26970
  const configPath = getUserWorkspaceConfigPath();
26806
- const parsed = parseSkillKey(skillKey);
26971
+ const parsed = parseSkillKey2(skillKey);
26807
26972
  if (!parsed) {
26808
26973
  return {
26809
26974
  success: false,
@@ -26876,6 +27041,24 @@ async function setUserPluginSkillsMode(pluginName, mode, skillNames) {
26876
27041
  };
26877
27042
  }
26878
27043
  }
27044
+ async function upsertUserGitHubPluginSourceAllowlist(source, skillNames) {
27045
+ await ensureUserWorkspace();
27046
+ const configPath = getUserWorkspaceConfigPath();
27047
+ try {
27048
+ const content = await readFile5(configPath, "utf-8");
27049
+ const config = load(content);
27050
+ const result = await upsertGitHubPluginSourceAllowlistInConfig(config, source, skillNames);
27051
+ if (!result.success)
27052
+ return result;
27053
+ await writeFile2(configPath, dump(config, { lineWidth: -1 }), "utf-8");
27054
+ return result;
27055
+ } catch (error) {
27056
+ return {
27057
+ success: false,
27058
+ error: error instanceof Error ? error.message : String(error)
27059
+ };
27060
+ }
27061
+ }
26879
27062
  function pluginSourceToInfo(plugin, scope) {
26880
27063
  const parsed = parsePluginSpec(plugin);
26881
27064
  if (parsed) {
@@ -26890,14 +27073,14 @@ function pluginSourceToInfo(plugin, scope) {
26890
27073
  const ghParsed = parseGitHubUrl(plugin);
26891
27074
  return {
26892
27075
  spec: plugin,
26893
- name: ghParsed?.repo ?? basename3(plugin),
27076
+ name: ghParsed?.repo ?? basename2(plugin),
26894
27077
  marketplace: "",
26895
27078
  scope
26896
27079
  };
26897
27080
  }
26898
27081
  return {
26899
27082
  spec: plugin,
26900
- name: basename3(plugin),
27083
+ name: basename2(plugin),
26901
27084
  marketplace: "",
26902
27085
  scope
26903
27086
  };
@@ -27021,8 +27204,8 @@ var init_user_workspace = __esm(() => {
27021
27204
 
27022
27205
  // src/core/marketplace.ts
27023
27206
  import { existsSync as existsSync7 } from "node:fs";
27024
- import { mkdir as mkdir5, readFile as readFile6, readdir as readdir3, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
27025
- import { basename as basename4, dirname as dirname5, join as join10, resolve as resolve7 } from "node:path";
27207
+ import { mkdir as mkdir5, readFile as readFile6, readdir as readdir2, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
27208
+ import { basename as basename3, dirname as dirname4, join as join10, resolve as resolve7 } from "node:path";
27026
27209
  function parseLocation(location) {
27027
27210
  const [owner = "", repo = "", ...rest] = location.split("/");
27028
27211
  const branch = rest.length > 0 ? rest.join("/") : undefined;
@@ -27052,7 +27235,7 @@ async function loadRegistryFromPath(registryPath) {
27052
27235
  }
27053
27236
  }
27054
27237
  async function saveRegistryToPath(registry, registryPath) {
27055
- const dir = dirname5(registryPath);
27238
+ const dir = dirname4(registryPath);
27056
27239
  if (!existsSync7(dir)) {
27057
27240
  await mkdir5(dir, { recursive: true });
27058
27241
  }
@@ -27108,7 +27291,7 @@ function parseMarketplaceSource(source) {
27108
27291
  };
27109
27292
  }
27110
27293
  const absPath = resolve7(source);
27111
- const name = source.split(/[/\\]/).filter(Boolean).pop() || basename4(absPath) || "local";
27294
+ const name = source.split(/[/\\]/).filter(Boolean).pop() || basename3(absPath) || "local";
27112
27295
  return {
27113
27296
  type: "local",
27114
27297
  location: absPath,
@@ -27458,7 +27641,7 @@ async function listMarketplacePlugins(name, workspacePath) {
27458
27641
  return { plugins: [], warnings: manifestResult.warnings };
27459
27642
  }
27460
27643
  try {
27461
- const entries = await readdir3(pluginsDir, { withFileTypes: true });
27644
+ const entries = await readdir2(pluginsDir, { withFileTypes: true });
27462
27645
  const plugins = entries.filter((e) => e.isDirectory()).map((e) => ({
27463
27646
  name: e.name,
27464
27647
  path: join10(pluginsDir, e.name)
@@ -27810,653 +27993,762 @@ var init_marketplace = __esm(() => {
27810
27993
  updatedMarketplaceCache = new Set;
27811
27994
  });
27812
27995
 
27813
- // src/core/workspace-modify.ts
27996
+ // src/core/skills.ts
27814
27997
  import { existsSync as existsSync8 } from "node:fs";
27815
- import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
27816
- import { join as join11 } from "node:path";
27817
- async function setClients(clients, workspacePath = process.cwd()) {
27818
- try {
27819
- await ensureWorkspace(workspacePath);
27820
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
27821
- const content = await readFile7(configPath, "utf-8");
27822
- const config = load(content);
27823
- config.clients = clients;
27824
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
27825
- return { success: true };
27826
- } catch (error) {
27998
+ import { readFile as readFile7, readdir as readdir3 } from "node:fs/promises";
27999
+ import { join as join11, basename as basename4, resolve as resolve8 } from "node:path";
28000
+ async function resolvePluginPath(pluginSource, workspacePath) {
28001
+ if (isPluginSpec(pluginSource)) {
28002
+ const resolved2 = await resolvePluginSpecWithAutoRegister(pluginSource, {
28003
+ offline: true,
28004
+ workspacePath
28005
+ });
28006
+ if (!resolved2.success || !resolved2.path)
28007
+ return null;
27827
28008
  return {
27828
- success: false,
27829
- error: error instanceof Error ? error.message : String(error)
28009
+ path: resolved2.path,
28010
+ pluginName: resolved2.pluginName
27830
28011
  };
27831
28012
  }
27832
- }
27833
- async function ensureWorkspace(workspacePath, clients) {
27834
- const configDir = join11(workspacePath, CONFIG_DIR);
27835
- const configPath = join11(configDir, WORKSPACE_CONFIG_FILE);
27836
- if (existsSync8(configPath))
27837
- return;
27838
- const defaultConfig = {
27839
- repositories: [],
27840
- plugins: [],
27841
- clients: clients ?? [...DEFAULT_PROJECT_CLIENTS]
27842
- };
27843
- await mkdir6(configDir, { recursive: true });
27844
- await writeFile4(configPath, dump(defaultConfig, { lineWidth: -1 }), "utf-8");
27845
- }
27846
- async function addPlugin(plugin, workspacePath = process.cwd(), force) {
27847
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
27848
- await ensureWorkspace(workspacePath);
27849
- if (isPluginSpec(plugin)) {
27850
- const resolved = await resolvePluginSpecWithAutoRegister(plugin, { workspacePath });
27851
- if (!resolved.success) {
27852
- return {
27853
- success: false,
27854
- error: resolved.error || "Unknown error"
27855
- };
27856
- }
27857
- return await addPluginToConfig(resolved.registeredAs ? plugin.replace(/@[^@]+$/, `@${resolved.registeredAs}`) : plugin, configPath, resolved.registeredAs, force);
27858
- }
27859
- if (isGitHubUrl(plugin)) {
27860
- const validation = validatePluginSource(plugin);
27861
- if (!validation.valid) {
27862
- return {
27863
- success: false,
27864
- error: validation.error || "Invalid GitHub URL"
27865
- };
27866
- }
27867
- const verifyResult = await verifyGitHubUrlExists(plugin);
27868
- if (!verifyResult.exists) {
27869
- return {
27870
- success: false,
27871
- error: verifyResult.error || `GitHub URL not found: ${plugin}`
27872
- };
27873
- }
27874
- } else {
27875
- const fullPath = join11(workspacePath, plugin);
27876
- if (!existsSync8(fullPath) && !existsSync8(plugin)) {
27877
- return {
27878
- success: false,
27879
- error: `Plugin not found at ${plugin}`
27880
- };
27881
- }
27882
- }
27883
- return await addPluginToConfig(plugin, configPath, undefined, force);
27884
- }
27885
- async function addPluginToConfig(plugin, configPath, autoRegistered, force) {
27886
- try {
27887
- const content = await readFile7(configPath, "utf-8");
27888
- const config = load(content);
27889
- const existingExactIndex = config.plugins.findIndex((entry) => getPluginSource(entry) === plugin);
27890
- if (existingExactIndex !== -1) {
27891
- if (!force) {
27892
- return {
27893
- success: false,
27894
- error: `Plugin already exists in .allagents/workspace.yaml: ${plugin}`
27895
- };
27896
- }
27897
- }
27898
- if (!force) {
27899
- const newIdentity = await resolveGitHubIdentity(plugin);
27900
- if (newIdentity) {
27901
- for (const existing of config.plugins) {
27902
- const existingSource = getPluginSource(existing);
27903
- const existingIdentity = await resolveGitHubIdentity(existingSource);
27904
- if (existingIdentity === newIdentity) {
27905
- return {
27906
- success: false,
27907
- error: `Plugin duplicates existing entry '${existingSource}': both resolve to ${newIdentity}`
27908
- };
27909
- }
27910
- }
27911
- }
27912
- }
27913
- const wasReplaced = force && existingExactIndex !== -1;
27914
- if (wasReplaced) {
27915
- config.plugins.splice(existingExactIndex, 1);
27916
- }
27917
- config.plugins.push(plugin);
27918
- const newContent = dump(config, { lineWidth: -1 });
27919
- await writeFile4(configPath, newContent, "utf-8");
27920
- const result = {
27921
- success: true,
27922
- normalizedPlugin: plugin
27923
- };
27924
- if (autoRegistered) {
27925
- result.autoRegistered = autoRegistered;
27926
- }
27927
- if (wasReplaced) {
27928
- result.replaced = true;
27929
- }
27930
- return result;
27931
- } catch (error) {
27932
- return {
27933
- success: false,
27934
- error: error instanceof Error ? error.message : String(error)
27935
- };
28013
+ if (isGitHubUrl(pluginSource)) {
28014
+ const parsed = parseGitHubUrl(pluginSource);
28015
+ const result = await fetchPlugin(pluginSource, {
28016
+ offline: true,
28017
+ ...parsed?.branch && { branch: parsed.branch }
28018
+ });
28019
+ if (!result.success)
28020
+ return null;
28021
+ const path = parsed?.subpath ? join11(result.cachePath, parsed.subpath) : result.cachePath;
28022
+ return existsSync8(path) ? { path } : null;
27936
28023
  }
28024
+ const resolved = resolve8(workspacePath, pluginSource);
28025
+ return existsSync8(resolved) ? { path: resolved } : null;
27937
28026
  }
27938
- async function hasPlugin(plugin, workspacePath = process.cwd()) {
27939
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
27940
- if (!existsSync8(configPath))
27941
- return false;
27942
- try {
27943
- const content = await readFile7(configPath, "utf-8");
27944
- const config = load(content);
27945
- if (config.plugins.some((entry) => getPluginSource(entry) === plugin))
27946
- return true;
27947
- if (!isPluginSpec(plugin)) {
27948
- return config.plugins.some((entry) => {
27949
- const source = getPluginSource(entry);
27950
- return source.startsWith(`${plugin}@`) || source === plugin;
27951
- });
28027
+ async function discoverNestedSkillEntries(pluginPath) {
28028
+ const entries = await readdir3(pluginPath, { withFileTypes: true });
28029
+ const discovered = [];
28030
+ for (const entry of entries) {
28031
+ if (!entry.isDirectory())
28032
+ continue;
28033
+ const skillPath = join11(pluginPath, entry.name);
28034
+ if (existsSync8(join11(skillPath, "SKILL.md"))) {
28035
+ discovered.push({ name: entry.name, skillPath });
28036
+ continue;
27952
28037
  }
27953
- return false;
27954
- } catch {
27955
- return false;
28038
+ discovered.push(...await discoverNestedSkillEntries(skillPath));
27956
28039
  }
28040
+ return discovered;
27957
28041
  }
27958
- async function removePlugin(plugin, workspacePath = process.cwd()) {
28042
+ async function getAllSkillsFromPlugins(workspacePath = process.cwd()) {
27959
28043
  const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
27960
28044
  if (!existsSync8(configPath)) {
27961
- return {
27962
- success: false,
27963
- error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
27964
- Run 'allagents workspace init <path>' to create a new workspace`
27965
- };
28045
+ return [];
27966
28046
  }
27967
- try {
27968
- const content = await readFile7(configPath, "utf-8");
27969
- const config = load(content);
27970
- let index = config.plugins.findIndex((entry) => getPluginSource(entry) === plugin);
27971
- if (index === -1 && isPluginSpec(plugin) === false) {
27972
- index = config.plugins.findIndex((entry) => {
27973
- const source = getPluginSource(entry);
27974
- return source.startsWith(`${plugin}@`) || source === plugin;
27975
- });
28047
+ const content = await readFile7(configPath, "utf-8");
28048
+ const config = load(content);
28049
+ const isV1Fallback = config.version === undefined || config.version < 2;
28050
+ const disabledSkills = isV1Fallback ? new Set(config.disabledSkills ?? []) : new Set;
28051
+ const enabledSkills = isV1Fallback && config.enabledSkills ? new Set(config.enabledSkills) : null;
28052
+ const skills = [];
28053
+ for (const pluginEntry of config.plugins) {
28054
+ const pluginSource = getPluginSource(pluginEntry);
28055
+ const resolved = await resolvePluginPath(pluginSource, workspacePath);
28056
+ if (!resolved)
28057
+ continue;
28058
+ const pluginPath = resolved.path;
28059
+ const pluginName = resolved.pluginName ?? getPluginName(pluginPath);
28060
+ const skillsDir = join11(pluginPath, "skills");
28061
+ const pluginSkillsConfig = typeof pluginEntry === "string" ? undefined : pluginEntry.skills;
28062
+ const hasEnabledEntries = !pluginSkillsConfig && enabledSkills && [...enabledSkills].some((s) => s.startsWith(`${pluginName}`));
28063
+ let skillEntries;
28064
+ if (existsSync8(skillsDir)) {
28065
+ const entries = await readdir3(skillsDir, { withFileTypes: true });
28066
+ skillEntries = entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name, skillPath: join11(skillsDir, e.name) }));
28067
+ } else {
28068
+ const nestedSkills = await discoverNestedSkillEntries(pluginPath);
28069
+ if (nestedSkills.length > 0) {
28070
+ skillEntries = nestedSkills;
28071
+ } else {
28072
+ const rootSkillMd = join11(pluginPath, "SKILL.md");
28073
+ if (existsSync8(rootSkillMd)) {
28074
+ const skillContent = await readFile7(rootSkillMd, "utf-8");
28075
+ const metadata = parseSkillMetadata(skillContent);
28076
+ const skillName = metadata?.name ?? basename4(pluginPath);
28077
+ skillEntries = [{ name: skillName, skillPath: pluginPath }];
28078
+ } else {
28079
+ skillEntries = [];
28080
+ }
28081
+ }
27976
28082
  }
27977
- if (index === -1) {
27978
- const identity = await resolveGitHubIdentity(plugin);
27979
- if (identity) {
27980
- for (let i2 = 0;i2 < config.plugins.length; i2++) {
27981
- const p = config.plugins[i2];
27982
- if (!p)
27983
- continue;
27984
- const existing = await resolveGitHubIdentity(getPluginSource(p));
27985
- if (existing === identity) {
27986
- index = i2;
27987
- break;
27988
- }
28083
+ const pluginSkillsMode = pluginSkillsConfig === undefined ? "none" : Array.isArray(pluginSkillsConfig) ? "allowlist" : "blocklist";
28084
+ for (const { name, skillPath } of skillEntries) {
28085
+ const skillKey = `${pluginName}:${name}`;
28086
+ let isDisabled;
28087
+ if (pluginSkillsConfig !== undefined) {
28088
+ if (Array.isArray(pluginSkillsConfig)) {
28089
+ isDisabled = !pluginSkillsConfig.includes(name);
28090
+ } else {
28091
+ isDisabled = pluginSkillsConfig.exclude.includes(name);
27989
28092
  }
28093
+ } else if (isV1Fallback) {
28094
+ isDisabled = hasEnabledEntries ? !enabledSkills?.has(skillKey) : disabledSkills.has(skillKey);
28095
+ } else {
28096
+ isDisabled = false;
27990
28097
  }
28098
+ skills.push({
28099
+ name,
28100
+ pluginName,
28101
+ pluginSource,
28102
+ path: skillPath,
28103
+ disabled: isDisabled,
28104
+ pluginSkillsMode
28105
+ });
27991
28106
  }
27992
- if (index === -1) {
27993
- return {
27994
- success: false,
27995
- error: `Plugin not found in .allagents/workspace.yaml: ${plugin}`
27996
- };
28107
+ }
28108
+ return skills;
28109
+ }
28110
+ async function findSkillByName(skillName, workspacePath = process.cwd()) {
28111
+ const allSkills = await getAllSkillsFromPlugins(workspacePath);
28112
+ return allSkills.filter((s) => s.name === skillName);
28113
+ }
28114
+ async function discoverSkillNames(pluginPath) {
28115
+ if (!existsSync8(pluginPath))
28116
+ return [];
28117
+ const skillsDir = join11(pluginPath, "skills");
28118
+ if (existsSync8(skillsDir)) {
28119
+ const entries = await readdir3(skillsDir, { withFileTypes: true });
28120
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
28121
+ }
28122
+ const nestedSkills = await discoverNestedSkillEntries(pluginPath);
28123
+ if (nestedSkills.length > 0)
28124
+ return nestedSkills.map((entry) => entry.name);
28125
+ const rootSkillMd = join11(pluginPath, "SKILL.md");
28126
+ if (existsSync8(rootSkillMd)) {
28127
+ try {
28128
+ const content = await readFile7(rootSkillMd, "utf-8");
28129
+ const { parseSkillMetadata: parseSkillMetadata2 } = await Promise.resolve().then(() => (init_skill(), exports_skill));
28130
+ const metadata = parseSkillMetadata2(content);
28131
+ return [metadata?.name ?? basename4(pluginPath)];
28132
+ } catch {
28133
+ return [basename4(pluginPath)];
27997
28134
  }
27998
- const removedEntry = getPluginSource(config.plugins[index]);
27999
- config.plugins.splice(index, 1);
28000
- pruneDisabledSkillsForPlugin(config, removedEntry);
28001
- pruneEnabledSkillsForPlugin(config, removedEntry);
28002
- const newContent = dump(config, { lineWidth: -1 });
28003
- await writeFile4(configPath, newContent, "utf-8");
28004
- return { success: true };
28005
- } catch (error) {
28006
- return {
28007
- success: false,
28008
- error: error instanceof Error ? error.message : String(error)
28009
- };
28010
28135
  }
28136
+ return [];
28011
28137
  }
28012
- function pruneDisabledSkillsForPlugin(config, pluginEntry) {
28013
- if (!config.disabledSkills?.length)
28014
- return;
28015
- const names = extractPluginNames(pluginEntry);
28016
- if (names.length === 0)
28138
+ var init_skills = __esm(() => {
28139
+ init_js_yaml();
28140
+ init_constants();
28141
+ init_workspace_config();
28142
+ init_plugin();
28143
+ init_plugin_path();
28144
+ init_marketplace();
28145
+ init_skill();
28146
+ });
28147
+
28148
+ // src/core/transform.ts
28149
+ import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir6, cp, readdir as readdir4 } from "node:fs/promises";
28150
+ import { existsSync as existsSync9 } from "node:fs";
28151
+ import { join as join12, basename as basename5, dirname as dirname5, relative as relative2 } from "node:path";
28152
+ async function ensureWorkspaceRules(filePath, repositories, skillsIndexRefs = []) {
28153
+ const rulesContent = generateWorkspaceRules(repositories, skillsIndexRefs);
28154
+ const startMarker = "<!-- WORKSPACE-RULES:START -->";
28155
+ const endMarker = "<!-- WORKSPACE-RULES:END -->";
28156
+ if (!existsSync9(filePath)) {
28157
+ await writeFile4(filePath, `${rulesContent.trim()}
28158
+ `, "utf-8");
28017
28159
  return;
28018
- const prefixes = names.map((n) => `${n}:`);
28019
- config.disabledSkills = config.disabledSkills.filter((s) => !prefixes.some((p) => s.startsWith(p)));
28020
- if (config.disabledSkills.length === 0) {
28021
- config.disabledSkills = undefined;
28160
+ }
28161
+ const content = await readFile8(filePath, "utf-8");
28162
+ const startIndex = content.indexOf(startMarker);
28163
+ const endIndex = content.indexOf(endMarker);
28164
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
28165
+ const before = content.substring(0, startIndex);
28166
+ const after = content.substring(endIndex + endMarker.length);
28167
+ await writeFile4(filePath, before + rulesContent.trim() + after, "utf-8");
28168
+ } else {
28169
+ await writeFile4(filePath, content + rulesContent, "utf-8");
28022
28170
  }
28023
28171
  }
28024
- function extractPluginNames(pluginSource) {
28025
- if (isPluginSpec(pluginSource)) {
28026
- const parsed = parsePluginSpec(pluginSource);
28027
- if (!parsed)
28028
- return [];
28029
- const names = [parsed.plugin];
28030
- if (parsed.marketplaceName && parsed.marketplaceName !== parsed.plugin) {
28031
- names.push(parsed.marketplaceName);
28172
+ function isExcluded(pluginPath, filePath, exclude) {
28173
+ if (!exclude || exclude.length === 0)
28174
+ return false;
28175
+ const relativePath = relative2(pluginPath, filePath).replaceAll("\\", "/");
28176
+ return import_micromatch.default.isMatch(relativePath, exclude);
28177
+ }
28178
+ async function copyDirectoryWithExclusions(sourceDir, destDir, pluginPath, exclude) {
28179
+ await mkdir6(destDir, { recursive: true });
28180
+ const entries = await readdir4(sourceDir, { withFileTypes: true });
28181
+ for (const entry of entries) {
28182
+ const sourcePath = join12(sourceDir, entry.name);
28183
+ const destPath = join12(destDir, entry.name);
28184
+ if (isExcluded(pluginPath, sourcePath, exclude)) {
28185
+ continue;
28032
28186
  }
28033
- return names;
28034
- }
28035
- if (isGitHubUrl(pluginSource)) {
28036
- const parsed = parseGitHubUrl(pluginSource);
28037
- if (parsed) {
28038
- const names = [parsed.repo];
28039
- const ownerRepo = `${parsed.owner}-${parsed.repo}`;
28040
- if (ownerRepo !== parsed.repo)
28041
- names.push(ownerRepo);
28042
- return names;
28187
+ if (entry.isDirectory()) {
28188
+ await copyDirectoryWithExclusions(sourcePath, destPath, pluginPath, exclude);
28189
+ } else {
28190
+ await cp(sourcePath, destPath);
28043
28191
  }
28044
28192
  }
28045
- const parts = pluginSource.split(/[/\\]/).filter(Boolean);
28046
- const last2 = parts[parts.length - 1];
28047
- if (!last2)
28048
- return [];
28049
- return [last2.replace(/\.git$/, "")];
28050
28193
  }
28051
- function findPluginEntryByName(config, pluginName) {
28052
- return config.plugins.findIndex((entry) => extractPluginNames(getPluginSource(entry)).includes(pluginName));
28194
+ function getMapping(client, options2) {
28195
+ return options2?.clientMappings?.[client] ?? CLIENT_MAPPINGS[client];
28053
28196
  }
28054
- function ensureObjectPluginEntry(config, index) {
28055
- const entry = config.plugins[index];
28056
- if (entry === undefined)
28057
- throw new Error(`Plugin entry at index ${index} not found`);
28058
- if (typeof entry === "string") {
28059
- const objectEntry = { source: entry };
28060
- config.plugins[index] = objectEntry;
28061
- return objectEntry;
28197
+ async function copyCommands(pluginPath, workspacePath, client, options2 = {}) {
28198
+ const { dryRun = false } = options2;
28199
+ const mapping = getMapping(client, options2);
28200
+ const results = [];
28201
+ if (!mapping.commandsPath) {
28202
+ return results;
28062
28203
  }
28063
- return entry;
28064
- }
28065
- function parseSkillKey2(skillKey) {
28066
- const colonIdx = skillKey.indexOf(":");
28067
- if (colonIdx === -1)
28068
- return null;
28069
- return {
28070
- pluginName: skillKey.slice(0, colonIdx),
28071
- skillName: skillKey.slice(colonIdx + 1)
28072
- };
28073
- }
28074
- async function addDisabledSkill(skillKey, workspacePath = process.cwd()) {
28075
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28076
- if (!existsSync8(configPath)) {
28077
- return {
28078
- success: false,
28079
- error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
28080
- };
28204
+ const sourceDir = join12(pluginPath, "commands");
28205
+ if (!existsSync9(sourceDir)) {
28206
+ return results;
28081
28207
  }
28082
- const parsed = parseSkillKey2(skillKey);
28083
- if (!parsed) {
28084
- return {
28085
- success: false,
28086
- error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
28087
- };
28208
+ const destDir = join12(workspacePath, mapping.commandsPath);
28209
+ if (!dryRun) {
28210
+ await mkdir6(destDir, { recursive: true });
28088
28211
  }
28089
- const { pluginName, skillName } = parsed;
28090
- try {
28091
- const content = await readFile7(configPath, "utf-8");
28092
- const config = load(content);
28093
- const index = findPluginEntryByName(config, pluginName);
28094
- if (index === -1) {
28095
- return {
28096
- success: false,
28097
- error: `Plugin '${pluginName}' not found in workspace config`
28098
- };
28212
+ const files = await readdir4(sourceDir);
28213
+ const mdFiles = files.filter((f) => f.endsWith(".md"));
28214
+ const copyPromises = mdFiles.filter((file) => !isExcluded(pluginPath, join12(sourceDir, file), options2.exclude)).map(async (file) => {
28215
+ const sourcePath = join12(sourceDir, file);
28216
+ const destPath = join12(destDir, file);
28217
+ if (dryRun) {
28218
+ return { source: sourcePath, destination: destPath, action: "copied" };
28099
28219
  }
28100
- const entry = ensureObjectPluginEntry(config, index);
28101
- if (Array.isArray(entry.skills)) {
28220
+ try {
28221
+ const content = await readFile8(sourcePath, "utf-8");
28222
+ await writeFile4(destPath, content, "utf-8");
28223
+ return { source: sourcePath, destination: destPath, action: "copied" };
28224
+ } catch (error) {
28102
28225
  return {
28103
- success: false,
28104
- error: `Plugin '${pluginName}' is in allowlist mode; use removeEnabledSkill to disable a skill`
28226
+ source: sourcePath,
28227
+ destination: destPath,
28228
+ action: "failed",
28229
+ error: error instanceof Error ? error.message : "Unknown error"
28105
28230
  };
28106
28231
  }
28107
- const existing = entry.skills?.exclude ?? [];
28108
- if (existing.includes(skillName)) {
28109
- return {
28110
- success: false,
28111
- error: `Skill '${skillKey}' is already disabled`
28112
- };
28232
+ });
28233
+ return Promise.all(copyPromises);
28234
+ }
28235
+ async function copySkills(pluginPath, workspacePath, client, options2 = {}) {
28236
+ const { dryRun = false, skillNameMap, syncMode = "copy", canonicalSkillsPath } = options2;
28237
+ const mapping = getMapping(client, options2);
28238
+ const results = [];
28239
+ if (!mapping.skillsPath) {
28240
+ return results;
28241
+ }
28242
+ const skillsDir = join12(pluginPath, "skills");
28243
+ let skillSources;
28244
+ if (existsSync9(skillsDir)) {
28245
+ const entries = await readdir4(skillsDir, { withFileTypes: true });
28246
+ skillSources = entries.filter((e) => e.isDirectory()).filter((e) => !isExcluded(pluginPath, join12(skillsDir, e.name), options2.exclude)).map((e) => ({ name: e.name, sourcePath: join12(skillsDir, e.name), isRootLevel: false }));
28247
+ } else {
28248
+ const nestedSkills = (await discoverNestedSkillEntries(pluginPath)).filter((entry) => !isExcluded(pluginPath, entry.skillPath, options2.exclude)).map((entry) => ({ name: entry.name, sourcePath: entry.skillPath, isRootLevel: false }));
28249
+ if (nestedSkills.length > 0) {
28250
+ skillSources = nestedSkills;
28251
+ } else {
28252
+ const rootSkillMd = join12(pluginPath, "SKILL.md");
28253
+ if (existsSync9(rootSkillMd)) {
28254
+ const content = await readFile8(rootSkillMd, "utf-8");
28255
+ const metadata = parseSkillMetadata(content);
28256
+ const skillName = metadata?.name ?? basename5(pluginPath);
28257
+ skillSources = [{ name: skillName, sourcePath: pluginPath, isRootLevel: true }];
28258
+ } else {
28259
+ return results;
28260
+ }
28113
28261
  }
28114
- entry.skills = { exclude: [...existing, skillName] };
28115
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
28116
- return { success: true };
28117
- } catch (error) {
28118
- return {
28119
- success: false,
28120
- error: error instanceof Error ? error.message : String(error)
28121
- };
28122
28262
  }
28123
- }
28124
- async function removeDisabledSkill(skillKey, workspacePath = process.cwd()) {
28125
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28126
- if (!existsSync8(configPath)) {
28127
- return {
28128
- success: false,
28129
- error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
28130
- };
28263
+ if (skillNameMap) {
28264
+ skillSources = skillSources.filter((s) => skillNameMap.has(s.name));
28131
28265
  }
28132
- const parsed = parseSkillKey2(skillKey);
28133
- if (!parsed) {
28134
- return {
28135
- success: false,
28136
- error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
28137
- };
28266
+ if (skillSources.length === 0) {
28267
+ return results;
28138
28268
  }
28139
- const { pluginName, skillName } = parsed;
28140
- try {
28141
- const content = await readFile7(configPath, "utf-8");
28142
- const config = load(content);
28143
- const index = findPluginEntryByName(config, pluginName);
28144
- if (index === -1) {
28269
+ const destDir = join12(workspacePath, mapping.skillsPath);
28270
+ if (!dryRun) {
28271
+ await mkdir6(destDir, { recursive: true });
28272
+ }
28273
+ const useSymlinks = syncMode === "symlink" && !isUniversalClient(client) && canonicalSkillsPath;
28274
+ const copyPromises = skillSources.map(async (skill) => {
28275
+ const resolvedName = skillNameMap?.get(skill.name) ?? skill.name;
28276
+ const skillDestPath = join12(destDir, resolvedName);
28277
+ if (dryRun) {
28145
28278
  return {
28146
- success: false,
28147
- error: `Plugin '${pluginName}' not found in workspace config`
28279
+ source: skill.sourcePath,
28280
+ destination: skillDestPath,
28281
+ action: "copied"
28148
28282
  };
28149
28283
  }
28150
- const entry = config.plugins[index];
28151
- if (!entry) {
28152
- return { success: false, error: `Plugin '${pluginName}' not found in workspace config` };
28284
+ if (useSymlinks) {
28285
+ const canonicalSkillPath = join12(workspacePath, canonicalSkillsPath, resolvedName);
28286
+ const symlinkCreated = await createSymlink(canonicalSkillPath, skillDestPath);
28287
+ if (symlinkCreated) {
28288
+ return {
28289
+ source: canonicalSkillPath,
28290
+ destination: skillDestPath,
28291
+ action: "copied"
28292
+ };
28293
+ }
28153
28294
  }
28154
- if (typeof entry === "string" || !entry.skills || Array.isArray(entry.skills)) {
28295
+ try {
28296
+ if (skill.isRootLevel) {
28297
+ await mkdir6(skillDestPath, { recursive: true });
28298
+ await cp(join12(skill.sourcePath, "SKILL.md"), join12(skillDestPath, "SKILL.md"));
28299
+ } else if (options2.exclude && options2.exclude.length > 0) {
28300
+ await copyDirectoryWithExclusions(skill.sourcePath, skillDestPath, pluginPath, options2.exclude);
28301
+ } else {
28302
+ await cp(skill.sourcePath, skillDestPath, { recursive: true });
28303
+ }
28155
28304
  return {
28156
- success: false,
28157
- error: `Skill '${skillKey}' is already enabled`
28305
+ source: skill.sourcePath,
28306
+ destination: skillDestPath,
28307
+ action: "copied"
28158
28308
  };
28159
- }
28160
- if (!entry.skills.exclude.includes(skillName)) {
28309
+ } catch (error) {
28161
28310
  return {
28162
- success: false,
28163
- error: `Skill '${skillKey}' is already enabled`
28311
+ source: skill.sourcePath,
28312
+ destination: skillDestPath,
28313
+ action: "failed",
28314
+ error: error instanceof Error ? error.message : "Unknown error"
28164
28315
  };
28165
28316
  }
28166
- const newExclude = entry.skills.exclude.filter((s) => s !== skillName);
28167
- entry.skills = newExclude.length > 0 ? { exclude: newExclude } : undefined;
28168
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
28169
- return { success: true };
28170
- } catch (error) {
28171
- return {
28172
- success: false,
28173
- error: error instanceof Error ? error.message : String(error)
28174
- };
28317
+ });
28318
+ return Promise.all(copyPromises);
28319
+ }
28320
+ async function collectPluginSkills(pluginPath, pluginSource, disabledSkills, pluginName, enabledSkills, pluginSkillsConfig) {
28321
+ const skillsDir = join12(pluginPath, "skills");
28322
+ const hasEnabledEntries = !pluginSkillsConfig && enabledSkills && pluginName && [...enabledSkills].some((s) => s.startsWith(`${pluginName}:`));
28323
+ let candidateDirs;
28324
+ if (existsSync9(skillsDir)) {
28325
+ const entries = await readdir4(skillsDir, { withFileTypes: true });
28326
+ candidateDirs = entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name, path: join12(skillsDir, e.name) }));
28327
+ } else {
28328
+ const nestedDirs = await discoverNestedSkillEntries(pluginPath).then((entries) => entries.map((entry) => ({ name: entry.name, path: entry.skillPath })));
28329
+ if (nestedDirs.length > 0) {
28330
+ candidateDirs = nestedDirs;
28331
+ } else {
28332
+ const rootSkillMd = join12(pluginPath, "SKILL.md");
28333
+ if (existsSync9(rootSkillMd)) {
28334
+ const content = await readFile8(rootSkillMd, "utf-8");
28335
+ const metadata = parseSkillMetadata(content);
28336
+ const skillName = metadata?.name ?? basename5(pluginPath);
28337
+ candidateDirs = [{ name: skillName, path: pluginPath }];
28338
+ } else {
28339
+ candidateDirs = [];
28340
+ }
28341
+ }
28342
+ }
28343
+ let filteredDirs;
28344
+ if (pluginSkillsConfig !== undefined) {
28345
+ if (Array.isArray(pluginSkillsConfig)) {
28346
+ filteredDirs = candidateDirs.filter((e) => pluginSkillsConfig.includes(e.name));
28347
+ } else {
28348
+ filteredDirs = candidateDirs.filter((e) => !pluginSkillsConfig.exclude.includes(e.name));
28349
+ }
28350
+ } else if (pluginName) {
28351
+ if (hasEnabledEntries) {
28352
+ filteredDirs = candidateDirs.filter((e) => enabledSkills?.has(`${pluginName}:${e.name}`));
28353
+ } else if (disabledSkills) {
28354
+ filteredDirs = candidateDirs.filter((e) => !disabledSkills.has(`${pluginName}:${e.name}`));
28355
+ } else {
28356
+ filteredDirs = candidateDirs;
28357
+ }
28358
+ } else {
28359
+ filteredDirs = candidateDirs;
28175
28360
  }
28361
+ return filteredDirs.map((entry) => ({
28362
+ folderName: entry.name,
28363
+ skillPath: entry.path,
28364
+ pluginPath,
28365
+ pluginSource
28366
+ }));
28176
28367
  }
28177
- async function addEnabledSkill(skillKey, workspacePath = process.cwd()) {
28178
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28179
- if (!existsSync8(configPath)) {
28180
- return {
28181
- success: false,
28182
- error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
28183
- };
28368
+ async function copyHooks(pluginPath, workspacePath, client, options2 = {}) {
28369
+ const { dryRun = false } = options2;
28370
+ const mapping = getMapping(client, options2);
28371
+ const results = [];
28372
+ if (!mapping.hooksPath) {
28373
+ return results;
28184
28374
  }
28185
- const parsed = parseSkillKey2(skillKey);
28186
- if (!parsed) {
28187
- return {
28188
- success: false,
28189
- error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
28190
- };
28375
+ const sourceDir = join12(pluginPath, "hooks");
28376
+ if (!existsSync9(sourceDir)) {
28377
+ return results;
28191
28378
  }
28192
- const { pluginName, skillName } = parsed;
28379
+ const destDir = join12(workspacePath, mapping.hooksPath);
28380
+ if (dryRun) {
28381
+ results.push({ source: sourceDir, destination: destDir, action: "copied" });
28382
+ return results;
28383
+ }
28384
+ await mkdir6(destDir, { recursive: true });
28193
28385
  try {
28194
- const content = await readFile7(configPath, "utf-8");
28195
- const config = load(content);
28196
- const index = findPluginEntryByName(config, pluginName);
28197
- if (index === -1) {
28198
- return {
28199
- success: false,
28200
- error: `Plugin '${pluginName}' not found in workspace config`
28201
- };
28202
- }
28203
- const entry = ensureObjectPluginEntry(config, index);
28204
- if (entry.skills && !Array.isArray(entry.skills)) {
28205
- return {
28206
- success: false,
28207
- error: `Plugin '${pluginName}' is in blocklist mode; use removeDisabledSkill to enable a skill`
28208
- };
28209
- }
28210
- const existing = entry.skills ?? [];
28211
- if (existing.includes(skillName)) {
28212
- return {
28213
- success: false,
28214
- error: `Skill '${skillKey}' is already enabled`
28215
- };
28386
+ if (options2.exclude && options2.exclude.length > 0) {
28387
+ await copyDirectoryWithExclusions(sourceDir, destDir, pluginPath, options2.exclude);
28388
+ } else {
28389
+ await cp(sourceDir, destDir, { recursive: true });
28216
28390
  }
28217
- entry.skills = [...existing, skillName];
28218
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
28219
- return { success: true };
28391
+ results.push({ source: sourceDir, destination: destDir, action: "copied" });
28220
28392
  } catch (error) {
28221
- return {
28222
- success: false,
28223
- error: error instanceof Error ? error.message : String(error)
28224
- };
28393
+ results.push({
28394
+ source: sourceDir,
28395
+ destination: destDir,
28396
+ action: "failed",
28397
+ error: error instanceof Error ? error.message : "Unknown error"
28398
+ });
28225
28399
  }
28400
+ return results;
28226
28401
  }
28227
- async function removeEnabledSkill(skillKey, workspacePath = process.cwd()) {
28228
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28229
- if (!existsSync8(configPath)) {
28230
- return {
28231
- success: false,
28232
- error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
28233
- };
28402
+ async function copyAgents(pluginPath, workspacePath, client, options2 = {}) {
28403
+ const { dryRun = false } = options2;
28404
+ const mapping = getMapping(client, options2);
28405
+ const results = [];
28406
+ if (!mapping.agentsPath) {
28407
+ return results;
28234
28408
  }
28235
- const parsed = parseSkillKey2(skillKey);
28236
- if (!parsed) {
28237
- return {
28238
- success: false,
28239
- error: `Invalid skill key format: '${skillKey}' (expected pluginName:skillName)`
28240
- };
28409
+ const sourceDir = join12(pluginPath, "agents");
28410
+ if (!existsSync9(sourceDir)) {
28411
+ return results;
28241
28412
  }
28242
- const { pluginName, skillName } = parsed;
28243
- try {
28244
- const content = await readFile7(configPath, "utf-8");
28245
- const config = load(content);
28246
- const index = findPluginEntryByName(config, pluginName);
28247
- if (index === -1) {
28413
+ const destDir = join12(workspacePath, mapping.agentsPath);
28414
+ if (!dryRun) {
28415
+ await mkdir6(destDir, { recursive: true });
28416
+ }
28417
+ const files = await readdir4(sourceDir);
28418
+ const mdFiles = files.filter((f) => f.endsWith(".md"));
28419
+ const copyPromises = mdFiles.filter((file) => !isExcluded(pluginPath, join12(sourceDir, file), options2.exclude)).map(async (file) => {
28420
+ const sourcePath = join12(sourceDir, file);
28421
+ const destPath = join12(destDir, file);
28422
+ if (dryRun) {
28423
+ return { source: sourcePath, destination: destPath, action: "copied" };
28424
+ }
28425
+ try {
28426
+ const content = await readFile8(sourcePath, "utf-8");
28427
+ await writeFile4(destPath, content, "utf-8");
28428
+ return { source: sourcePath, destination: destPath, action: "copied" };
28429
+ } catch (error) {
28248
28430
  return {
28249
- success: false,
28250
- error: `Plugin '${pluginName}' not found in workspace config`
28431
+ source: sourcePath,
28432
+ destination: destPath,
28433
+ action: "failed",
28434
+ error: error instanceof Error ? error.message : "Unknown error"
28251
28435
  };
28252
28436
  }
28253
- const entry = config.plugins[index];
28254
- if (!entry) {
28255
- return { success: false, error: `Plugin '${pluginName}' not found in workspace config` };
28437
+ });
28438
+ return Promise.all(copyPromises);
28439
+ }
28440
+ async function copyAndAdjustDirectory(sourceDir, destDir, sourceBase, pluginPath, skillsPath, skillNameMap, exclude) {
28441
+ await mkdir6(destDir, { recursive: true });
28442
+ const entries = await readdir4(sourceDir, { withFileTypes: true });
28443
+ for (const entry of entries) {
28444
+ const sourcePath = join12(sourceDir, entry.name);
28445
+ const destPath = join12(destDir, entry.name);
28446
+ if (isExcluded(pluginPath, sourcePath, exclude)) {
28447
+ continue;
28256
28448
  }
28257
- if (typeof entry === "string" || !entry.skills || !Array.isArray(entry.skills)) {
28258
- return {
28259
- success: false,
28260
- error: `Skill '${skillKey}' is already disabled`
28261
- };
28449
+ if (entry.isDirectory()) {
28450
+ await copyAndAdjustDirectory(sourcePath, destPath, sourceBase, pluginPath, skillsPath, skillNameMap, exclude);
28451
+ } else {
28452
+ const relativePath = relative2(sourceBase, sourcePath).replaceAll("\\", "/");
28453
+ const isMarkdown = entry.name.endsWith(".md") || entry.name.endsWith(".markdown");
28454
+ if (isMarkdown) {
28455
+ let content = await readFile8(sourcePath, "utf-8");
28456
+ content = adjustLinksInContent(content, relativePath, {
28457
+ ...skillNameMap && { skillNameMap },
28458
+ workspaceSkillsPath: skillsPath
28459
+ });
28460
+ await writeFile4(destPath, content, "utf-8");
28461
+ } else {
28462
+ await cp(sourcePath, destPath);
28463
+ }
28262
28464
  }
28263
- if (!entry.skills.includes(skillName)) {
28264
- return {
28265
- success: false,
28266
- error: `Skill '${skillKey}' is already disabled`
28267
- };
28465
+ }
28466
+ }
28467
+ async function copyGitHubContent(pluginPath, workspacePath, client, options2 = {}) {
28468
+ const { dryRun = false, skillNameMap } = options2;
28469
+ const mapping = getMapping(client, options2);
28470
+ const results = [];
28471
+ if (!mapping.githubPath) {
28472
+ return results;
28473
+ }
28474
+ const sourceDir = join12(pluginPath, ".github");
28475
+ if (!existsSync9(sourceDir)) {
28476
+ return results;
28477
+ }
28478
+ const destDir = join12(workspacePath, mapping.githubPath);
28479
+ if (dryRun) {
28480
+ results.push({ source: sourceDir, destination: destDir, action: "copied" });
28481
+ return results;
28482
+ }
28483
+ try {
28484
+ if (mapping.skillsPath || options2.exclude && options2.exclude.length > 0) {
28485
+ await copyAndAdjustDirectory(sourceDir, destDir, sourceDir, pluginPath, mapping.skillsPath ?? "", skillNameMap, options2.exclude);
28486
+ } else {
28487
+ await cp(sourceDir, destDir, { recursive: true });
28268
28488
  }
28269
- const newSkills = entry.skills.filter((s) => s !== skillName);
28270
- entry.skills = newSkills;
28271
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
28272
- return { success: true };
28489
+ results.push({ source: sourceDir, destination: destDir, action: "copied" });
28273
28490
  } catch (error) {
28274
- return {
28275
- success: false,
28276
- error: error instanceof Error ? error.message : String(error)
28277
- };
28491
+ results.push({
28492
+ source: sourceDir,
28493
+ destination: destDir,
28494
+ action: "failed",
28495
+ error: error instanceof Error ? error.message : "Unknown error"
28496
+ });
28278
28497
  }
28498
+ return results;
28279
28499
  }
28280
- async function setPluginSkillsMode(pluginName, mode, skillNames, workspacePath = process.cwd()) {
28281
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28282
- if (!existsSync8(configPath)) {
28283
- return {
28284
- success: false,
28285
- error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
28286
- };
28500
+ async function copyPluginToWorkspace(pluginPath, workspacePath, client, options2 = {}) {
28501
+ const { skillNameMap, syncMode, canonicalSkillsPath, ...baseOptions } = options2;
28502
+ const [commandResults, skillResults, hookResults, agentResults] = await Promise.all([
28503
+ copyCommands(pluginPath, workspacePath, client, baseOptions),
28504
+ copySkills(pluginPath, workspacePath, client, {
28505
+ ...baseOptions,
28506
+ ...skillNameMap && { skillNameMap },
28507
+ ...syncMode && { syncMode },
28508
+ ...canonicalSkillsPath && { canonicalSkillsPath }
28509
+ }),
28510
+ copyHooks(pluginPath, workspacePath, client, baseOptions),
28511
+ copyAgents(pluginPath, workspacePath, client, baseOptions)
28512
+ ]);
28513
+ const githubResults = await copyGitHubContent(pluginPath, workspacePath, client, {
28514
+ ...baseOptions,
28515
+ ...skillNameMap && { skillNameMap }
28516
+ });
28517
+ return [...commandResults, ...skillResults, ...hookResults, ...agentResults, ...githubResults];
28518
+ }
28519
+ function isExplicitGitHubSource(source) {
28520
+ if (source.startsWith("https://github.com/") || source.startsWith("http://github.com/") || source.startsWith("github.com/") || source.startsWith("gh:")) {
28521
+ return true;
28522
+ }
28523
+ if (!source.startsWith(".") && !source.startsWith("/") && source.includes("/")) {
28524
+ const parts = source.split("/");
28525
+ if (parts.length >= 3) {
28526
+ const validOwnerRepo = /^[a-zA-Z0-9_.-]+$/;
28527
+ if (parts[0] && parts[1] && validOwnerRepo.test(parts[0]) && validOwnerRepo.test(parts[1])) {
28528
+ return true;
28529
+ }
28530
+ }
28531
+ }
28532
+ return false;
28533
+ }
28534
+ function resolveFileSourcePath(source, defaultSourcePath, githubCache) {
28535
+ if (!isExplicitGitHubSource(source)) {
28536
+ if (source.startsWith("/")) {
28537
+ return { path: source };
28538
+ }
28539
+ if (source.startsWith("../")) {
28540
+ return { path: join12(process.cwd(), source) };
28541
+ }
28542
+ if (defaultSourcePath) {
28543
+ return { path: join12(defaultSourcePath, source) };
28544
+ }
28545
+ return { path: join12(process.cwd(), source) };
28287
28546
  }
28288
- try {
28289
- const content = await readFile7(configPath, "utf-8");
28290
- const config = load(content);
28291
- const index = findPluginEntryByName(config, pluginName);
28292
- if (index === -1) {
28547
+ const parsed = parseFileSource(source);
28548
+ if (parsed.type === "github" && parsed.owner && parsed.repo && parsed.filePath) {
28549
+ const cacheKey = `${parsed.owner}/${parsed.repo}`;
28550
+ const cachePath = githubCache?.get(cacheKey);
28551
+ if (!cachePath) {
28293
28552
  return {
28294
- success: false,
28295
- error: `Plugin '${pluginName}' not found in workspace config`
28553
+ path: "",
28554
+ error: `GitHub cache not found for ${cacheKey}. Ensure the repo is fetched.`
28296
28555
  };
28297
28556
  }
28298
- const entry = ensureObjectPluginEntry(config, index);
28299
- if (mode === "allowlist") {
28300
- entry.skills = [...skillNames];
28301
- } else {
28302
- entry.skills = skillNames.length > 0 ? { exclude: [...skillNames] } : undefined;
28303
- }
28304
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
28305
- return { success: true };
28306
- } catch (error) {
28557
+ return { path: join12(cachePath, parsed.filePath) };
28558
+ }
28559
+ if (parsed.type === "github") {
28307
28560
  return {
28308
- success: false,
28309
- error: error instanceof Error ? error.message : String(error)
28561
+ path: "",
28562
+ error: `Invalid GitHub file source: ${source}. Must include path to file (e.g., owner/repo/path/to/file.md)`
28310
28563
  };
28311
28564
  }
28312
- }
28313
- function pruneEnabledSkillsForPlugin(config, pluginEntry) {
28314
- if (!config.enabledSkills?.length)
28315
- return;
28316
- const names = extractPluginNames(pluginEntry);
28317
- if (names.length === 0)
28318
- return;
28319
- const prefixes = names.map((n) => `${n}:`);
28320
- config.enabledSkills = config.enabledSkills.filter((s) => !prefixes.some((p) => s.startsWith(p)));
28321
- if (config.enabledSkills.length === 0) {
28322
- config.enabledSkills = undefined;
28323
- }
28324
- }
28325
- async function resolveGitHubIdentity(pluginSource) {
28326
- if (isGitHubUrl(pluginSource)) {
28327
- const parsed = parseGitHubUrl(pluginSource);
28328
- return parsed ? `${parsed.owner}/${parsed.repo}`.toLowerCase() : null;
28329
- }
28330
- if (isPluginSpec(pluginSource)) {
28331
- const parsed = parsePluginSpec(pluginSource);
28332
- if (!parsed)
28333
- return null;
28334
- const marketplace = await getMarketplace(parsed.marketplaceName);
28335
- if (!marketplace)
28336
- return null;
28337
- const manifestResult = await parseMarketplaceManifest(marketplace.path);
28338
- if (!manifestResult.success)
28339
- return null;
28340
- const entry = manifestResult.data.plugins.find((p) => p.name === parsed.plugin);
28341
- if (!entry || typeof entry.source === "string")
28342
- return null;
28343
- const parsedUrl = parseGitHubUrl(entry.source.url);
28344
- return parsedUrl ? `${parsedUrl.owner}/${parsedUrl.repo}`.toLowerCase() : null;
28345
- }
28346
28565
  return null;
28347
28566
  }
28348
- async function migrateWorkspaceSkillsV1toV2(workspacePath) {
28349
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28350
- if (!existsSync8(configPath))
28351
- return;
28352
- let config;
28353
- try {
28354
- const content = await readFile7(configPath, "utf-8");
28355
- config = load(content);
28356
- } catch {
28357
- return;
28358
- }
28359
- if (!config || config.version !== undefined && config.version >= 2)
28360
- return;
28361
- const enabledSkills = config.enabledSkills ?? [];
28362
- const disabledSkills = config.disabledSkills ?? [];
28363
- const enabledByPlugin = new Map;
28364
- for (const skillKey of enabledSkills) {
28365
- const parsed = parseSkillKey2(skillKey);
28366
- if (!parsed)
28367
- continue;
28368
- const list = enabledByPlugin.get(parsed.pluginName) ?? [];
28369
- list.push(parsed.skillName);
28370
- enabledByPlugin.set(parsed.pluginName, list);
28567
+ async function copyWorkspaceFiles(sourcePath, workspacePath, files, options2 = {}) {
28568
+ const { dryRun = false, githubCache, repositories = [], skillsIndexRefs = [] } = options2;
28569
+ const results = [];
28570
+ const stringPatterns = [];
28571
+ const objectEntries = [];
28572
+ const copiedAgentFiles = [];
28573
+ for (const file of files) {
28574
+ if (typeof file === "string") {
28575
+ stringPatterns.push(file);
28576
+ } else {
28577
+ let dest = file.dest;
28578
+ if (!dest && file.source) {
28579
+ const parts = file.source.split("/");
28580
+ dest = parts[parts.length - 1] || file.source;
28581
+ }
28582
+ if (!dest) {
28583
+ results.push({
28584
+ source: "unknown",
28585
+ destination: join12(workspacePath, "unknown"),
28586
+ action: "failed",
28587
+ error: "File entry must have at least source or dest specified"
28588
+ });
28589
+ continue;
28590
+ }
28591
+ objectEntries.push(file.source ? { source: file.source, dest } : { dest });
28592
+ }
28371
28593
  }
28372
- const disabledByPlugin = new Map;
28373
- for (const skillKey of disabledSkills) {
28374
- const parsed = parseSkillKey2(skillKey);
28375
- if (!parsed)
28376
- continue;
28377
- const list = disabledByPlugin.get(parsed.pluginName) ?? [];
28378
- list.push(parsed.skillName);
28379
- disabledByPlugin.set(parsed.pluginName, list);
28594
+ if (stringPatterns.length > 0) {
28595
+ if (!sourcePath) {
28596
+ for (const pattern of stringPatterns) {
28597
+ if (!isGlobPattern(pattern) && !pattern.startsWith("!")) {
28598
+ results.push({
28599
+ source: pattern,
28600
+ destination: join12(workspacePath, pattern),
28601
+ action: "failed",
28602
+ error: `Cannot resolve file '${pattern}' - no workspace.source configured and no explicit source provided`
28603
+ });
28604
+ }
28605
+ }
28606
+ } else {
28607
+ const resolvedFiles = await resolveGlobPatterns(sourcePath, stringPatterns);
28608
+ for (const resolved of resolvedFiles) {
28609
+ const destPath = join12(workspacePath, resolved.relativePath);
28610
+ if (!existsSync9(resolved.sourcePath)) {
28611
+ const wasLiteral = stringPatterns.some((p) => !isGlobPattern(p) && !p.startsWith("!") && p === resolved.relativePath);
28612
+ if (wasLiteral) {
28613
+ results.push({
28614
+ source: resolved.sourcePath,
28615
+ destination: destPath,
28616
+ action: "failed",
28617
+ error: `Source file not found: ${resolved.sourcePath}`
28618
+ });
28619
+ }
28620
+ continue;
28621
+ }
28622
+ if (dryRun) {
28623
+ results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
28624
+ if (AGENT_FILES2.includes(resolved.relativePath)) {
28625
+ copiedAgentFiles.push(resolved.relativePath);
28626
+ }
28627
+ continue;
28628
+ }
28629
+ try {
28630
+ await mkdir6(dirname5(destPath), { recursive: true });
28631
+ const content = await readFile8(resolved.sourcePath, "utf-8");
28632
+ await writeFile4(destPath, content, "utf-8");
28633
+ results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
28634
+ if (AGENT_FILES2.includes(resolved.relativePath)) {
28635
+ copiedAgentFiles.push(resolved.relativePath);
28636
+ }
28637
+ } catch (error) {
28638
+ results.push({
28639
+ source: resolved.sourcePath,
28640
+ destination: destPath,
28641
+ action: "failed",
28642
+ error: error instanceof Error ? error.message : "Unknown error"
28643
+ });
28644
+ }
28645
+ }
28646
+ }
28380
28647
  }
28381
- for (const [pluginName, skillNames] of enabledByPlugin) {
28382
- const index = findPluginEntryByName(config, pluginName);
28383
- if (index === -1) {
28384
- console.warn(`[migrate v1→v2] No plugin found for '${pluginName}', skipping`);
28648
+ for (const entry of objectEntries) {
28649
+ const destPath = join12(workspacePath, entry.dest);
28650
+ let srcPath;
28651
+ if (entry.source) {
28652
+ const resolved = resolveFileSourcePath(entry.source, sourcePath, githubCache);
28653
+ if (!resolved) {
28654
+ results.push({
28655
+ source: entry.source,
28656
+ destination: destPath,
28657
+ action: "failed",
28658
+ error: `Failed to resolve source: ${entry.source}`
28659
+ });
28660
+ continue;
28661
+ }
28662
+ if (resolved.error) {
28663
+ results.push({
28664
+ source: entry.source,
28665
+ destination: destPath,
28666
+ action: "failed",
28667
+ error: resolved.error
28668
+ });
28669
+ continue;
28670
+ }
28671
+ srcPath = resolved.path;
28672
+ } else {
28673
+ if (!sourcePath) {
28674
+ results.push({
28675
+ source: entry.dest,
28676
+ destination: destPath,
28677
+ action: "failed",
28678
+ error: `Cannot resolve file '${entry.dest}' - no workspace.source configured and no explicit source provided`
28679
+ });
28680
+ continue;
28681
+ }
28682
+ srcPath = join12(sourcePath, entry.dest);
28683
+ }
28684
+ if (!existsSync9(srcPath)) {
28685
+ results.push({
28686
+ source: srcPath,
28687
+ destination: destPath,
28688
+ action: "failed",
28689
+ error: `Source file not found: ${srcPath}`
28690
+ });
28385
28691
  continue;
28386
28692
  }
28387
- const entry = ensureObjectPluginEntry(config, index);
28388
- entry.skills = skillNames;
28389
- }
28390
- for (const [pluginName, skillNames] of disabledByPlugin) {
28391
- const index = findPluginEntryByName(config, pluginName);
28392
- if (index === -1) {
28393
- console.warn(`[migrate v1→v2] No plugin found for '${pluginName}', skipping`);
28693
+ if (dryRun) {
28694
+ results.push({ source: srcPath, destination: destPath, action: "copied" });
28695
+ if (AGENT_FILES2.includes(entry.dest)) {
28696
+ copiedAgentFiles.push(entry.dest);
28697
+ }
28394
28698
  continue;
28395
28699
  }
28396
- const entry = ensureObjectPluginEntry(config, index);
28397
- if (!Array.isArray(entry.skills)) {
28398
- entry.skills = { exclude: skillNames };
28700
+ try {
28701
+ await mkdir6(dirname5(destPath), { recursive: true });
28702
+ const content = await readFile8(srcPath, "utf-8");
28703
+ await writeFile4(destPath, content, "utf-8");
28704
+ results.push({ source: srcPath, destination: destPath, action: "copied" });
28705
+ if (AGENT_FILES2.includes(entry.dest)) {
28706
+ copiedAgentFiles.push(entry.dest);
28707
+ }
28708
+ } catch (error) {
28709
+ results.push({
28710
+ source: srcPath,
28711
+ destination: destPath,
28712
+ action: "failed",
28713
+ error: error instanceof Error ? error.message : "Unknown error"
28714
+ });
28399
28715
  }
28400
28716
  }
28401
- config.enabledSkills = undefined;
28402
- config.disabledSkills = undefined;
28403
- config.version = 2;
28404
- try {
28405
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
28406
- } catch (error) {
28407
- console.warn(`[migrate v1→v2] Failed to write migrated config: ${error instanceof Error ? error.message : String(error)}`);
28408
- }
28409
- }
28410
- async function updateRepositories(changes, workspacePath = process.cwd()) {
28411
- if (changes.remove.length === 0 && changes.add.length === 0) {
28412
- return { success: true };
28413
- }
28414
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28415
- try {
28416
- const content = await readFile7(configPath, "utf-8");
28417
- const config = load(content);
28418
- const removeSet = new Set(changes.remove);
28419
- config.repositories = config.repositories.filter((repo) => !removeSet.has(repo.path));
28420
- config.repositories.push(...changes.add);
28421
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
28422
- return { success: true };
28423
- } catch (error) {
28424
- return {
28425
- success: false,
28426
- error: error instanceof Error ? error.message : String(error)
28427
- };
28428
- }
28429
- }
28430
- async function setRepositories(repositories, workspacePath = process.cwd()) {
28431
- const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28432
- try {
28433
- const content = await readFile7(configPath, "utf-8");
28434
- const config = load(content);
28435
- config.repositories = repositories;
28436
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
28437
- return { success: true };
28438
- } catch (error) {
28439
- return {
28440
- success: false,
28441
- error: error instanceof Error ? error.message : String(error)
28442
- };
28717
+ if (!dryRun && repositories.length > 0) {
28718
+ for (const agentFile of copiedAgentFiles) {
28719
+ const targetPath = join12(workspacePath, agentFile);
28720
+ try {
28721
+ await ensureWorkspaceRules(targetPath, repositories, skillsIndexRefs);
28722
+ } catch (error) {
28723
+ results.push({
28724
+ source: "WORKSPACE-RULES",
28725
+ destination: targetPath,
28726
+ action: "failed",
28727
+ error: error instanceof Error ? error.message : "Failed to inject WORKSPACE-RULES"
28728
+ });
28729
+ }
28730
+ }
28443
28731
  }
28732
+ return results;
28444
28733
  }
28445
- var DEFAULT_PROJECT_CLIENTS;
28446
- var init_workspace_modify = __esm(() => {
28447
- init_js_yaml();
28734
+ var import_micromatch, AGENT_FILES2;
28735
+ var init_transform = __esm(() => {
28736
+ init_glob_patterns();
28737
+ init_client_mapping();
28448
28738
  init_constants();
28449
- init_workspace_config();
28450
- init_marketplace_manifest_parser();
28451
28739
  init_plugin_path();
28452
- init_marketplace();
28453
- DEFAULT_PROJECT_CLIENTS = ["universal"];
28740
+ init_symlink();
28741
+ init_link_adjuster();
28742
+ init_skill();
28743
+ init_skills();
28744
+ import_micromatch = __toESM(require_micromatch(), 1);
28745
+ AGENT_FILES2 = ["AGENTS.md", "CLAUDE.md"];
28454
28746
  });
28455
28747
 
28456
28748
  // src/core/repo-skills.ts
28457
- import { existsSync as existsSync9, lstatSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
28458
- import { readdir as readdir4, readFile as readFile8 } from "node:fs/promises";
28459
- import { basename as basename5, join as join12, relative as relative3, resolve as resolve8 } from "node:path";
28749
+ import { existsSync as existsSync10, lstatSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
28750
+ import { readdir as readdir5, readFile as readFile9 } from "node:fs/promises";
28751
+ import { basename as basename6, join as join13, relative as relative3, resolve as resolve9 } from "node:path";
28460
28752
  async function discoverRepoSkills(repoPath, options2) {
28461
28753
  if (options2.disabled)
28462
28754
  return [];
@@ -28476,19 +28768,19 @@ async function discoverRepoSkills(repoPath, options2) {
28476
28768
  const results = [];
28477
28769
  const seen = new Set;
28478
28770
  for (const skillDir of skillDirs) {
28479
- const absDir = join12(repoPath, skillDir);
28480
- if (!existsSync9(absDir))
28771
+ const absDir = join13(repoPath, skillDir);
28772
+ if (!existsSync10(absDir))
28481
28773
  continue;
28482
28774
  let entries;
28483
28775
  try {
28484
- entries = await readdir4(absDir, { withFileTypes: true });
28776
+ entries = await readdir5(absDir, { withFileTypes: true });
28485
28777
  } catch {
28486
28778
  continue;
28487
28779
  }
28488
28780
  for (const entry of entries) {
28489
28781
  if (!entry.isDirectory())
28490
28782
  continue;
28491
- const entryPath = join12(absDir, entry.name);
28783
+ const entryPath = join13(absDir, entry.name);
28492
28784
  try {
28493
28785
  const stat2 = lstatSync(entryPath);
28494
28786
  if (stat2.isSymbolicLink())
@@ -28496,14 +28788,14 @@ async function discoverRepoSkills(repoPath, options2) {
28496
28788
  } catch {
28497
28789
  continue;
28498
28790
  }
28499
- const skillMdPath = join12(entryPath, "SKILL.md");
28500
- if (!existsSync9(skillMdPath))
28791
+ const skillMdPath = join13(entryPath, "SKILL.md");
28792
+ if (!existsSync10(skillMdPath))
28501
28793
  continue;
28502
28794
  const relPath = relative3(repoPath, skillMdPath);
28503
28795
  if (seen.has(relPath))
28504
28796
  continue;
28505
28797
  try {
28506
- const content = await readFile8(skillMdPath, "utf-8");
28798
+ const content = await readFile9(skillMdPath, "utf-8");
28507
28799
  const metadata = parseSkillMetadata(content);
28508
28800
  if (!metadata)
28509
28801
  continue;
@@ -28524,7 +28816,7 @@ async function discoverWorkspaceSkills(workspacePath, repositories, clientNames)
28524
28816
  for (const repo of repositories) {
28525
28817
  if (repo.skills === false || repo.skills === undefined)
28526
28818
  continue;
28527
- const repoAbsPath = resolve8(workspacePath, repo.path);
28819
+ const repoAbsPath = resolve9(workspacePath, repo.path);
28528
28820
  const discoverOpts = Array.isArray(repo.skills) ? { skillPaths: repo.skills } : { clients: clientNames };
28529
28821
  const repoSkills = await discoverRepoSkills(repoAbsPath, discoverOpts);
28530
28822
  for (const skill of repoSkills) {
@@ -28556,7 +28848,7 @@ async function discoverWorkspaceSkills(workspacePath, repositories, clientNames)
28556
28848
  function writeSkillsIndex(workspacePath, skillsByRepo) {
28557
28849
  if (skillsByRepo.size === 0)
28558
28850
  return { writtenFiles: [], refs: [] };
28559
- const indexDir = join12(workspacePath, ".allagents", "skills-index");
28851
+ const indexDir = join13(workspacePath, ".allagents", "skills-index");
28560
28852
  mkdirSync(indexDir, { recursive: true });
28561
28853
  const writtenFiles = [];
28562
28854
  const refs = [];
@@ -28574,15 +28866,15 @@ ${skillEntries}
28574
28866
  </available_skills>
28575
28867
  `;
28576
28868
  const filePath = `skills-index/${repoName}.md`;
28577
- writeFileSync(join12(indexDir, `${repoName}.md`), content, "utf-8");
28869
+ writeFileSync(join13(indexDir, `${repoName}.md`), content, "utf-8");
28578
28870
  writtenFiles.push(filePath);
28579
28871
  refs.push({ repoName, indexPath: `.allagents/${filePath}` });
28580
28872
  }
28581
28873
  return { writtenFiles, refs };
28582
28874
  }
28583
28875
  function cleanupSkillsIndex(workspacePath, currentFiles) {
28584
- const indexDir = join12(workspacePath, ".allagents", "skills-index");
28585
- if (!existsSync9(indexDir))
28876
+ const indexDir = join13(workspacePath, ".allagents", "skills-index");
28877
+ if (!existsSync10(indexDir))
28586
28878
  return;
28587
28879
  const currentSet = new Set(currentFiles.map((f) => f.replace("skills-index/", "")));
28588
28880
  let entries;
@@ -28593,7 +28885,7 @@ function cleanupSkillsIndex(workspacePath, currentFiles) {
28593
28885
  }
28594
28886
  for (const entry of entries) {
28595
28887
  if (!currentSet.has(entry)) {
28596
- rmSync(join12(indexDir, entry), { force: true });
28888
+ rmSync(join13(indexDir, entry), { force: true });
28597
28889
  }
28598
28890
  }
28599
28891
  try {
@@ -28606,11 +28898,11 @@ function cleanupSkillsIndex(workspacePath, currentFiles) {
28606
28898
  function groupSkillsByRepo(skills, repositories) {
28607
28899
  const repoNameMap = new Map;
28608
28900
  for (const repo of repositories) {
28609
- repoNameMap.set(repo.path, repo.name ?? basename5(repo.path));
28901
+ repoNameMap.set(repo.path, repo.name ?? basename6(repo.path));
28610
28902
  }
28611
28903
  const grouped = new Map;
28612
28904
  for (const skill of skills) {
28613
- const repoName = repoNameMap.get(skill.repoPath) ?? basename5(skill.repoPath);
28905
+ const repoName = repoNameMap.get(skill.repoPath) ?? basename6(skill.repoPath);
28614
28906
  const existing = grouped.get(skill.repoPath);
28615
28907
  if (existing) {
28616
28908
  existing.skills.push(skill);
@@ -28626,9 +28918,9 @@ var init_repo_skills = __esm(() => {
28626
28918
  });
28627
28919
 
28628
28920
  // src/core/workspace-repo.ts
28629
- import { readFile as readFile9, writeFile as writeFile5 } from "node:fs/promises";
28630
- import { existsSync as existsSync10 } from "node:fs";
28631
- import { join as join13 } from "node:path";
28921
+ import { readFile as readFile10, writeFile as writeFile5 } from "node:fs/promises";
28922
+ import { existsSync as existsSync11 } from "node:fs";
28923
+ import { join as join14 } from "node:path";
28632
28924
  async function detectRemote(repoPath) {
28633
28925
  try {
28634
28926
  const env2 = { ...process.env };
@@ -28670,10 +28962,10 @@ function normalizePath(p) {
28670
28962
  }
28671
28963
  async function addRepository(path, options2 = {}, workspacePath = process.cwd()) {
28672
28964
  const normalizedPath = normalizePath(path);
28673
- const configPath = join13(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28965
+ const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28674
28966
  await ensureWorkspace(workspacePath);
28675
28967
  try {
28676
- const content = await readFile9(configPath, "utf-8");
28968
+ const content = await readFile10(configPath, "utf-8");
28677
28969
  const config = load(content);
28678
28970
  if (config.repositories.some((r) => normalizePath(r.path) === normalizedPath)) {
28679
28971
  return { success: false, error: `Repository already exists: ${normalizedPath}` };
@@ -28693,8 +28985,8 @@ async function addRepository(path, options2 = {}, workspacePath = process.cwd())
28693
28985
  }
28694
28986
  }
28695
28987
  async function removeRepository(path, workspacePath = process.cwd()) {
28696
- const configPath = join13(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28697
- if (!existsSync10(configPath)) {
28988
+ const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28989
+ if (!existsSync11(configPath)) {
28698
28990
  return {
28699
28991
  success: false,
28700
28992
  error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
@@ -28702,7 +28994,7 @@ async function removeRepository(path, workspacePath = process.cwd()) {
28702
28994
  };
28703
28995
  }
28704
28996
  try {
28705
- const content = await readFile9(configPath, "utf-8");
28997
+ const content = await readFile10(configPath, "utf-8");
28706
28998
  const config = load(content);
28707
28999
  const normalizedPath = normalizePath(path);
28708
29000
  const index = config.repositories.findIndex((r) => normalizePath(r.path) === normalizedPath);
@@ -28717,11 +29009,11 @@ async function removeRepository(path, workspacePath = process.cwd()) {
28717
29009
  }
28718
29010
  }
28719
29011
  async function listRepositories(workspacePath = process.cwd()) {
28720
- const configPath = join13(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28721
- if (!existsSync10(configPath))
29012
+ const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
29013
+ if (!existsSync11(configPath))
28722
29014
  return [];
28723
29015
  try {
28724
- const content = await readFile9(configPath, "utf-8");
29016
+ const content = await readFile10(configPath, "utf-8");
28725
29017
  const config = load(content);
28726
29018
  return config.repositories ?? [];
28727
29019
  } catch {
@@ -28732,10 +29024,10 @@ function resolveClientNames(clients) {
28732
29024
  return (clients ?? []).map((c) => typeof c === "string" ? c : c.name);
28733
29025
  }
28734
29026
  async function updateAgentFiles(workspacePath = process.cwd()) {
28735
- const configPath = join13(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
28736
- if (!existsSync10(configPath))
29027
+ const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
29028
+ if (!existsSync11(configPath))
28737
29029
  return;
28738
- const content = await readFile9(configPath, "utf-8");
29030
+ const content = await readFile10(configPath, "utf-8");
28739
29031
  const config = load(content);
28740
29032
  if (config.repositories.length === 0)
28741
29033
  return;
@@ -28753,7 +29045,7 @@ async function updateAgentFiles(workspacePath = process.cwd()) {
28753
29045
  }
28754
29046
  agentFiles.add("AGENTS.md");
28755
29047
  for (const agentFile of agentFiles) {
28756
- await ensureWorkspaceRules(join13(workspacePath, agentFile), config.repositories, skillsIndexRefs);
29048
+ await ensureWorkspaceRules(join14(workspacePath, agentFile), config.repositories, skillsIndexRefs);
28757
29049
  }
28758
29050
  }
28759
29051
  var init_workspace_repo = __esm(() => {
@@ -28937,14 +29229,14 @@ var init_sync_state = __esm(() => {
28937
29229
  });
28938
29230
 
28939
29231
  // src/core/config-gitignore.ts
28940
- import { writeFile as writeFile6, readFile as readFile10 } from "node:fs/promises";
28941
- import { existsSync as existsSync11 } from "node:fs";
28942
- import { join as join14 } from "node:path";
29232
+ import { writeFile as writeFile6, readFile as readFile11 } from "node:fs/promises";
29233
+ import { existsSync as existsSync12 } from "node:fs";
29234
+ import { join as join15 } from "node:path";
28943
29235
  async function ensureConfigGitignore(workspacePath) {
28944
- const gitignorePath = join14(workspacePath, CONFIG_DIR, ".gitignore");
29236
+ const gitignorePath = join15(workspacePath, CONFIG_DIR, ".gitignore");
28945
29237
  let existing = "";
28946
- if (existsSync11(gitignorePath)) {
28947
- existing = await readFile10(gitignorePath, "utf-8");
29238
+ if (existsSync12(gitignorePath)) {
29239
+ existing = await readFile11(gitignorePath, "utf-8");
28948
29240
  }
28949
29241
  const missing = GITIGNORE_ENTRIES.filter((entry) => !existing.split(`
28950
29242
  `).includes(entry));
@@ -28965,19 +29257,19 @@ var init_config_gitignore = __esm(() => {
28965
29257
  });
28966
29258
 
28967
29259
  // src/core/sync-state.ts
28968
- import { readFile as readFile11, writeFile as writeFile7, mkdir as mkdir7 } from "node:fs/promises";
28969
- import { existsSync as existsSync12 } from "node:fs";
28970
- import { join as join15, dirname as dirname6 } from "node:path";
29260
+ import { readFile as readFile12, writeFile as writeFile7, mkdir as mkdir7 } from "node:fs/promises";
29261
+ import { existsSync as existsSync13 } from "node:fs";
29262
+ import { join as join16, dirname as dirname6 } from "node:path";
28971
29263
  function getSyncStatePath(workspacePath) {
28972
- return join15(workspacePath, CONFIG_DIR, SYNC_STATE_FILE);
29264
+ return join16(workspacePath, CONFIG_DIR, SYNC_STATE_FILE);
28973
29265
  }
28974
29266
  async function loadSyncState(workspacePath) {
28975
29267
  const statePath = getSyncStatePath(workspacePath);
28976
- if (!existsSync12(statePath)) {
29268
+ if (!existsSync13(statePath)) {
28977
29269
  return null;
28978
29270
  }
28979
29271
  try {
28980
- const content = await readFile11(statePath, "utf-8");
29272
+ const content = await readFile12(statePath, "utf-8");
28981
29273
  const parsed = JSON.parse(content);
28982
29274
  const result = SyncStateSchema.safeParse(parsed);
28983
29275
  if (!result.success) {
@@ -29052,12 +29344,12 @@ var init_sync_state2 = __esm(() => {
29052
29344
 
29053
29345
  // src/core/vscode-workspace.ts
29054
29346
  import { createHash as createHash2 } from "node:crypto";
29055
- import { resolve as resolve9, basename as basename6, isAbsolute as isAbsolute3, relative as relative4 } from "node:path";
29347
+ import { resolve as resolve10, basename as basename7, isAbsolute as isAbsolute3, relative as relative4 } from "node:path";
29056
29348
  function normalizeWorkspacePath(path) {
29057
29349
  return path.replace(/\\/g, "/");
29058
29350
  }
29059
29351
  function resolveFolderAbsolutePath(workspacePath, folderPath) {
29060
- return (isAbsolute3(folderPath) ? folderPath : resolve9(workspacePath, folderPath)).replace(/\\/g, "/");
29352
+ return (isAbsolute3(folderPath) ? folderPath : resolve10(workspacePath, folderPath)).replace(/\\/g, "/");
29061
29353
  }
29062
29354
  function resolveFolderDisplayPath(folderPath) {
29063
29355
  return normalizeWorkspacePath(folderPath);
@@ -29065,7 +29357,7 @@ function resolveFolderDisplayPath(folderPath) {
29065
29357
  function buildPathPlaceholderMap(repositories, workspacePath) {
29066
29358
  const map2 = new Map;
29067
29359
  for (const repo of repositories) {
29068
- const absolutePath = resolve9(workspacePath, repo.path);
29360
+ const absolutePath = resolve10(workspacePath, repo.path);
29069
29361
  map2.set(repo.path, absolutePath);
29070
29362
  }
29071
29363
  return map2;
@@ -29100,7 +29392,7 @@ function generateVscodeWorkspace(input) {
29100
29392
  const folders = [];
29101
29393
  const seenPaths = new Set;
29102
29394
  folders.push({ path: "." });
29103
- seenPaths.add(resolve9(workspacePath, "."));
29395
+ seenPaths.add(resolve10(workspacePath, "."));
29104
29396
  for (const repo of repositories) {
29105
29397
  const absolutePath = resolveFolderAbsolutePath(workspacePath, repo.path);
29106
29398
  const entry = { path: resolveFolderDisplayPath(repo.path) };
@@ -29142,22 +29434,22 @@ function getWorkspaceOutputPath(workspacePath, vscodeConfig) {
29142
29434
  const name = vscodeConfig?.output;
29143
29435
  if (name) {
29144
29436
  const filename = name.endsWith(".code-workspace") ? name : `${name}.code-workspace`;
29145
- return resolve9(workspacePath, filename);
29437
+ return resolve10(workspacePath, filename);
29146
29438
  }
29147
- const dirName = basename6(resolve9(workspacePath));
29148
- return resolve9(workspacePath, `${dirName}.code-workspace`);
29439
+ const dirName = basename7(resolve10(workspacePath));
29440
+ return resolve10(workspacePath, `${dirName}.code-workspace`);
29149
29441
  }
29150
29442
  function computeWorkspaceHash(content) {
29151
29443
  return createHash2("sha256").update(content).digest("hex");
29152
29444
  }
29153
29445
  function reconcileVscodeWorkspaceFolders(workspacePath, codeWorkspaceFolders, lastSyncedRepos, currentRepos) {
29154
- const normalizedWorkspacePath = resolve9(workspacePath).replace(/\\/g, "/");
29446
+ const normalizedWorkspacePath = resolve10(workspacePath).replace(/\\/g, "/");
29155
29447
  const codeWorkspaceAbsPaths = new Set;
29156
29448
  const codeWorkspaceNames = new Map;
29157
29449
  for (const folder of codeWorkspaceFolders) {
29158
29450
  if (folder.path === ".")
29159
29451
  continue;
29160
- const absPath = (isAbsolute3(folder.path) ? folder.path : resolve9(workspacePath, folder.path)).replace(/\\/g, "/");
29452
+ const absPath = (isAbsolute3(folder.path) ? folder.path : resolve10(workspacePath, folder.path)).replace(/\\/g, "/");
29161
29453
  codeWorkspaceAbsPaths.add(absPath);
29162
29454
  if (folder.name)
29163
29455
  codeWorkspaceNames.set(absPath, folder.name);
@@ -29165,7 +29457,7 @@ function reconcileVscodeWorkspaceFolders(workspacePath, codeWorkspaceFolders, la
29165
29457
  const lastSyncedSet = new Set(lastSyncedRepos.map((p) => p.replace(/\\/g, "/")));
29166
29458
  const currentReposByAbsPath = new Map;
29167
29459
  for (const repo of currentRepos) {
29168
- const absPath = resolve9(workspacePath, repo.path).replace(/\\/g, "/");
29460
+ const absPath = resolve10(workspacePath, repo.path).replace(/\\/g, "/");
29169
29461
  currentReposByAbsPath.set(absPath, repo);
29170
29462
  }
29171
29463
  const currentAbsPaths = new Set(currentReposByAbsPath.keys());
@@ -29216,8 +29508,8 @@ var init_vscode_workspace = __esm(() => {
29216
29508
  });
29217
29509
 
29218
29510
  // src/core/vscode-mcp.ts
29219
- import { existsSync as existsSync13, readFileSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
29220
- import { join as join16, dirname as dirname7 } from "node:path";
29511
+ import { existsSync as existsSync14, readFileSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
29512
+ import { join as join17, dirname as dirname7 } from "node:path";
29221
29513
  function deepEqual(a, b) {
29222
29514
  if (a === b)
29223
29515
  return true;
@@ -29249,17 +29541,17 @@ function getVscodeMcpConfigPath() {
29249
29541
  if (!appData) {
29250
29542
  throw new Error("APPDATA environment variable not set");
29251
29543
  }
29252
- return join16(appData, "Code", "User", "mcp.json");
29544
+ return join17(appData, "Code", "User", "mcp.json");
29253
29545
  }
29254
29546
  const home = getHomeDir();
29255
29547
  if (platform2 === "darwin") {
29256
- return join16(home, "Library", "Application Support", "Code", "User", "mcp.json");
29548
+ return join17(home, "Library", "Application Support", "Code", "User", "mcp.json");
29257
29549
  }
29258
- return join16(home, ".config", "Code", "User", "mcp.json");
29550
+ return join17(home, ".config", "Code", "User", "mcp.json");
29259
29551
  }
29260
29552
  function readPluginMcpConfig(pluginPath) {
29261
- const mcpPath = join16(pluginPath, ".mcp.json");
29262
- if (!existsSync13(mcpPath)) {
29553
+ const mcpPath = join17(pluginPath, ".mcp.json");
29554
+ if (!existsSync14(mcpPath)) {
29263
29555
  return null;
29264
29556
  }
29265
29557
  try {
@@ -29325,7 +29617,7 @@ function syncVscodeMcpConfig(validatedPlugins, options2) {
29325
29617
  trackedServers: []
29326
29618
  };
29327
29619
  let existingConfig = {};
29328
- if (existsSync13(configPath)) {
29620
+ if (existsSync14(configPath)) {
29329
29621
  try {
29330
29622
  const content = readFileSync(configPath, "utf-8");
29331
29623
  existingConfig = import_json5.default.parse(content);
@@ -29376,7 +29668,7 @@ function syncVscodeMcpConfig(validatedPlugins, options2) {
29376
29668
  if (hasChanges && !dryRun) {
29377
29669
  existingConfig.servers = existingServers;
29378
29670
  const dir = dirname7(configPath);
29379
- if (!existsSync13(dir)) {
29671
+ if (!existsSync14(dir)) {
29380
29672
  mkdirSync2(dir, { recursive: true });
29381
29673
  }
29382
29674
  writeFileSync2(configPath, `${JSON.stringify(existingConfig, null, 2)}
@@ -29394,7 +29686,7 @@ function syncVscodeMcpConfig(validatedPlugins, options2) {
29394
29686
  if (result.removed > 0 && !dryRun) {
29395
29687
  existingConfig.servers = existingServers;
29396
29688
  const dir = dirname7(configPath);
29397
- if (!existsSync13(dir)) {
29689
+ if (!existsSync14(dir)) {
29398
29690
  mkdirSync2(dir, { recursive: true });
29399
29691
  }
29400
29692
  writeFileSync2(configPath, `${JSON.stringify(existingConfig, null, 2)}
@@ -29451,7 +29743,7 @@ function applyMcpProxy(servers, client, config) {
29451
29743
  // src/core/native/types.ts
29452
29744
  import { spawn as spawn2 } from "node:child_process";
29453
29745
  function executeCommand(binary2, args, options2 = {}) {
29454
- return new Promise((resolve10) => {
29746
+ return new Promise((resolve11) => {
29455
29747
  const proc = spawn2(binary2, args, {
29456
29748
  cwd: options2.cwd,
29457
29749
  stdio: ["ignore", "pipe", "pipe"],
@@ -29472,7 +29764,7 @@ function executeCommand(binary2, args, options2 = {}) {
29472
29764
  return;
29473
29765
  resolved = true;
29474
29766
  const trimmedStderr = stderr.trim();
29475
- resolve10({
29767
+ resolve11({
29476
29768
  success: code === 0,
29477
29769
  output: stdout.trim(),
29478
29770
  ...trimmedStderr && { error: trimmedStderr }
@@ -29482,7 +29774,7 @@ function executeCommand(binary2, args, options2 = {}) {
29482
29774
  if (resolved)
29483
29775
  return;
29484
29776
  resolved = true;
29485
- resolve10({
29777
+ resolve11({
29486
29778
  success: false,
29487
29779
  output: "",
29488
29780
  error: `Failed to execute ${binary2} CLI: ${err.message}`
@@ -29501,7 +29793,7 @@ function mergeNativeSyncResults(results) {
29501
29793
  var init_types2 = () => {};
29502
29794
 
29503
29795
  // src/core/codex-mcp.ts
29504
- import { existsSync as existsSync14, readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
29796
+ import { existsSync as existsSync15, readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
29505
29797
  import { dirname as dirname8 } from "node:path";
29506
29798
  function buildCodexMcpAddArgs(name, config) {
29507
29799
  if (typeof config.url === "string") {
@@ -29712,7 +30004,7 @@ function syncCodexProjectMcpConfig(validatedPlugins, options2) {
29712
30004
  trackedServers: []
29713
30005
  };
29714
30006
  let existingContent = "";
29715
- if (existsSync14(configPath)) {
30007
+ if (existsSync15(configPath)) {
29716
30008
  try {
29717
30009
  existingContent = readFileSync2(configPath, "utf-8");
29718
30010
  } catch {
@@ -29768,7 +30060,7 @@ function syncCodexProjectMcpConfig(validatedPlugins, options2) {
29768
30060
  `)}
29769
30061
  `;
29770
30062
  const dir = dirname8(configPath);
29771
- if (!existsSync14(dir)) {
30063
+ if (!existsSync15(dir)) {
29772
30064
  mkdirSync3(dir, { recursive: true });
29773
30065
  }
29774
30066
  writeFileSync3(configPath, output, "utf-8");
@@ -29782,7 +30074,7 @@ var init_codex_mcp = __esm(() => {
29782
30074
  });
29783
30075
 
29784
30076
  // src/core/claude-mcp.ts
29785
- import { existsSync as existsSync15, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "node:fs";
30077
+ import { existsSync as existsSync16, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "node:fs";
29786
30078
  import { dirname as dirname9 } from "node:path";
29787
30079
  function deepEqual2(a, b) {
29788
30080
  if (a === b)
@@ -29850,7 +30142,7 @@ function syncClaudeMcpConfig(validatedPlugins, options2) {
29850
30142
  trackedServers: []
29851
30143
  };
29852
30144
  let existingConfig = {};
29853
- if (existsSync15(configPath)) {
30145
+ if (existsSync16(configPath)) {
29854
30146
  try {
29855
30147
  const content = readFileSync3(configPath, "utf-8");
29856
30148
  existingConfig = import_json52.default.parse(content);
@@ -29901,7 +30193,7 @@ function syncClaudeMcpConfig(validatedPlugins, options2) {
29901
30193
  if (hasChanges && !dryRun) {
29902
30194
  existingConfig.mcpServers = existingServers;
29903
30195
  const dir = dirname9(configPath);
29904
- if (!existsSync15(dir)) {
30196
+ if (!existsSync16(dir)) {
29905
30197
  mkdirSync4(dir, { recursive: true });
29906
30198
  }
29907
30199
  writeFileSync4(configPath, `${JSON.stringify(existingConfig, null, 2)}
@@ -29919,7 +30211,7 @@ function syncClaudeMcpConfig(validatedPlugins, options2) {
29919
30211
  if (result.removed > 0 && !dryRun) {
29920
30212
  existingConfig.mcpServers = existingServers;
29921
30213
  const dir = dirname9(configPath);
29922
- if (!existsSync15(dir)) {
30214
+ if (!existsSync16(dir)) {
29923
30215
  mkdirSync4(dir, { recursive: true });
29924
30216
  }
29925
30217
  writeFileSync4(configPath, `${JSON.stringify(existingConfig, null, 2)}
@@ -30018,41 +30310,41 @@ var init_claude_mcp = __esm(() => {
30018
30310
  });
30019
30311
 
30020
30312
  // src/core/copilot-mcp.ts
30021
- import { join as join17 } from "node:path";
30313
+ import { join as join18 } from "node:path";
30022
30314
  function getCopilotMcpConfigPath() {
30023
- return join17(getHomeDir(), ".copilot", "mcp-config.json");
30315
+ return join18(getHomeDir(), ".copilot", "mcp-config.json");
30024
30316
  }
30025
30317
  var init_copilot_mcp = __esm(() => {
30026
30318
  init_constants();
30027
30319
  });
30028
30320
 
30029
30321
  // src/core/mcp-sync.ts
30030
- import { existsSync as existsSync16 } from "node:fs";
30031
- import { join as join18 } from "node:path";
30322
+ import { existsSync as existsSync17 } from "node:fs";
30323
+ import { join as join19 } from "node:path";
30032
30324
  function buildSyncSpecs(workspacePath) {
30033
30325
  return [
30034
30326
  {
30035
30327
  client: "vscode",
30036
30328
  scope: "vscode",
30037
- configPath: join18(workspacePath, ".vscode", "mcp.json"),
30329
+ configPath: join19(workspacePath, ".vscode", "mcp.json"),
30038
30330
  syncFn: syncVscodeMcpConfig
30039
30331
  },
30040
30332
  {
30041
30333
  client: "claude",
30042
30334
  scope: "claude",
30043
- configPath: join18(workspacePath, ".mcp.json"),
30335
+ configPath: join19(workspacePath, ".mcp.json"),
30044
30336
  syncFn: syncClaudeMcpConfig
30045
30337
  },
30046
30338
  {
30047
30339
  client: "codex",
30048
30340
  scope: "codex",
30049
- configPath: join18(workspacePath, ".codex", "config.toml"),
30341
+ configPath: join19(workspacePath, ".codex", "config.toml"),
30050
30342
  syncFn: syncCodexProjectMcpConfig
30051
30343
  },
30052
30344
  {
30053
30345
  client: "copilot",
30054
30346
  scope: "copilot",
30055
- configPath: join18(workspacePath, ".copilot", "mcp-config.json"),
30347
+ configPath: join19(workspacePath, ".copilot", "mcp-config.json"),
30056
30348
  syncFn: syncClaudeMcpConfig
30057
30349
  }
30058
30350
  ];
@@ -30107,8 +30399,8 @@ function syncMcpServers(workspacePath, validPlugins, config, previousState, sync
30107
30399
  async function syncMcpOnly(workspacePath = process.cwd(), options2 = {}) {
30108
30400
  const { offline = false, dryRun = false } = options2;
30109
30401
  await migrateWorkspaceSkillsV1toV2(workspacePath);
30110
- const configPath = join18(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
30111
- if (!existsSync16(configPath)) {
30402
+ const configPath = join19(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
30403
+ if (!existsSync17(configPath)) {
30112
30404
  return {
30113
30405
  success: false,
30114
30406
  mcpResults: {},
@@ -30482,9 +30774,9 @@ function padStart2(str3, len) {
30482
30774
  }
30483
30775
 
30484
30776
  // src/core/managed-repos.ts
30485
- import { existsSync as existsSync17 } from "node:fs";
30777
+ import { existsSync as existsSync18 } from "node:fs";
30486
30778
  import { mkdir as mkdir8 } from "node:fs/promises";
30487
- import { dirname as dirname10, resolve as resolve10 } from "node:path";
30779
+ import { dirname as dirname10, resolve as resolve11 } from "node:path";
30488
30780
  function expandHome(p) {
30489
30781
  if (p.startsWith("~/") || p === "~") {
30490
30782
  return p.replace("~", getHomeDir());
@@ -30565,8 +30857,8 @@ async function processManagedRepos(repositories, workspacePath, options2 = {}) {
30565
30857
  continue;
30566
30858
  }
30567
30859
  const expandedPath = expandHome(repo.path);
30568
- const absolutePath = resolve10(workspacePath, expandedPath);
30569
- if (!existsSync17(absolutePath)) {
30860
+ const absolutePath = resolve11(workspacePath, expandedPath);
30861
+ if (!existsSync18(absolutePath)) {
30570
30862
  if (!shouldClone(repo.managed)) {
30571
30863
  results.push({ path: repo.path, repo: repo.repo, action: "skipped" });
30572
30864
  continue;
@@ -30617,9 +30909,9 @@ var init_managed_repos = __esm(() => {
30617
30909
  });
30618
30910
 
30619
30911
  // src/core/sync.ts
30620
- import { existsSync as existsSync18, readFileSync as readFileSync4, writeFileSync as writeFileSync5, lstatSync as lstatSync2 } from "node:fs";
30912
+ import { existsSync as existsSync19, readFileSync as readFileSync4, writeFileSync as writeFileSync5, lstatSync as lstatSync2 } from "node:fs";
30621
30913
  import { rm as rm4, unlink as unlink2, rmdir, copyFile } from "node:fs/promises";
30622
- import { join as join19, resolve as resolve11, dirname as dirname11, relative as relative5 } from "node:path";
30914
+ import { join as join20, resolve as resolve12, dirname as dirname11, relative as relative5 } from "node:path";
30623
30915
  function deduplicateClientsByPath(clients, clientMappings = CLIENT_MAPPINGS) {
30624
30916
  const pathToClients = new Map;
30625
30917
  for (const client of clients) {
@@ -30782,7 +31074,7 @@ async function selectivePurgeWorkspace(workspacePath, state, clients) {
30782
31074
  const previousFiles = getPreviouslySyncedFiles(state, client);
30783
31075
  const purgedPaths = [];
30784
31076
  for (const filePath of previousFiles) {
30785
- const fullPath = join19(workspacePath, filePath);
31077
+ const fullPath = join20(workspacePath, filePath);
30786
31078
  const cleanPath = fullPath.replace(/\/$/, "");
30787
31079
  let stats;
30788
31080
  try {
@@ -30811,8 +31103,8 @@ async function selectivePurgeWorkspace(workspacePath, state, clients) {
30811
31103
  async function cleanupEmptyParents(workspacePath, filePath) {
30812
31104
  let parentPath = dirname11(filePath);
30813
31105
  while (parentPath && parentPath !== "." && parentPath !== "/") {
30814
- const fullParentPath = join19(workspacePath, parentPath);
30815
- if (!existsSync18(fullParentPath)) {
31106
+ const fullParentPath = join20(workspacePath, parentPath);
31107
+ if (!existsSync19(fullParentPath)) {
30816
31108
  parentPath = dirname11(parentPath);
30817
31109
  continue;
30818
31110
  }
@@ -30897,8 +31189,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
30897
31189
  errors2.push(`Cannot resolve file '${file}' - no workspace.source configured`);
30898
31190
  continue;
30899
31191
  }
30900
- const fullPath = join19(defaultSourcePath, file);
30901
- if (!existsSync18(fullPath)) {
31192
+ const fullPath = join20(defaultSourcePath, file);
31193
+ if (!existsSync19(fullPath)) {
30902
31194
  errors2.push(`File source not found: ${fullPath}`);
30903
31195
  }
30904
31196
  continue;
@@ -30916,8 +31208,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
30916
31208
  errors2.push(`GitHub cache not found for ${cacheKey}`);
30917
31209
  continue;
30918
31210
  }
30919
- const fullPath = join19(cachePath, parsed.filePath);
30920
- if (!existsSync18(fullPath)) {
31211
+ const fullPath = join20(cachePath, parsed.filePath);
31212
+ if (!existsSync19(fullPath)) {
30921
31213
  errors2.push(`Path not found in repository: ${cacheKey}/${parsed.filePath}`);
30922
31214
  }
30923
31215
  } else {
@@ -30925,13 +31217,13 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
30925
31217
  if (file.source.startsWith("/")) {
30926
31218
  fullPath = file.source;
30927
31219
  } else if (file.source.startsWith("../")) {
30928
- fullPath = resolve11(file.source);
31220
+ fullPath = resolve12(file.source);
30929
31221
  } else if (defaultSourcePath) {
30930
- fullPath = join19(defaultSourcePath, file.source);
31222
+ fullPath = join20(defaultSourcePath, file.source);
30931
31223
  } else {
30932
- fullPath = resolve11(file.source);
31224
+ fullPath = resolve12(file.source);
30933
31225
  }
30934
- if (!existsSync18(fullPath)) {
31226
+ if (!existsSync19(fullPath)) {
30935
31227
  errors2.push(`File source not found: ${fullPath}`);
30936
31228
  }
30937
31229
  }
@@ -30940,8 +31232,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
30940
31232
  errors2.push(`Cannot resolve file '${file.dest}' - no workspace.source configured and no explicit source provided`);
30941
31233
  continue;
30942
31234
  }
30943
- const fullPath = join19(defaultSourcePath, file.dest ?? "");
30944
- if (!existsSync18(fullPath)) {
31235
+ const fullPath = join20(defaultSourcePath, file.dest ?? "");
31236
+ if (!existsSync19(fullPath)) {
30945
31237
  errors2.push(`File source not found: ${fullPath}`);
30946
31238
  }
30947
31239
  }
@@ -31089,7 +31381,7 @@ async function validatePlugin(pluginSource, workspacePath, offline) {
31089
31381
  ...fetchResult.error && { error: fetchResult.error }
31090
31382
  };
31091
31383
  }
31092
- const resolvedPath2 = parsed?.subpath ? join19(fetchResult.cachePath, parsed.subpath) : fetchResult.cachePath;
31384
+ const resolvedPath2 = parsed?.subpath ? join20(fetchResult.cachePath, parsed.subpath) : fetchResult.cachePath;
31093
31385
  return {
31094
31386
  plugin: pluginSource,
31095
31387
  resolved: resolvedPath2,
@@ -31098,8 +31390,8 @@ async function validatePlugin(pluginSource, workspacePath, offline) {
31098
31390
  nativeClients: []
31099
31391
  };
31100
31392
  }
31101
- const resolvedPath = resolve11(workspacePath, pluginSource);
31102
- if (!existsSync18(resolvedPath)) {
31393
+ const resolvedPath = resolve12(workspacePath, pluginSource);
31394
+ if (!existsSync19(resolvedPath)) {
31103
31395
  return {
31104
31396
  plugin: pluginSource,
31105
31397
  resolved: resolvedPath,
@@ -31272,10 +31564,10 @@ function buildPluginSkillNameMaps(allSkills) {
31272
31564
  return pluginMaps;
31273
31565
  }
31274
31566
  function generateVscodeWorkspaceFile(workspacePath, config) {
31275
- const configDir = join19(workspacePath, CONFIG_DIR);
31276
- const templatePath = join19(configDir, VSCODE_TEMPLATE_FILE);
31567
+ const configDir = join20(workspacePath, CONFIG_DIR);
31568
+ const templatePath = join20(configDir, VSCODE_TEMPLATE_FILE);
31277
31569
  let template;
31278
- if (existsSync18(templatePath)) {
31570
+ if (existsSync19(templatePath)) {
31279
31571
  try {
31280
31572
  template = import_json53.default.parse(readFileSync4(templatePath, "utf-8"));
31281
31573
  } catch (error) {
@@ -31427,7 +31719,7 @@ async function syncVscodeWorkspaceFile(workspacePath, config, configPath, previo
31427
31719
  let updatedConfig = config;
31428
31720
  if (previousState?.vscodeWorkspaceHash && previousState?.vscodeWorkspaceRepos) {
31429
31721
  const outputPath = getWorkspaceOutputPath(workspacePath, config.vscode);
31430
- if (existsSync18(outputPath)) {
31722
+ if (existsSync19(outputPath)) {
31431
31723
  const existingContent = readFileSync4(outputPath, "utf-8");
31432
31724
  const currentHash = computeWorkspaceHash(existingContent);
31433
31725
  if (currentHash !== previousState.vscodeWorkspaceHash) {
@@ -31460,7 +31752,7 @@ async function syncVscodeWorkspaceFile(workspacePath, config, configPath, previo
31460
31752
  }
31461
31753
  const writtenContent = generateVscodeWorkspaceFile(workspacePath, updatedConfig);
31462
31754
  const hash = computeWorkspaceHash(writtenContent);
31463
- const repos = updatedConfig.repositories.map((r) => resolve11(workspacePath, r.path).replace(/\\/g, "/"));
31755
+ const repos = updatedConfig.repositories.map((r) => resolve12(workspacePath, r.path).replace(/\\/g, "/"));
31464
31756
  return { config: updatedConfig, hash, repos };
31465
31757
  }
31466
31758
  async function buildSourcesProvenance(validatedPlugins, pluginEntries) {
@@ -31542,9 +31834,9 @@ async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
31542
31834
  skipManaged = false
31543
31835
  } = options2;
31544
31836
  const sw = new Stopwatch;
31545
- const configDir = join19(workspacePath, CONFIG_DIR);
31546
- const configPath = join19(configDir, WORKSPACE_CONFIG_FILE);
31547
- if (!existsSync18(configPath)) {
31837
+ const configDir = join20(workspacePath, CONFIG_DIR);
31838
+ const configPath = join20(configDir, WORKSPACE_CONFIG_FILE);
31839
+ if (!existsSync19(configPath)) {
31548
31840
  return failedSyncResult(`${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
31549
31841
  Run 'allagents workspace init <path>' to create a new workspace`);
31550
31842
  }
@@ -31641,8 +31933,8 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
31641
31933
  const filesToCopy = [...config.workspace.files];
31642
31934
  if (hasRepositories && sourcePath) {
31643
31935
  for (const agentFile of AGENT_FILES) {
31644
- const agentPath = join19(sourcePath, agentFile);
31645
- if (existsSync18(agentPath) && !filesToCopy.includes(agentFile)) {
31936
+ const agentPath = join20(sourcePath, agentFile);
31937
+ if (existsSync19(agentPath) && !filesToCopy.includes(agentFile)) {
31646
31938
  filesToCopy.push(agentFile);
31647
31939
  }
31648
31940
  }
@@ -31682,10 +31974,10 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
31682
31974
  skillsIndexRefs
31683
31975
  });
31684
31976
  if (hasRepositories && !dryRun && syncClients.includes("claude") && sourcePath) {
31685
- const claudePath = join19(workspacePath, "CLAUDE.md");
31686
- const agentsPath = join19(workspacePath, "AGENTS.md");
31687
- const claudeExistsInSource = existsSync18(join19(sourcePath, "CLAUDE.md"));
31688
- if (!claudeExistsInSource && existsSync18(agentsPath) && !existsSync18(claudePath)) {
31977
+ const claudePath = join20(workspacePath, "CLAUDE.md");
31978
+ const agentsPath = join20(workspacePath, "AGENTS.md");
31979
+ const claudeExistsInSource = existsSync19(join20(sourcePath, "CLAUDE.md"));
31980
+ if (!claudeExistsInSource && existsSync19(agentsPath) && !existsSync19(claudePath)) {
31689
31981
  await copyFile(agentsPath, claudePath);
31690
31982
  }
31691
31983
  }
@@ -31769,7 +32061,7 @@ async function seedFetchCacheFromMarketplaces(results) {
31769
32061
  }
31770
32062
  function readGitBranch(repoPath) {
31771
32063
  try {
31772
- const head = readFileSync4(join19(repoPath, ".git", "HEAD"), "utf-8").trim();
32064
+ const head = readFileSync4(join20(repoPath, ".git", "HEAD"), "utf-8").trim();
31773
32065
  const prefix = "ref: refs/heads/";
31774
32066
  return head.startsWith(prefix) ? head.slice(prefix.length) : null;
31775
32067
  } catch {
@@ -31779,7 +32071,7 @@ function readGitBranch(repoPath) {
31779
32071
  async function syncUserWorkspace(options2 = {}) {
31780
32072
  await migrateUserWorkspaceSkillsV1toV2();
31781
32073
  const sw = new Stopwatch;
31782
- const homeDir = resolve11(getHomeDir());
32074
+ const homeDir = resolve12(getHomeDir());
31783
32075
  const config = await getUserWorkspaceConfig();
31784
32076
  if (!config) {
31785
32077
  return {
@@ -31977,11 +32269,11 @@ var init_sync = __esm(() => {
31977
32269
  });
31978
32270
 
31979
32271
  // src/core/github-fetch.ts
31980
- import { existsSync as existsSync19, readFileSync as readFileSync5 } from "node:fs";
31981
- import { join as join20 } from "node:path";
32272
+ import { existsSync as existsSync20, readFileSync as readFileSync5 } from "node:fs";
32273
+ import { join as join21 } from "node:path";
31982
32274
  function readFileFromClone(tempDir, filePath) {
31983
- const fullPath = join20(tempDir, filePath);
31984
- if (existsSync19(fullPath)) {
32275
+ const fullPath = join21(tempDir, filePath);
32276
+ if (existsSync20(fullPath)) {
31985
32277
  return readFileSync5(fullPath, "utf-8");
31986
32278
  }
31987
32279
  return null;
@@ -32080,15 +32372,15 @@ var init_github_fetch = __esm(() => {
32080
32372
  });
32081
32373
 
32082
32374
  // src/core/workspace.ts
32083
- import { cp as cp2, mkdir as mkdir9, readFile as readFile12, writeFile as writeFile8, copyFile as copyFile2, unlink as unlink3 } from "node:fs/promises";
32084
- import { existsSync as existsSync20 } from "node:fs";
32085
- import { join as join21, resolve as resolve12, dirname as dirname12, relative as relative6, sep as sep2, isAbsolute as isAbsolute4 } from "node:path";
32375
+ import { cp as cp2, mkdir as mkdir9, readFile as readFile13, writeFile as writeFile8, copyFile as copyFile2, unlink as unlink3 } from "node:fs/promises";
32376
+ import { existsSync as existsSync21 } from "node:fs";
32377
+ import { join as join22, resolve as resolve13, dirname as dirname12, relative as relative6, sep as sep2, isAbsolute as isAbsolute4 } from "node:path";
32086
32378
  import { fileURLToPath } from "node:url";
32087
32379
  async function initWorkspace(targetPath = ".", options2 = {}) {
32088
- const absoluteTarget = resolve12(targetPath);
32089
- const configDir = join21(absoluteTarget, CONFIG_DIR);
32090
- const configPath = join21(configDir, WORKSPACE_CONFIG_FILE);
32091
- if (existsSync20(configPath)) {
32380
+ const absoluteTarget = resolve13(targetPath);
32381
+ const configDir = join22(absoluteTarget, CONFIG_DIR);
32382
+ const configPath = join22(configDir, WORKSPACE_CONFIG_FILE);
32383
+ if (existsSync21(configPath)) {
32092
32384
  if (options2.force) {
32093
32385
  await unlink3(configPath);
32094
32386
  } else {
@@ -32099,7 +32391,7 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
32099
32391
  const currentFilePath = fileURLToPath(import.meta.url);
32100
32392
  const currentFileDir = dirname12(currentFilePath);
32101
32393
  const isProduction = currentFilePath.includes(`${sep2}dist${sep2}`);
32102
- const defaultTemplatePath = isProduction ? join21(currentFileDir, "templates", "default") : join21(currentFileDir, "..", "templates", "default");
32394
+ const defaultTemplatePath = isProduction ? join22(currentFileDir, "templates", "default") : join22(currentFileDir, "..", "templates", "default");
32103
32395
  let githubTempDir;
32104
32396
  let parsedFromUrl;
32105
32397
  let githubBasePath = "";
@@ -32138,20 +32430,20 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
32138
32430
  }
32139
32431
  console.log(`✓ Using workspace.yaml from: ${options2.from}`);
32140
32432
  } else {
32141
- const fromPath = resolve12(options2.from);
32142
- if (!existsSync20(fromPath)) {
32433
+ const fromPath = resolve13(options2.from);
32434
+ if (!existsSync21(fromPath)) {
32143
32435
  throw new Error(`Template not found: ${fromPath}`);
32144
32436
  }
32145
32437
  const { stat: stat2 } = await import("node:fs/promises");
32146
32438
  const fromStat = await stat2(fromPath);
32147
32439
  let sourceYamlPath;
32148
32440
  if (fromStat.isDirectory()) {
32149
- const nestedPath = join21(fromPath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
32150
- const rootPath = join21(fromPath, WORKSPACE_CONFIG_FILE);
32151
- if (existsSync20(nestedPath)) {
32441
+ const nestedPath = join22(fromPath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
32442
+ const rootPath = join22(fromPath, WORKSPACE_CONFIG_FILE);
32443
+ if (existsSync21(nestedPath)) {
32152
32444
  sourceYamlPath = nestedPath;
32153
32445
  sourceDir = fromPath;
32154
- } else if (existsSync20(rootPath)) {
32446
+ } else if (existsSync21(rootPath)) {
32155
32447
  sourceYamlPath = rootPath;
32156
32448
  sourceDir = fromPath;
32157
32449
  } else {
@@ -32167,14 +32459,14 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
32167
32459
  sourceDir = parentDir;
32168
32460
  }
32169
32461
  }
32170
- workspaceYamlContent = await readFile12(sourceYamlPath, "utf-8");
32462
+ workspaceYamlContent = await readFile13(sourceYamlPath, "utf-8");
32171
32463
  if (sourceDir) {
32172
32464
  const parsed2 = load(workspaceYamlContent);
32173
32465
  const workspace = parsed2?.workspace;
32174
32466
  if (workspace?.source) {
32175
32467
  const source = workspace.source;
32176
32468
  if (!isGitHubUrl(source) && !isAbsolute4(source)) {
32177
- workspace.source = resolve12(sourceDir, source);
32469
+ workspace.source = resolve13(sourceDir, source);
32178
32470
  workspaceYamlContent = dump(parsed2, { lineWidth: -1 });
32179
32471
  }
32180
32472
  }
@@ -32182,11 +32474,11 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
32182
32474
  console.log(`✓ Using workspace.yaml from: ${sourceYamlPath}`);
32183
32475
  }
32184
32476
  } else {
32185
- const defaultYamlPath = join21(defaultTemplatePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
32186
- if (!existsSync20(defaultYamlPath)) {
32477
+ const defaultYamlPath = join22(defaultTemplatePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
32478
+ if (!existsSync21(defaultYamlPath)) {
32187
32479
  throw new Error(`Default template not found at: ${defaultTemplatePath}`);
32188
32480
  }
32189
- workspaceYamlContent = await readFile12(defaultYamlPath, "utf-8");
32481
+ workspaceYamlContent = await readFile13(defaultYamlPath, "utf-8");
32190
32482
  }
32191
32483
  if (options2.clients && options2.clients.length > 0) {
32192
32484
  const configParsed = load(workspaceYamlContent);
@@ -32199,8 +32491,8 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
32199
32491
  const clientNames = getClientTypes(clients);
32200
32492
  const VSCODE_TEMPLATE_FILE2 = "template.code-workspace";
32201
32493
  if (clientNames.includes("vscode") && options2.from) {
32202
- const targetTemplatePath = join21(configDir, VSCODE_TEMPLATE_FILE2);
32203
- if (!existsSync20(targetTemplatePath)) {
32494
+ const targetTemplatePath = join22(configDir, VSCODE_TEMPLATE_FILE2);
32495
+ if (!existsSync21(targetTemplatePath)) {
32204
32496
  if (isGitHubUrl(options2.from) && githubTempDir) {
32205
32497
  if (parsedFromUrl) {
32206
32498
  const templatePath = githubBasePath ? `${githubBasePath}/${CONFIG_DIR}/${VSCODE_TEMPLATE_FILE2}` : `${CONFIG_DIR}/${VSCODE_TEMPLATE_FILE2}`;
@@ -32210,8 +32502,8 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
32210
32502
  }
32211
32503
  }
32212
32504
  } else if (sourceDir) {
32213
- const sourceTemplatePath = join21(sourceDir, CONFIG_DIR, VSCODE_TEMPLATE_FILE2);
32214
- if (existsSync20(sourceTemplatePath)) {
32505
+ const sourceTemplatePath = join22(sourceDir, CONFIG_DIR, VSCODE_TEMPLATE_FILE2);
32506
+ if (existsSync21(sourceTemplatePath)) {
32215
32507
  await copyFile2(sourceTemplatePath, targetTemplatePath);
32216
32508
  }
32217
32509
  }
@@ -32224,8 +32516,8 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
32224
32516
  if (options2.from && isGitHubUrl(options2.from) && githubTempDir) {
32225
32517
  if (parsedFromUrl) {
32226
32518
  for (const agentFile of AGENT_FILES) {
32227
- const targetFilePath = join21(absoluteTarget, agentFile);
32228
- if (existsSync20(targetFilePath)) {
32519
+ const targetFilePath = join22(absoluteTarget, agentFile);
32520
+ if (existsSync21(targetFilePath)) {
32229
32521
  copiedAgentFiles.push(agentFile);
32230
32522
  continue;
32231
32523
  }
@@ -32240,30 +32532,30 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
32240
32532
  } else {
32241
32533
  const effectiveSourceDir = sourceDir ?? defaultTemplatePath;
32242
32534
  for (const agentFile of AGENT_FILES) {
32243
- const targetFilePath = join21(absoluteTarget, agentFile);
32244
- if (existsSync20(targetFilePath)) {
32535
+ const targetFilePath = join22(absoluteTarget, agentFile);
32536
+ if (existsSync21(targetFilePath)) {
32245
32537
  copiedAgentFiles.push(agentFile);
32246
32538
  continue;
32247
32539
  }
32248
- const sourcePath = join21(effectiveSourceDir, agentFile);
32249
- if (existsSync20(sourcePath)) {
32250
- const content = await readFile12(sourcePath, "utf-8");
32540
+ const sourcePath = join22(effectiveSourceDir, agentFile);
32541
+ if (existsSync21(sourcePath)) {
32542
+ const content = await readFile13(sourcePath, "utf-8");
32251
32543
  await writeFile8(targetFilePath, content, "utf-8");
32252
32544
  copiedAgentFiles.push(agentFile);
32253
32545
  }
32254
32546
  }
32255
32547
  }
32256
32548
  if (copiedAgentFiles.length === 0) {
32257
- await ensureWorkspaceRules(join21(absoluteTarget, "AGENTS.md"), repositories);
32549
+ await ensureWorkspaceRules(join22(absoluteTarget, "AGENTS.md"), repositories);
32258
32550
  copiedAgentFiles.push("AGENTS.md");
32259
32551
  } else {
32260
32552
  for (const agentFile of copiedAgentFiles) {
32261
- await ensureWorkspaceRules(join21(absoluteTarget, agentFile), repositories);
32553
+ await ensureWorkspaceRules(join22(absoluteTarget, agentFile), repositories);
32262
32554
  }
32263
32555
  }
32264
32556
  if (clientNames.includes("claude") && !copiedAgentFiles.includes("CLAUDE.md") && copiedAgentFiles.includes("AGENTS.md")) {
32265
- const agentsPath = join21(absoluteTarget, "AGENTS.md");
32266
- const claudePath = join21(absoluteTarget, "CLAUDE.md");
32557
+ const agentsPath = join22(absoluteTarget, "AGENTS.md");
32558
+ const claudePath = join22(absoluteTarget, "CLAUDE.md");
32267
32559
  await copyFile2(agentsPath, claudePath);
32268
32560
  }
32269
32561
  }
@@ -32308,14 +32600,14 @@ Next steps:`);
32308
32600
  async function seedCacheFromClone(tempDir, owner, repo, branch) {
32309
32601
  const cachePaths = [
32310
32602
  getPluginCachePath(owner, repo, branch),
32311
- join21(getMarketplacesDir(), repo)
32603
+ join22(getMarketplacesDir(), repo)
32312
32604
  ];
32313
32605
  for (const cachePath of cachePaths) {
32314
- if (existsSync20(cachePath))
32606
+ if (existsSync21(cachePath))
32315
32607
  continue;
32316
32608
  try {
32317
32609
  const parentDir = dirname12(cachePath);
32318
- if (!existsSync20(parentDir)) {
32610
+ if (!existsSync21(parentDir)) {
32319
32611
  await mkdir9(parentDir, { recursive: true });
32320
32612
  }
32321
32613
  await cp2(tempDir, cachePath, { recursive: true });
@@ -32336,11 +32628,11 @@ var init_workspace = __esm(() => {
32336
32628
  });
32337
32629
 
32338
32630
  // src/core/status.ts
32339
- import { existsSync as existsSync21 } from "node:fs";
32340
- import { join as join22 } from "node:path";
32631
+ import { existsSync as existsSync22 } from "node:fs";
32632
+ import { join as join23 } from "node:path";
32341
32633
  async function getWorkspaceStatus(workspacePath = process.cwd()) {
32342
- const configPath = join22(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
32343
- if (!existsSync21(configPath) || isUserConfigPath(workspacePath)) {
32634
+ const configPath = join23(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
32635
+ if (!existsSync22(configPath) || isUserConfigPath(workspacePath)) {
32344
32636
  const userPlugins = await getUserPluginStatuses();
32345
32637
  return {
32346
32638
  success: true,
@@ -32382,7 +32674,7 @@ async function getWorkspaceStatus(workspacePath = process.cwd()) {
32382
32674
  function getPluginStatus(parsed) {
32383
32675
  if (parsed.type === "github") {
32384
32676
  const cachePath = parsed.owner && parsed.repo ? getPluginCachePath(parsed.owner, parsed.repo) : "";
32385
- const available2 = cachePath ? existsSync21(cachePath) : false;
32677
+ const available2 = cachePath ? existsSync22(cachePath) : false;
32386
32678
  return {
32387
32679
  source: parsed.original,
32388
32680
  type: "github",
@@ -32392,7 +32684,7 @@ function getPluginStatus(parsed) {
32392
32684
  ...parsed.repo && { repo: parsed.repo }
32393
32685
  };
32394
32686
  }
32395
- const available = existsSync21(parsed.normalized);
32687
+ const available = existsSync22(parsed.normalized);
32396
32688
  return {
32397
32689
  source: parsed.original,
32398
32690
  type: "local",
@@ -34553,160 +34845,6 @@ var init_prompt_clients = __esm(() => {
34553
34845
  ({ autocompleteMultiselect } = exports_dist);
34554
34846
  });
34555
34847
 
34556
- // src/core/skills.ts
34557
- import { existsSync as existsSync24 } from "node:fs";
34558
- import { readFile as readFile14, readdir as readdir5 } from "node:fs/promises";
34559
- import { join as join25, basename as basename7, resolve as resolve14 } from "node:path";
34560
- async function resolvePluginPath(pluginSource, workspacePath) {
34561
- if (isPluginSpec(pluginSource)) {
34562
- const resolved2 = await resolvePluginSpecWithAutoRegister(pluginSource, {
34563
- offline: true,
34564
- workspacePath
34565
- });
34566
- if (!resolved2.success || !resolved2.path)
34567
- return null;
34568
- return {
34569
- path: resolved2.path,
34570
- pluginName: resolved2.pluginName
34571
- };
34572
- }
34573
- if (isGitHubUrl(pluginSource)) {
34574
- const parsed = parseGitHubUrl(pluginSource);
34575
- const result = await fetchPlugin(pluginSource, {
34576
- offline: true,
34577
- ...parsed?.branch && { branch: parsed.branch }
34578
- });
34579
- if (!result.success)
34580
- return null;
34581
- const path = parsed?.subpath ? join25(result.cachePath, parsed.subpath) : result.cachePath;
34582
- return { path };
34583
- }
34584
- const resolved = resolve14(workspacePath, pluginSource);
34585
- return existsSync24(resolved) ? { path: resolved } : null;
34586
- }
34587
- async function getAllSkillsFromPlugins(workspacePath = process.cwd()) {
34588
- const configPath = join25(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
34589
- if (!existsSync24(configPath)) {
34590
- return [];
34591
- }
34592
- const content = await readFile14(configPath, "utf-8");
34593
- const config = load(content);
34594
- const isV1Fallback = config.version === undefined || config.version < 2;
34595
- const disabledSkills = isV1Fallback ? new Set(config.disabledSkills ?? []) : new Set;
34596
- const enabledSkills = isV1Fallback && config.enabledSkills ? new Set(config.enabledSkills) : null;
34597
- const skills = [];
34598
- for (const pluginEntry of config.plugins) {
34599
- const pluginSource = getPluginSource(pluginEntry);
34600
- const resolved = await resolvePluginPath(pluginSource, workspacePath);
34601
- if (!resolved)
34602
- continue;
34603
- const pluginPath = resolved.path;
34604
- const pluginName = resolved.pluginName ?? getPluginName(pluginPath);
34605
- const skillsDir = join25(pluginPath, "skills");
34606
- const pluginSkillsConfig = typeof pluginEntry === "string" ? undefined : pluginEntry.skills;
34607
- const hasEnabledEntries = !pluginSkillsConfig && enabledSkills && [...enabledSkills].some((s) => s.startsWith(`${pluginName}`));
34608
- let skillEntries;
34609
- if (existsSync24(skillsDir)) {
34610
- const entries = await readdir5(skillsDir, { withFileTypes: true });
34611
- skillEntries = entries.filter((e) => e.isDirectory()).map((e) => ({ name: e.name, skillPath: join25(skillsDir, e.name) }));
34612
- } else {
34613
- const entries = await readdir5(pluginPath, { withFileTypes: true });
34614
- const flatSkills = [];
34615
- for (const entry of entries) {
34616
- if (!entry.isDirectory())
34617
- continue;
34618
- const skillMdPath = join25(pluginPath, entry.name, "SKILL.md");
34619
- if (existsSync24(skillMdPath)) {
34620
- flatSkills.push({ name: entry.name, skillPath: join25(pluginPath, entry.name) });
34621
- }
34622
- }
34623
- if (flatSkills.length > 0) {
34624
- skillEntries = flatSkills;
34625
- } else {
34626
- const rootSkillMd = join25(pluginPath, "SKILL.md");
34627
- if (existsSync24(rootSkillMd)) {
34628
- const skillContent = await readFile14(rootSkillMd, "utf-8");
34629
- const metadata = parseSkillMetadata(skillContent);
34630
- const skillName = metadata?.name ?? basename7(pluginPath);
34631
- skillEntries = [{ name: skillName, skillPath: pluginPath }];
34632
- } else {
34633
- skillEntries = [];
34634
- }
34635
- }
34636
- }
34637
- const pluginSkillsMode = pluginSkillsConfig === undefined ? "none" : Array.isArray(pluginSkillsConfig) ? "allowlist" : "blocklist";
34638
- for (const { name, skillPath } of skillEntries) {
34639
- const skillKey = `${pluginName}:${name}`;
34640
- let isDisabled;
34641
- if (pluginSkillsConfig !== undefined) {
34642
- if (Array.isArray(pluginSkillsConfig)) {
34643
- isDisabled = !pluginSkillsConfig.includes(name);
34644
- } else {
34645
- isDisabled = pluginSkillsConfig.exclude.includes(name);
34646
- }
34647
- } else if (isV1Fallback) {
34648
- isDisabled = hasEnabledEntries ? !enabledSkills?.has(skillKey) : disabledSkills.has(skillKey);
34649
- } else {
34650
- isDisabled = false;
34651
- }
34652
- skills.push({
34653
- name,
34654
- pluginName,
34655
- pluginSource,
34656
- path: skillPath,
34657
- disabled: isDisabled,
34658
- pluginSkillsMode
34659
- });
34660
- }
34661
- }
34662
- return skills;
34663
- }
34664
- async function findSkillByName(skillName, workspacePath = process.cwd()) {
34665
- const allSkills = await getAllSkillsFromPlugins(workspacePath);
34666
- return allSkills.filter((s) => s.name === skillName);
34667
- }
34668
- async function discoverSkillNames(pluginPath) {
34669
- if (!existsSync24(pluginPath))
34670
- return [];
34671
- const skillsDir = join25(pluginPath, "skills");
34672
- if (existsSync24(skillsDir)) {
34673
- const entries2 = await readdir5(skillsDir, { withFileTypes: true });
34674
- return entries2.filter((e) => e.isDirectory()).map((e) => e.name);
34675
- }
34676
- const entries = await readdir5(pluginPath, { withFileTypes: true });
34677
- const flatSkills = [];
34678
- for (const entry of entries) {
34679
- if (!entry.isDirectory())
34680
- continue;
34681
- if (existsSync24(join25(pluginPath, entry.name, "SKILL.md"))) {
34682
- flatSkills.push(entry.name);
34683
- }
34684
- }
34685
- if (flatSkills.length > 0)
34686
- return flatSkills;
34687
- const rootSkillMd = join25(pluginPath, "SKILL.md");
34688
- if (existsSync24(rootSkillMd)) {
34689
- try {
34690
- const content = await readFile14(rootSkillMd, "utf-8");
34691
- const { parseSkillMetadata: parseSkillMetadata2 } = await Promise.resolve().then(() => (init_skill(), exports_skill));
34692
- const metadata = parseSkillMetadata2(content);
34693
- return [metadata?.name ?? basename7(pluginPath)];
34694
- } catch {
34695
- return [basename7(pluginPath)];
34696
- }
34697
- }
34698
- return [];
34699
- }
34700
- var init_skills = __esm(() => {
34701
- init_js_yaml();
34702
- init_constants();
34703
- init_workspace_config();
34704
- init_plugin();
34705
- init_plugin_path();
34706
- init_marketplace();
34707
- init_skill();
34708
- });
34709
-
34710
34848
  // src/core/skill-search.ts
34711
34849
  function validateSkillSearchArgs(query, options2) {
34712
34850
  if (query.trim().length < 2) {
@@ -34841,6 +34979,23 @@ async function runOneQuery(q3, page, limit, token, fetchFn) {
34841
34979
  truncated: Boolean(parsed.incomplete_results)
34842
34980
  };
34843
34981
  }
34982
+ async function fetchPrimaryPages(q3, page, limit, token, fetchFn) {
34983
+ const needed = page * limit * 3;
34984
+ const numPages = Math.min(Math.max(1, Math.ceil(needed / SEARCH_PAGE_SIZE)), MAX_RESULTS / SEARCH_PAGE_SIZE);
34985
+ const items = [];
34986
+ let total = 0;
34987
+ let truncated = false;
34988
+ for (let currentPage = 1;currentPage <= numPages; currentPage += 1) {
34989
+ const result = await runOneQuery(q3, currentPage, SEARCH_PAGE_SIZE, token, fetchFn);
34990
+ items.push(...result.items);
34991
+ total = result.total;
34992
+ truncated = truncated || result.truncated;
34993
+ if (result.items.length < SEARCH_PAGE_SIZE) {
34994
+ break;
34995
+ }
34996
+ }
34997
+ return { items, total, truncated };
34998
+ }
34844
34999
  async function searchSkills(query, options2 = {}, deps = {}) {
34845
35000
  validateSkillSearchArgs(query, options2);
34846
35001
  const fetchFn = deps.fetch ?? fetch;
@@ -34850,7 +35005,7 @@ async function searchSkills(query, options2 = {}, deps = {}) {
34850
35005
  const limit = options2.limit ?? 15;
34851
35006
  const token = await (deps.tokenResolver ?? resolveGhToken)();
34852
35007
  const queries = buildSearchQueries(query, options2.owner);
34853
- const settled = await Promise.allSettled(queries.map((entry) => runOneQuery(entry.q, page, limit, token, fetchFn)));
35008
+ const settled = await Promise.allSettled(queries.map((entry) => entry.priority === 4 ? fetchPrimaryPages(entry.q, page, limit, token, fetchFn) : runOneQuery(entry.q, 1, SEARCH_PAGE_SIZE, token, fetchFn)));
34854
35009
  const primaryIdx = queries.findIndex((q3) => q3.priority === 4);
34855
35010
  const primarySettled = settled[primaryIdx];
34856
35011
  if (primarySettled?.status === "rejected") {
@@ -34876,17 +35031,21 @@ async function searchSkills(query, options2 = {}, deps = {}) {
34876
35031
  const firstSegment = item.path.split("/")[0] ?? "";
34877
35032
  return !firstSegment.startsWith(".");
34878
35033
  });
35034
+ rankByRelevance(visible, query);
35035
+ const workingSet = truncateForProcessing(visible, page, limit);
34879
35036
  await Promise.all([
34880
- fetchStarsForItems(visible, token, fetchFn),
34881
- enrichDescriptionsForItems(visible, token, fetchFn)
35037
+ fetchStarsForItems(workingSet, token, fetchFn),
35038
+ enrichDescriptionsForItems(workingSet, token, fetchFn)
34882
35039
  ]);
34883
- visible.sort((a, b) => b.stars - a.stars);
34884
- const finalItems = visible.slice(0, limit);
35040
+ const filtered = filterByRelevance(workingSet, query);
35041
+ rankByRelevance(filtered, query);
35042
+ const dedupedByName = deduplicateByName(filtered);
35043
+ const { items: finalItems, totalPages } = paginate(dedupedByName, page, limit);
34885
35044
  return {
34886
35045
  query,
34887
35046
  items: finalItems,
34888
- total: finalItems.length,
34889
- truncated: buckets.some((b) => b.result.truncated) || visible.length > limit
35047
+ total: dedupedByName.length,
35048
+ truncated: buckets.some((b) => b.result.truncated) || totalPages > page
34890
35049
  };
34891
35050
  }
34892
35051
  function dedupeItems(items) {
@@ -34901,6 +35060,73 @@ function dedupeItems(items) {
34901
35060
  }
34902
35061
  return out;
34903
35062
  }
35063
+ function splitRepo(item) {
35064
+ const [owner = item.repo, repoName = ""] = item.repo.split("/", 2);
35065
+ return { owner, repoName };
35066
+ }
35067
+ function relevanceScore(item, query) {
35068
+ const term = query.trim().toLowerCase();
35069
+ const termHyphen = term.replace(/ /g, "-");
35070
+ const name = item.name.toLowerCase();
35071
+ const namespace = item.namespace.toLowerCase();
35072
+ const description = item.description.toLowerCase();
35073
+ let score = 0;
35074
+ if (name === term || name === termHyphen) {
35075
+ score += 3000;
35076
+ } else if (name.includes(term) || name.includes(termHyphen)) {
35077
+ score += 1000;
35078
+ }
35079
+ if (namespace?.includes(term)) {
35080
+ score += 500;
35081
+ }
35082
+ if (description.includes(term)) {
35083
+ score += 100;
35084
+ }
35085
+ if (item.stars > 0) {
35086
+ score += Math.floor(Math.sqrt(item.stars) * 30);
35087
+ }
35088
+ return score;
35089
+ }
35090
+ function rankByRelevance(items, query) {
35091
+ items.sort((a, b) => relevanceScore(b, query) - relevanceScore(a, query));
35092
+ }
35093
+ function filterByRelevance(items, query) {
35094
+ const term = query.trim().toLowerCase();
35095
+ const termHyphen = term.replace(/ /g, "-");
35096
+ return items.filter((item) => {
35097
+ const { owner, repoName } = splitRepo(item);
35098
+ return item.name.toLowerCase().includes(term) || item.name.toLowerCase().includes(termHyphen) || item.namespace.toLowerCase().includes(term) || item.description.toLowerCase().includes(term) || owner.toLowerCase().includes(term) || repoName.toLowerCase().includes(term);
35099
+ });
35100
+ }
35101
+ function truncateForProcessing(items, page, limit) {
35102
+ const maxToProcess = Math.max(page * limit * 3, limit * 3);
35103
+ return items.length > maxToProcess ? items.slice(0, maxToProcess) : items;
35104
+ }
35105
+ function deduplicateByName(items) {
35106
+ const maxPerName = 3;
35107
+ const counts = new Map;
35108
+ const out = [];
35109
+ for (const item of items) {
35110
+ const key = qualifiedName(item).toLowerCase();
35111
+ const count = counts.get(key) ?? 0;
35112
+ if (count >= maxPerName)
35113
+ continue;
35114
+ counts.set(key, count + 1);
35115
+ out.push(item);
35116
+ }
35117
+ return out;
35118
+ }
35119
+ function paginate(items, page, limit) {
35120
+ if (items.length === 0) {
35121
+ return { items: [], totalPages: 0 };
35122
+ }
35123
+ const totalPages = Math.ceil(items.length / limit);
35124
+ const start = (page - 1) * limit;
35125
+ return {
35126
+ items: items.slice(start, start + limit),
35127
+ totalPages
35128
+ };
35129
+ }
34904
35130
  async function fetchStarsForItems(items, token, fetchFn) {
34905
35131
  const uniqueRepos = [...new Set(items.map((i2) => i2.repo))];
34906
35132
  const headers = buildGitHubApiHeaders(token);
@@ -34982,7 +35208,7 @@ function decodeGitBlob(content, encoding) {
34982
35208
  return;
34983
35209
  return Buffer.from(content.replace(/\s+/g, ""), "base64").toString("utf8");
34984
35210
  }
34985
- var OWNER_REGEX, COULD_BE_OWNER_REGEX, ENRICHMENT_CONCURRENCY = 10, SkillSearchError;
35211
+ var OWNER_REGEX, SEARCH_PAGE_SIZE = 100, MAX_RESULTS = 1000, COULD_BE_OWNER_REGEX, ENRICHMENT_CONCURRENCY = 10, SkillSearchError;
34986
35212
  var init_skill_search = __esm(() => {
34987
35213
  init_skill();
34988
35214
  OWNER_REGEX = /^[A-Za-z0-9-]{1,39}$/;
@@ -41967,7 +42193,7 @@ var package_default;
41967
42193
  var init_package = __esm(() => {
41968
42194
  package_default = {
41969
42195
  name: "allagents",
41970
- version: "1.11.4-next.1",
42196
+ version: "1.11.6-next.1",
41971
42197
  packageManager: "bun@1.3.12",
41972
42198
  description: "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
41973
42199
  type: "module",
@@ -43664,8 +43890,8 @@ init_workspace();
43664
43890
  init_sync();
43665
43891
  init_status2();
43666
43892
  var import_cmd_ts2 = __toESM(require_cjs(), 1);
43667
- import { existsSync as existsSync23 } from "node:fs";
43668
- import { join as join24, resolve as resolve13 } from "node:path";
43893
+ import { existsSync as existsSync24 } from "node:fs";
43894
+ import { join as join25, resolve as resolve14 } from "node:path";
43669
43895
 
43670
43896
  // src/core/prune.ts
43671
43897
  init_js_yaml();
@@ -43673,9 +43899,9 @@ init_constants();
43673
43899
  init_marketplace();
43674
43900
  init_user_workspace();
43675
43901
  init_workspace_config();
43676
- import { readFile as readFile13, writeFile as writeFile9 } from "node:fs/promises";
43677
- import { existsSync as existsSync22 } from "node:fs";
43678
- import { join as join23 } from "node:path";
43902
+ import { readFile as readFile14, writeFile as writeFile9 } from "node:fs/promises";
43903
+ import { existsSync as existsSync23 } from "node:fs";
43904
+ import { join as join24 } from "node:path";
43679
43905
  async function isOrphanedPlugin(pluginSpec) {
43680
43906
  if (!isPluginSpec(pluginSpec))
43681
43907
  return false;
@@ -43702,9 +43928,9 @@ async function prunePlugins(plugins) {
43702
43928
  }
43703
43929
  async function pruneOrphanedPlugins(workspacePath) {
43704
43930
  let projectResult = { removed: [], kept: [], keptEntries: [] };
43705
- const projectConfigPath = join23(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
43706
- if (existsSync22(projectConfigPath) && !isUserConfigPath(workspacePath)) {
43707
- const content = await readFile13(projectConfigPath, "utf-8");
43931
+ const projectConfigPath = join24(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
43932
+ if (existsSync23(projectConfigPath) && !isUserConfigPath(workspacePath)) {
43933
+ const content = await readFile14(projectConfigPath, "utf-8");
43708
43934
  const config = load(content);
43709
43935
  projectResult = await prunePlugins(config.plugins);
43710
43936
  if (projectResult.removed.length > 0) {
@@ -44090,8 +44316,8 @@ var syncCmd = import_cmd_ts2.command({
44090
44316
  `);
44091
44317
  }
44092
44318
  const userConfigExists = !!await getUserWorkspaceConfig();
44093
- const projectConfigPath = join24(process.cwd(), ".allagents", "workspace.yaml");
44094
- const projectConfigExists = existsSync23(projectConfigPath);
44319
+ const projectConfigPath = join25(process.cwd(), ".allagents", "workspace.yaml");
44320
+ const projectConfigExists = existsSync24(projectConfigPath);
44095
44321
  if (!userConfigExists && !projectConfigExists) {
44096
44322
  await ensureUserWorkspace();
44097
44323
  if (isJsonMode()) {
@@ -44386,7 +44612,7 @@ var repoAddCmd = import_cmd_ts2.command({
44386
44612
  },
44387
44613
  handler: async ({ path: repoPath, description }) => {
44388
44614
  try {
44389
- const resolvedPath = resolve13(process.cwd(), repoPath);
44615
+ const resolvedPath = resolve14(process.cwd(), repoPath);
44390
44616
  const remote = await detectRemote(resolvedPath);
44391
44617
  const result = await addRepository(repoPath, {
44392
44618
  source: remote?.source,
@@ -44814,15 +45040,17 @@ var skillsRemoveMeta = {
44814
45040
  };
44815
45041
  var skillsSearchMeta = {
44816
45042
  command: "skill search",
44817
- description: "Search GitHub for skills by querying SKILL.md files via the Code Search API. Results are sorted by star count. In TTY mode, shows a filter-as-you-type multi-select picker and offers to install the selected skills.",
45043
+ description: "Search GitHub for skills by querying SKILL.md files via the Code Search API. Results are ranked by relevance, with skill-name matches first. In TTY mode, shows a filter-as-you-type multi-select picker and offers to install the selected skills.",
44818
45044
  whenToUse: 'To discover available skills from public GitHub repositories without leaving the CLI. Bridges "I want a skill that does X" → install.',
44819
45045
  examples: [
44820
45046
  "allagents skill search terraform",
45047
+ "allagents skill pr-search",
45048
+ 'allagents skill "pr search"',
44821
45049
  "allagents skill search terraform --owner hashicorp",
44822
45050
  "allagents skill search docs --page 2 --limit 10",
44823
45051
  "allagents --json skill search docs --limit 5"
44824
45052
  ],
44825
- expectedOutput: "Skills sorted by star count: repo, skill name, stars, description. In TTY mode, followed by a searchable multi-select install prompt.",
45053
+ expectedOutput: "Skills ranked by relevance: repo, skill name, stars, description. In TTY mode, followed by a searchable multi-select install prompt.",
44826
45054
  positionals: [
44827
45055
  { name: "query", type: "string", required: true, description: "Search query (≥2 characters)." }
44828
45056
  ],
@@ -44933,6 +45161,12 @@ async function recordSourceProvenance(opts) {
44933
45161
  ...pinnedRef && { pinnedRef }
44934
45162
  });
44935
45163
  }
45164
+ function resolveFetchedSourcePath(source, cachePath) {
45165
+ if (!isGitHubUrl(source))
45166
+ return cachePath;
45167
+ const parsed = parseGitHubUrl(source);
45168
+ return parsed?.subpath ? join26(cachePath, parsed.subpath) : cachePath;
45169
+ }
44936
45170
  function extractInlineRef(spec) {
44937
45171
  const slashIdx = spec.indexOf("/");
44938
45172
  if (slashIdx === -1)
@@ -45206,11 +45440,12 @@ async function installSkillFromSource(opts) {
45206
45440
  if (!fetchResult.success) {
45207
45441
  return { success: false, error: `Failed to fetch '${from}': ${fetchResult.error ?? "Unknown error"}` };
45208
45442
  }
45209
- const manifestResult = await parseMarketplaceManifest(fetchResult.cachePath);
45443
+ const sourcePath = resolveFetchedSourcePath(from, fetchResult.cachePath);
45444
+ const manifestResult = await parseMarketplaceManifest(sourcePath);
45210
45445
  if (manifestResult.success) {
45211
45446
  return installSkillViaMarketplace({ skill, from, isUser, workspacePath });
45212
45447
  }
45213
- return installSkillDirect({ skill, from, isUser, workspacePath, cachePath: fetchResult.cachePath });
45448
+ return installSkillDirect({ skill, from, isUser, workspacePath, cachePath: sourcePath });
45214
45449
  }
45215
45450
  async function installSkillViaMarketplace(opts) {
45216
45451
  const { skill, from, isUser, workspacePath } = opts;
@@ -45263,7 +45498,8 @@ Available skills: ${allAvailableSkills.join(", ") || "none"}`
45263
45498
  }
45264
45499
  async function installSkillDirect(opts) {
45265
45500
  const { skill, from, isUser, workspacePath, cachePath } = opts;
45266
- const availableSkills = await discoverSkillNames(cachePath);
45501
+ const sourcePath = resolveFetchedSourcePath(from, cachePath);
45502
+ const availableSkills = await discoverSkillNames(sourcePath);
45267
45503
  if (!availableSkills.includes(skill)) {
45268
45504
  return {
45269
45505
  success: false,
@@ -45274,6 +45510,25 @@ Available skills: ${availableSkills.join(", ") || "none"}
45274
45510
  Tip: run \`allagents skill list\` to see all installed skills.`
45275
45511
  };
45276
45512
  }
45513
+ if (isGitHubUrl(from)) {
45514
+ const existingEnabledSkills = await getEnabledSkillsForGitHubSource(from, workspacePath);
45515
+ const desiredSkills = [...existingEnabledSkills];
45516
+ if (!desiredSkills.includes(skill))
45517
+ desiredSkills.push(skill);
45518
+ const updateResult = isUser ? await upsertUserGitHubPluginSourceAllowlist(from, desiredSkills) : await upsertGitHubPluginSourceAllowlist(from, desiredSkills, workspacePath);
45519
+ if (!updateResult.success) {
45520
+ return {
45521
+ success: false,
45522
+ error: `Failed to update plugin '${from}': ${updateResult.error ?? "Unknown error"}`
45523
+ };
45524
+ }
45525
+ return finishSkillEnable({
45526
+ skill,
45527
+ pluginName: extractPrimaryPluginName(updateResult.normalizedPlugin ?? from),
45528
+ isUser,
45529
+ workspacePath
45530
+ });
45531
+ }
45277
45532
  const installResult = isUser ? await addUserPlugin(from) : await addPlugin(from, workspacePath);
45278
45533
  if (!installResult.success) {
45279
45534
  if (!installResult.error?.includes("already exists") && !installResult.error?.includes("duplicates existing")) {
@@ -45283,9 +45538,36 @@ Tip: run \`allagents skill list\` to see all installed skills.`
45283
45538
  console.log("Plugin already installed.");
45284
45539
  }
45285
45540
  }
45286
- const pluginName = getPluginName(cachePath);
45541
+ const pluginName = getPluginName(sourcePath);
45287
45542
  return applySkillAllowlist({ skill, pluginName, isUser, workspacePath });
45288
45543
  }
45544
+ function extractPrimaryPluginName(source) {
45545
+ const parsed = isGitHubUrl(source) ? parseGitHubUrl(source) : null;
45546
+ if (parsed?.subpath) {
45547
+ const segments = parsed.subpath.split("/").filter(Boolean);
45548
+ const leaf = segments[segments.length - 1];
45549
+ if (leaf)
45550
+ return leaf;
45551
+ }
45552
+ return getPluginName(source);
45553
+ }
45554
+ async function getEnabledSkillsForGitHubSource(source, workspacePath) {
45555
+ const identity = await resolveGitHubIdentity(source);
45556
+ if (!identity)
45557
+ return [];
45558
+ const enabledSkills = [];
45559
+ const allSkills = await getAllSkillsFromPlugins(workspacePath);
45560
+ for (const skill of allSkills) {
45561
+ if (skill.disabled)
45562
+ continue;
45563
+ const skillIdentity = await resolveGitHubIdentity(skill.pluginSource);
45564
+ if (skillIdentity !== identity)
45565
+ continue;
45566
+ if (!enabledSkills.includes(skill.name))
45567
+ enabledSkills.push(skill.name);
45568
+ }
45569
+ return enabledSkills;
45570
+ }
45289
45571
  async function applySkillAllowlist(opts) {
45290
45572
  const { skill, pluginName, isUser, workspacePath } = opts;
45291
45573
  const allSkills = await getAllSkillsFromPlugins(workspacePath);
@@ -45305,6 +45587,10 @@ async function applySkillAllowlist(opts) {
45305
45587
  return { success: false, error: `Failed to configure skill allowlist: ${setModeResult.error ?? "Unknown error"}` };
45306
45588
  }
45307
45589
  }
45590
+ return finishSkillEnable({ skill, pluginName, isUser, workspacePath });
45591
+ }
45592
+ async function finishSkillEnable(opts) {
45593
+ const { skill, pluginName, isUser, workspacePath } = opts;
45308
45594
  if (!isJsonMode()) {
45309
45595
  console.log(`✓ Enabled skill: ${skill} (${pluginName})`);
45310
45596
  }
@@ -45353,13 +45639,14 @@ async function discoverSkillsFromSource(from) {
45353
45639
  if (!fetchResult.success) {
45354
45640
  return { success: false, error: `Failed to fetch '${from}': ${fetchResult.error ?? "Unknown error"}` };
45355
45641
  }
45356
- const manifestResult = await parseMarketplaceManifest(fetchResult.cachePath);
45642
+ const sourcePath = resolveFetchedSourcePath(from, fetchResult.cachePath);
45643
+ const manifestResult = await parseMarketplaceManifest(sourcePath);
45357
45644
  if (manifestResult.success) {
45358
45645
  const all = [];
45359
45646
  for (const plugin of manifestResult.data.plugins) {
45360
45647
  if (typeof plugin.source === "object")
45361
45648
  continue;
45362
- const resolved = resolvePluginSourcePath(plugin.source, fetchResult.cachePath);
45649
+ const resolved = resolvePluginSourcePath(plugin.source, sourcePath);
45363
45650
  if (!existsSync25(resolved))
45364
45651
  continue;
45365
45652
  const skills2 = await discoverSkillsWithMetadata(resolved, plugin.name);
@@ -45367,7 +45654,7 @@ async function discoverSkillsFromSource(from) {
45367
45654
  }
45368
45655
  return { success: true, skills: all, isMarketplace: true };
45369
45656
  }
45370
- const skills = await discoverSkillsWithMetadata(fetchResult.cachePath);
45657
+ const skills = await discoverSkillsWithMetadata(sourcePath);
45371
45658
  return { success: true, skills, isMarketplace: false };
45372
45659
  }
45373
45660
  async function installAllSkillsFromSource(opts) {
@@ -45382,14 +45669,43 @@ async function installAllSkillsFromSource(opts) {
45382
45669
  if (!fetchResult.success) {
45383
45670
  return { success: false, error: `Failed to fetch '${from}': ${fetchResult.error ?? "Unknown error"}` };
45384
45671
  }
45385
- const manifestResult = await parseMarketplaceManifest(fetchResult.cachePath);
45672
+ const sourcePath = resolveFetchedSourcePath(from, fetchResult.cachePath);
45673
+ const manifestResult = await parseMarketplaceManifest(sourcePath);
45386
45674
  if (manifestResult.success) {
45387
45675
  return installAllViaMarketplace({ from, isUser, workspacePath, cachedPath: fetchResult.cachePath });
45388
45676
  }
45389
- const skillNames = await discoverSkillNames(fetchResult.cachePath);
45677
+ const skillNames = await discoverSkillNames(sourcePath);
45390
45678
  if (skillNames.length === 0) {
45391
45679
  return { success: false, error: `No skills found in '${from}'.` };
45392
45680
  }
45681
+ if (isGitHubUrl(from)) {
45682
+ const existingEnabledSkills = await getEnabledSkillsForGitHubSource(from, workspacePath);
45683
+ const desiredSkills = [...existingEnabledSkills];
45684
+ for (const skillName of skillNames) {
45685
+ if (!desiredSkills.includes(skillName))
45686
+ desiredSkills.push(skillName);
45687
+ }
45688
+ const updateResult = isUser ? await upsertUserGitHubPluginSourceAllowlist(from, desiredSkills) : await upsertGitHubPluginSourceAllowlist(from, desiredSkills, workspacePath);
45689
+ if (!updateResult.success) {
45690
+ return {
45691
+ success: false,
45692
+ error: `Failed to configure skill allowlist: ${updateResult.error ?? "Unknown error"}`
45693
+ };
45694
+ }
45695
+ const pluginName2 = extractPrimaryPluginName(updateResult.normalizedPlugin ?? from);
45696
+ if (!isJsonMode()) {
45697
+ console.log(`✓ Enabled ${skillNames.length} skill(s) from ${pluginName2}: ${skillNames.join(", ")}`);
45698
+ }
45699
+ const syncResult2 = isUser ? await syncUserWorkspace() : await syncWorkspace(workspacePath);
45700
+ if (!syncResult2.success) {
45701
+ return { success: false, error: "Sync failed" };
45702
+ }
45703
+ return {
45704
+ success: true,
45705
+ installed: [{ pluginName: pluginName2, skills: desiredSkills }],
45706
+ syncResult: syncResult2
45707
+ };
45708
+ }
45393
45709
  const installResult = isUser ? await addUserPlugin(from) : await addPlugin(from, workspacePath);
45394
45710
  if (!installResult.success) {
45395
45711
  if (!installResult.error?.includes("already exists") && !installResult.error?.includes("duplicates existing")) {
@@ -45399,7 +45715,7 @@ async function installAllSkillsFromSource(opts) {
45399
45715
  console.log("Plugin already installed.");
45400
45716
  }
45401
45717
  }
45402
- const pluginName = getPluginName(fetchResult.cachePath);
45718
+ const pluginName = getPluginName(sourcePath);
45403
45719
  const setModeResult = isUser ? await setUserPluginSkillsMode(pluginName, "allowlist", skillNames) : await setPluginSkillsMode(pluginName, "allowlist", skillNames, workspacePath);
45404
45720
  if (!setModeResult.success) {
45405
45721
  return { success: false, error: `Failed to configure skill allowlist: ${setModeResult.error ?? "Unknown error"}` };
@@ -45954,7 +46270,7 @@ var searchCmd = import_cmd_ts3.command({
45954
46270
  name: "search",
45955
46271
  description: buildDescription(skillsSearchMeta),
45956
46272
  args: {
45957
- query: import_cmd_ts3.positional({ type: import_cmd_ts3.string, displayName: "query" }),
46273
+ query: import_cmd_ts3.restPositionals({ type: import_cmd_ts3.string, displayName: "query" }),
45958
46274
  owner: import_cmd_ts3.option({
45959
46275
  type: import_cmd_ts3.optional(import_cmd_ts3.string),
45960
46276
  long: "owner",
@@ -45973,6 +46289,7 @@ var searchCmd = import_cmd_ts3.command({
45973
46289
  },
45974
46290
  handler: async ({ query, owner, page, limit }) => {
45975
46291
  try {
46292
+ const searchQuery = query.join(" ").trim();
45976
46293
  const opts = {};
45977
46294
  if (owner)
45978
46295
  opts.owner = owner;
@@ -46002,7 +46319,7 @@ var searchCmd = import_cmd_ts3.command({
46002
46319
  }
46003
46320
  opts.limit = n;
46004
46321
  }
46005
- const result = await searchSkills(query, opts);
46322
+ const result = await searchSkills(searchQuery, opts);
46006
46323
  if (isJsonMode()) {
46007
46324
  jsonOutput({
46008
46325
  success: true,
@@ -46012,16 +46329,16 @@ var searchCmd = import_cmd_ts3.command({
46012
46329
  return;
46013
46330
  }
46014
46331
  if (result.items.length === 0) {
46015
- console.log(`No skills found for "${query}".`);
46332
+ console.log(`No skills found for "${searchQuery}".`);
46016
46333
  return;
46017
46334
  }
46018
46335
  const isTTY = process.stdout.isTTY && process.stdin.isTTY;
46019
46336
  if (!isTTY) {
46020
- printSearchResults(result.items, query, result.truncated);
46337
+ printSearchResults(result.items, searchQuery, result.truncated);
46021
46338
  return;
46022
46339
  }
46023
46340
  const { autocompleteMultiselect: autocompleteMultiselect2, isCancel, log } = await Promise.resolve().then(() => (init_dist2(), exports_dist));
46024
- log.success(formatSkillSearchSummary(result.items.length, query, result.truncated));
46341
+ log.success(formatSkillSearchSummary(result.items.length, searchQuery, result.truncated));
46025
46342
  const options2 = result.items.map((item) => ({
46026
46343
  label: `${qualifiedName(item)} ${source_default.dim(item.repo)}`,
46027
46344
  value: item.path,
@@ -58135,6 +58452,44 @@ var selfCmd = conciseSubcommands({
58135
58452
  }
58136
58453
  });
58137
58454
 
58455
+ // src/cli/skill-arg-normalizer.ts
58456
+ var SKILL_SUBCOMMANDS = new Set(["list", "remove", "add", "search"]);
58457
+ function normalizeSkillAlias(args) {
58458
+ if (args.length === 0)
58459
+ return args;
58460
+ return args[0] === "skills" ? ["skill", ...args.slice(1)] : args;
58461
+ }
58462
+ function shouldUseSkillSearchShorthand(rest) {
58463
+ const first2 = rest[0] ?? "";
58464
+ if (rest.length === 0)
58465
+ return false;
58466
+ if (first2.startsWith("-") || SKILL_SUBCOMMANDS.has(first2))
58467
+ return false;
58468
+ return rest.length > 1 || /[-\s]/.test(first2);
58469
+ }
58470
+ function normalizeSkillHelpArgs(args) {
58471
+ const normalized = normalizeSkillAlias(args);
58472
+ if (normalized.length === 0 || normalized[0] !== "skill") {
58473
+ return normalized;
58474
+ }
58475
+ const [, ...rest] = normalized;
58476
+ return shouldUseSkillSearchShorthand(rest) ? ["skill", "search"] : normalized;
58477
+ }
58478
+ function normalizeSkillArgs(args) {
58479
+ const normalized = normalizeSkillAlias(args);
58480
+ if (normalized.length === 0 || normalized[0] !== "skill") {
58481
+ return args;
58482
+ }
58483
+ const [, ...rest] = normalized;
58484
+ if (rest.length === 0) {
58485
+ return ["skill"];
58486
+ }
58487
+ if (!shouldUseSkillSearchShorthand(rest)) {
58488
+ return ["skill", ...rest];
58489
+ }
58490
+ return ["skill", "search", ...rest];
58491
+ }
58492
+
58138
58493
  // src/cli/agent-help.ts
58139
58494
  var allCommands = [
58140
58495
  initMeta,
@@ -58189,7 +58544,7 @@ function findMetaByCommand(commandPath) {
58189
58544
  }
58190
58545
  function printAgentHelp(args, version2) {
58191
58546
  const positional5 = args.filter((a) => !a.startsWith("-"));
58192
- const normalized = positional5[0] === "skills" ? ["skill", ...positional5.slice(1)] : positional5;
58547
+ const normalized = normalizeSkillHelpArgs(positional5);
58193
58548
  const commandPath = normalized.join(" ");
58194
58549
  if (!commandPath) {
58195
58550
  const tree = {
@@ -58242,8 +58597,8 @@ var app = conciseSubcommands({
58242
58597
  var rawArgs = process.argv.slice(2);
58243
58598
  var { args: argsNoJson, json: json2, jsonFields: jsonFields2 } = extractJsonFlag(rawArgs);
58244
58599
  var { args: argsNoJq, jqExpr: jqExpr2 } = extractJqFlag(argsNoJson);
58245
- var { args: argsWithSkillAlias, agentHelp } = extractAgentHelpFlag(argsNoJq);
58246
- var finalArgs = argsWithSkillAlias[0] === "skills" ? ["skill", ...argsWithSkillAlias.slice(1)] : argsWithSkillAlias;
58600
+ var { args: argsAfterAgentHelp, agentHelp } = extractAgentHelpFlag(argsNoJq);
58601
+ var finalArgs = normalizeSkillArgs(argsAfterAgentHelp);
58247
58602
  if (jqExpr2 && !json2) {
58248
58603
  process.stderr.write(`Error: --jq requires --json.
58249
58604
  `);
@@ -58269,7 +58624,7 @@ if (!agentHelp && !json2 && !isWizard) {
58269
58624
  `);
58270
58625
  }
58271
58626
  if (agentHelp) {
58272
- printAgentHelp(finalArgs, package_default.version);
58627
+ printAgentHelp(normalizeSkillHelpArgs(argsAfterAgentHelp), package_default.version);
58273
58628
  } else if (isWizard) {
58274
58629
  const { runWizard: runWizard2 } = await Promise.resolve().then(() => (init_wizard(), exports_wizard));
58275
58630
  await runWizard2();