allagents 0.17.2 → 0.20.0

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 +1073 -232
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11382,7 +11382,7 @@ var init_zod = __esm(() => {
11382
11382
  });
11383
11383
 
11384
11384
  // src/models/workspace-config.ts
11385
- var RepositorySchema, WorkspaceFileSchema, WorkspaceSchema, PluginSourceSchema, ClientTypeSchema, VscodeConfigSchema, WorkspaceConfigSchema;
11385
+ var RepositorySchema, WorkspaceFileSchema, WorkspaceSchema, PluginSourceSchema, ClientTypeSchema, VscodeConfigSchema, SyncModeSchema, WorkspaceConfigSchema;
11386
11386
  var init_workspace_config = __esm(() => {
11387
11387
  init_zod();
11388
11388
  RepositorySchema = exports_external.object({
@@ -11417,12 +11417,15 @@ var init_workspace_config = __esm(() => {
11417
11417
  VscodeConfigSchema = exports_external.object({
11418
11418
  output: exports_external.string().optional()
11419
11419
  });
11420
+ SyncModeSchema = exports_external.enum(["symlink", "copy"]);
11420
11421
  WorkspaceConfigSchema = exports_external.object({
11421
11422
  workspace: WorkspaceSchema.optional(),
11422
11423
  repositories: exports_external.array(RepositorySchema),
11423
11424
  plugins: exports_external.array(PluginSourceSchema),
11424
11425
  clients: exports_external.array(ClientTypeSchema),
11425
- vscode: VscodeConfigSchema.optional()
11426
+ vscode: VscodeConfigSchema.optional(),
11427
+ syncMode: SyncModeSchema.optional(),
11428
+ disabledSkills: exports_external.array(exports_external.string()).optional()
11426
11429
  });
11427
11430
  });
11428
11431
 
@@ -21223,7 +21226,10 @@ var init_glob_patterns = __esm(() => {
21223
21226
  });
21224
21227
 
21225
21228
  // src/models/client-mapping.ts
21226
- var CLIENT_MAPPINGS, USER_CLIENT_MAPPINGS;
21229
+ function isUniversalClient(client) {
21230
+ return CLIENT_MAPPINGS[client].skillsPath === CANONICAL_SKILLS_PATH;
21231
+ }
21232
+ var CLIENT_MAPPINGS, CANONICAL_SKILLS_PATH = ".agents/skills/", USER_CLIENT_MAPPINGS;
21227
21233
  var init_client_mapping = __esm(() => {
21228
21234
  CLIENT_MAPPINGS = {
21229
21235
  claude: {
@@ -24858,10 +24864,72 @@ var init_skill = __esm(() => {
24858
24864
  import_gray_matter = __toESM(require_gray_matter(), 1);
24859
24865
  });
24860
24866
 
24867
+ // src/utils/symlink.ts
24868
+ import { symlink, lstat, readlink, rm as rm2, mkdir as mkdir2 } from "node:fs/promises";
24869
+ import { dirname as dirname2, relative, resolve as resolve4 } from "node:path";
24870
+ import { platform } from "node:os";
24871
+ import { realpath } from "node:fs/promises";
24872
+ async function resolveParentSymlinks(path) {
24873
+ const resolved = resolve4(path);
24874
+ const dir = dirname2(resolved);
24875
+ const base = resolved.substring(dir.length + 1);
24876
+ try {
24877
+ const realDir = await realpath(dir);
24878
+ return `${realDir}/${base}`;
24879
+ } catch {
24880
+ return resolved;
24881
+ }
24882
+ }
24883
+ function resolveSymlinkTarget(linkPath, linkTarget) {
24884
+ return resolve4(dirname2(linkPath), linkTarget);
24885
+ }
24886
+ async function createSymlink(target, linkPath) {
24887
+ try {
24888
+ const resolvedTarget = resolve4(target);
24889
+ const resolvedLinkPath = resolve4(linkPath);
24890
+ if (resolvedTarget === resolvedLinkPath) {
24891
+ return true;
24892
+ }
24893
+ const realTarget = await resolveParentSymlinks(target);
24894
+ const realLinkPath = await resolveParentSymlinks(linkPath);
24895
+ if (realTarget === realLinkPath) {
24896
+ return true;
24897
+ }
24898
+ try {
24899
+ const stats = await lstat(linkPath);
24900
+ if (stats.isSymbolicLink()) {
24901
+ const existingTarget = await readlink(linkPath);
24902
+ if (resolveSymlinkTarget(linkPath, existingTarget) === resolvedTarget) {
24903
+ return true;
24904
+ }
24905
+ await rm2(linkPath);
24906
+ } else {
24907
+ await rm2(linkPath, { recursive: true });
24908
+ }
24909
+ } catch (err) {
24910
+ if (err && typeof err === "object" && "code" in err && err.code === "ELOOP") {
24911
+ try {
24912
+ await rm2(linkPath, { force: true });
24913
+ } catch {}
24914
+ }
24915
+ }
24916
+ const linkDir = dirname2(linkPath);
24917
+ await mkdir2(linkDir, { recursive: true });
24918
+ const realLinkDir = await resolveParentSymlinks(linkDir);
24919
+ const relativePath = relative(realLinkDir, target);
24920
+ const symlinkType = platform() === "win32" ? "junction" : undefined;
24921
+ await symlink(relativePath, linkPath, symlinkType);
24922
+ return true;
24923
+ } catch {
24924
+ return false;
24925
+ }
24926
+ }
24927
+ var init_symlink = () => {};
24928
+
24861
24929
  // src/core/transform.ts
24862
- import { readFile as readFile4, writeFile, mkdir as mkdir2, cp, readdir as readdir2 } from "node:fs/promises";
24930
+ import { readFile as readFile4, writeFile, mkdir as mkdir3, cp, readdir as readdir2 } from "node:fs/promises";
24863
24931
  import { existsSync as existsSync4 } from "node:fs";
24864
- import { join as join6, dirname as dirname2 } from "node:path";
24932
+ import { join as join6, dirname as dirname3 } from "node:path";
24865
24933
  async function ensureWorkspaceRules(filePath, rules) {
24866
24934
  const rulesContent = rules ?? WORKSPACE_RULES;
24867
24935
  const startMarker = "<!-- WORKSPACE-RULES:START -->";
@@ -24898,7 +24966,7 @@ async function copyCommands(pluginPath, workspacePath, client, options2 = {}) {
24898
24966
  }
24899
24967
  const destDir = join6(workspacePath, mapping.commandsPath);
24900
24968
  if (!dryRun) {
24901
- await mkdir2(destDir, { recursive: true });
24969
+ await mkdir3(destDir, { recursive: true });
24902
24970
  }
24903
24971
  const files = await readdir2(sourceDir);
24904
24972
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -24924,7 +24992,7 @@ async function copyCommands(pluginPath, workspacePath, client, options2 = {}) {
24924
24992
  return Promise.all(copyPromises);
24925
24993
  }
24926
24994
  async function copySkills(pluginPath, workspacePath, client, options2 = {}) {
24927
- const { dryRun = false, skillNameMap } = options2;
24995
+ const { dryRun = false, skillNameMap, syncMode = "copy", canonicalSkillsPath } = options2;
24928
24996
  const mapping = getMapping(client, options2);
24929
24997
  const results = [];
24930
24998
  if (!mapping.skillsPath) {
@@ -24936,10 +25004,14 @@ async function copySkills(pluginPath, workspacePath, client, options2 = {}) {
24936
25004
  }
24937
25005
  const destDir = join6(workspacePath, mapping.skillsPath);
24938
25006
  if (!dryRun) {
24939
- await mkdir2(destDir, { recursive: true });
25007
+ await mkdir3(destDir, { recursive: true });
24940
25008
  }
24941
25009
  const entries = await readdir2(sourceDir, { withFileTypes: true });
24942
- const skillDirs = entries.filter((e) => e.isDirectory());
25010
+ let skillDirs = entries.filter((e) => e.isDirectory());
25011
+ if (skillNameMap) {
25012
+ skillDirs = skillDirs.filter((e) => skillNameMap.has(e.name));
25013
+ }
25014
+ const useSymlinks = syncMode === "symlink" && !isUniversalClient(client) && canonicalSkillsPath;
24943
25015
  const copyPromises = skillDirs.map(async (entry) => {
24944
25016
  const skillSourcePath = join6(sourceDir, entry.name);
24945
25017
  const resolvedName = skillNameMap?.get(entry.name) ?? entry.name;
@@ -24960,6 +25032,17 @@ async function copySkills(pluginPath, workspacePath, client, options2 = {}) {
24960
25032
  action: "copied"
24961
25033
  };
24962
25034
  }
25035
+ if (useSymlinks) {
25036
+ const canonicalSkillPath = join6(workspacePath, canonicalSkillsPath, resolvedName);
25037
+ const symlinkCreated = await createSymlink(canonicalSkillPath, skillDestPath);
25038
+ if (symlinkCreated) {
25039
+ return {
25040
+ source: canonicalSkillPath,
25041
+ destination: skillDestPath,
25042
+ action: "copied"
25043
+ };
25044
+ }
25045
+ }
24963
25046
  try {
24964
25047
  await cp(skillSourcePath, skillDestPath, { recursive: true });
24965
25048
  return {
@@ -24978,14 +25061,15 @@ async function copySkills(pluginPath, workspacePath, client, options2 = {}) {
24978
25061
  });
24979
25062
  return Promise.all(copyPromises);
24980
25063
  }
24981
- async function collectPluginSkills(pluginPath, pluginSource) {
25064
+ async function collectPluginSkills(pluginPath, pluginSource, disabledSkills, pluginName) {
24982
25065
  const skillsDir = join6(pluginPath, "skills");
24983
25066
  if (!existsSync4(skillsDir)) {
24984
25067
  return [];
24985
25068
  }
24986
25069
  const entries = await readdir2(skillsDir, { withFileTypes: true });
24987
25070
  const skillDirs = entries.filter((e) => e.isDirectory());
24988
- return skillDirs.map((entry) => ({
25071
+ const filteredDirs = disabledSkills && pluginName ? skillDirs.filter((e) => !disabledSkills.has(`${pluginName}:${e.name}`)) : skillDirs;
25072
+ return filteredDirs.map((entry) => ({
24989
25073
  folderName: entry.name,
24990
25074
  skillPath: join6(skillsDir, entry.name),
24991
25075
  pluginPath,
@@ -25008,7 +25092,7 @@ async function copyHooks(pluginPath, workspacePath, client, options2 = {}) {
25008
25092
  results.push({ source: sourceDir, destination: destDir, action: "copied" });
25009
25093
  return results;
25010
25094
  }
25011
- await mkdir2(destDir, { recursive: true });
25095
+ await mkdir3(destDir, { recursive: true });
25012
25096
  try {
25013
25097
  await cp(sourceDir, destDir, { recursive: true });
25014
25098
  results.push({ source: sourceDir, destination: destDir, action: "copied" });
@@ -25035,7 +25119,7 @@ async function copyAgents(pluginPath, workspacePath, client, options2 = {}) {
25035
25119
  }
25036
25120
  const destDir = join6(workspacePath, mapping.agentsPath);
25037
25121
  if (!dryRun) {
25038
- await mkdir2(destDir, { recursive: true });
25122
+ await mkdir3(destDir, { recursive: true });
25039
25123
  }
25040
25124
  const files = await readdir2(sourceDir);
25041
25125
  const mdFiles = files.filter((f) => f.endsWith(".md"));
@@ -25061,10 +25145,15 @@ async function copyAgents(pluginPath, workspacePath, client, options2 = {}) {
25061
25145
  return Promise.all(copyPromises);
25062
25146
  }
25063
25147
  async function copyPluginToWorkspace(pluginPath, workspacePath, client, options2 = {}) {
25064
- const { skillNameMap, ...baseOptions } = options2;
25148
+ const { skillNameMap, syncMode, canonicalSkillsPath, ...baseOptions } = options2;
25065
25149
  const [commandResults, skillResults, hookResults, agentResults] = await Promise.all([
25066
25150
  copyCommands(pluginPath, workspacePath, client, baseOptions),
25067
- copySkills(pluginPath, workspacePath, client, { ...baseOptions, ...skillNameMap && { skillNameMap } }),
25151
+ copySkills(pluginPath, workspacePath, client, {
25152
+ ...baseOptions,
25153
+ ...skillNameMap && { skillNameMap },
25154
+ ...syncMode && { syncMode },
25155
+ ...canonicalSkillsPath && { canonicalSkillsPath }
25156
+ }),
25068
25157
  copyHooks(pluginPath, workspacePath, client, baseOptions),
25069
25158
  copyAgents(pluginPath, workspacePath, client, baseOptions)
25070
25159
  ]);
@@ -25181,7 +25270,7 @@ async function copyWorkspaceFiles(sourcePath, workspacePath, files, options2 = {
25181
25270
  continue;
25182
25271
  }
25183
25272
  try {
25184
- await mkdir2(dirname2(destPath), { recursive: true });
25273
+ await mkdir3(dirname3(destPath), { recursive: true });
25185
25274
  const content = await readFile4(resolved.sourcePath, "utf-8");
25186
25275
  await writeFile(destPath, content, "utf-8");
25187
25276
  results.push({ source: resolved.sourcePath, destination: destPath, action: "copied" });
@@ -25252,7 +25341,7 @@ async function copyWorkspaceFiles(sourcePath, workspacePath, files, options2 = {
25252
25341
  continue;
25253
25342
  }
25254
25343
  try {
25255
- await mkdir2(dirname2(destPath), { recursive: true });
25344
+ await mkdir3(dirname3(destPath), { recursive: true });
25256
25345
  const content = await readFile4(srcPath, "utf-8");
25257
25346
  await writeFile(destPath, content, "utf-8");
25258
25347
  results.push({ source: srcPath, destination: destPath, action: "copied" });
@@ -25292,6 +25381,7 @@ var init_transform = __esm(() => {
25292
25381
  init_skill();
25293
25382
  init_constants();
25294
25383
  init_plugin_path();
25384
+ init_symlink();
25295
25385
  AGENT_FILES2 = ["AGENTS.md", "CLAUDE.md"];
25296
25386
  injectWorkspaceRules = ensureWorkspaceRules;
25297
25387
  });
@@ -25351,7 +25441,7 @@ var init_marketplace_manifest = __esm(() => {
25351
25441
  // src/utils/marketplace-manifest-parser.ts
25352
25442
  import { readFile as readFile5 } from "node:fs/promises";
25353
25443
  import { existsSync as existsSync5 } from "node:fs";
25354
- import { join as join7, resolve as resolve4 } from "node:path";
25444
+ import { join as join7, resolve as resolve5 } from "node:path";
25355
25445
  async function parseMarketplaceManifest(marketplacePath) {
25356
25446
  const manifestPath = join7(marketplacePath, MANIFEST_PATH);
25357
25447
  if (!existsSync5(manifestPath)) {
@@ -25455,7 +25545,7 @@ function resolvePluginSourcePath(source, marketplacePath) {
25455
25545
  if (typeof source === "object") {
25456
25546
  return source.url;
25457
25547
  }
25458
- return resolve4(marketplacePath, source);
25548
+ return resolve5(marketplacePath, source);
25459
25549
  }
25460
25550
  var MANIFEST_PATH = ".claude-plugin/marketplace.json";
25461
25551
  var init_marketplace_manifest_parser = __esm(() => {
@@ -25468,24 +25558,29 @@ __export(exports_user_workspace, {
25468
25558
  resolveGitHubIdentity: () => resolveGitHubIdentity,
25469
25559
  removeUserPluginsForMarketplace: () => removeUserPluginsForMarketplace,
25470
25560
  removeUserPlugin: () => removeUserPlugin,
25561
+ removeUserDisabledSkill: () => removeUserDisabledSkill,
25471
25562
  isUserConfigPath: () => isUserConfigPath,
25472
25563
  hasUserPlugin: () => hasUserPlugin,
25473
25564
  getUserWorkspaceConfigPath: () => getUserWorkspaceConfigPath,
25474
25565
  getUserWorkspaceConfig: () => getUserWorkspaceConfig,
25475
25566
  getUserPluginsForMarketplace: () => getUserPluginsForMarketplace,
25567
+ getUserDisabledSkills: () => getUserDisabledSkills,
25568
+ getInstalledUserPlugins: () => getInstalledUserPlugins,
25569
+ getInstalledProjectPlugins: () => getInstalledProjectPlugins,
25476
25570
  ensureUserWorkspace: () => ensureUserWorkspace,
25477
- addUserPlugin: () => addUserPlugin
25571
+ addUserPlugin: () => addUserPlugin,
25572
+ addUserDisabledSkill: () => addUserDisabledSkill
25478
25573
  });
25479
- import { readFile as readFile6, writeFile as writeFile2, mkdir as mkdir3 } from "node:fs/promises";
25574
+ import { readFile as readFile6, writeFile as writeFile2, mkdir as mkdir4 } from "node:fs/promises";
25480
25575
  import { existsSync as existsSync6 } from "node:fs";
25481
- import { join as join8, resolve as resolve5 } from "node:path";
25576
+ import { join as join8, resolve as resolve6 } from "node:path";
25482
25577
  function getUserWorkspaceConfigPath() {
25483
25578
  return join8(getAllagentsDir(), WORKSPACE_CONFIG_FILE);
25484
25579
  }
25485
25580
  function isUserConfigPath(workspacePath) {
25486
25581
  const projectConfigPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
25487
25582
  const userConfigPath = getUserWorkspaceConfigPath();
25488
- return resolve5(projectConfigPath) === resolve5(userConfigPath);
25583
+ return resolve6(projectConfigPath) === resolve6(userConfigPath);
25489
25584
  }
25490
25585
  async function ensureUserWorkspace() {
25491
25586
  const configPath = getUserWorkspaceConfigPath();
@@ -25494,9 +25589,9 @@ async function ensureUserWorkspace() {
25494
25589
  const defaultConfig = {
25495
25590
  repositories: [],
25496
25591
  plugins: [],
25497
- clients: [...ALL_CLIENTS]
25592
+ clients: [...DEFAULT_USER_CLIENTS]
25498
25593
  };
25499
- await mkdir3(getAllagentsDir(), { recursive: true });
25594
+ await mkdir4(getAllagentsDir(), { recursive: true });
25500
25595
  await writeFile2(configPath, dump(defaultConfig, { lineWidth: -1 }), "utf-8");
25501
25596
  }
25502
25597
  async function getUserWorkspaceConfig() {
@@ -25662,7 +25757,98 @@ async function addPluginToUserConfig(plugin, configPath, autoRegistered) {
25662
25757
  };
25663
25758
  }
25664
25759
  }
25665
- var ALL_CLIENTS;
25760
+ async function getUserDisabledSkills() {
25761
+ const config = await getUserWorkspaceConfig();
25762
+ return config?.disabledSkills ?? [];
25763
+ }
25764
+ async function addUserDisabledSkill(skillKey) {
25765
+ await ensureUserWorkspace();
25766
+ const configPath = getUserWorkspaceConfigPath();
25767
+ try {
25768
+ const content = await readFile6(configPath, "utf-8");
25769
+ const config = load(content);
25770
+ const disabledSkills = config.disabledSkills ?? [];
25771
+ if (disabledSkills.includes(skillKey)) {
25772
+ return { success: false, error: `Skill '${skillKey}' is already disabled` };
25773
+ }
25774
+ config.disabledSkills = [...disabledSkills, skillKey];
25775
+ await writeFile2(configPath, dump(config, { lineWidth: -1 }), "utf-8");
25776
+ return { success: true };
25777
+ } catch (error) {
25778
+ return {
25779
+ success: false,
25780
+ error: error instanceof Error ? error.message : String(error)
25781
+ };
25782
+ }
25783
+ }
25784
+ async function removeUserDisabledSkill(skillKey) {
25785
+ await ensureUserWorkspace();
25786
+ const configPath = getUserWorkspaceConfigPath();
25787
+ try {
25788
+ const content = await readFile6(configPath, "utf-8");
25789
+ const config = load(content);
25790
+ const disabledSkills = config.disabledSkills ?? [];
25791
+ if (!disabledSkills.includes(skillKey)) {
25792
+ return { success: false, error: `Skill '${skillKey}' is already enabled` };
25793
+ }
25794
+ config.disabledSkills = disabledSkills.filter((s) => s !== skillKey);
25795
+ if (config.disabledSkills.length === 0) {
25796
+ config.disabledSkills = undefined;
25797
+ }
25798
+ await writeFile2(configPath, dump(config, { lineWidth: -1 }), "utf-8");
25799
+ return { success: true };
25800
+ } catch (error) {
25801
+ return {
25802
+ success: false,
25803
+ error: error instanceof Error ? error.message : String(error)
25804
+ };
25805
+ }
25806
+ }
25807
+ async function getInstalledUserPlugins() {
25808
+ const config = await getUserWorkspaceConfig();
25809
+ if (!config)
25810
+ return [];
25811
+ const result = [];
25812
+ for (const plugin of config.plugins) {
25813
+ const parsed = parsePluginSpec(plugin);
25814
+ if (parsed) {
25815
+ result.push({
25816
+ spec: plugin,
25817
+ name: parsed.plugin,
25818
+ marketplace: parsed.marketplaceName,
25819
+ scope: "user"
25820
+ });
25821
+ }
25822
+ }
25823
+ return result;
25824
+ }
25825
+ async function getInstalledProjectPlugins(workspacePath) {
25826
+ const configPath = join8(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
25827
+ if (!existsSync6(configPath))
25828
+ return [];
25829
+ try {
25830
+ const content = await readFile6(configPath, "utf-8");
25831
+ const config = load(content);
25832
+ if (!config?.plugins)
25833
+ return [];
25834
+ const result = [];
25835
+ for (const plugin of config.plugins) {
25836
+ const parsed = parsePluginSpec(plugin);
25837
+ if (parsed) {
25838
+ result.push({
25839
+ spec: plugin,
25840
+ name: parsed.plugin,
25841
+ marketplace: parsed.marketplaceName,
25842
+ scope: "project"
25843
+ });
25844
+ }
25845
+ }
25846
+ return result;
25847
+ } catch {
25848
+ return [];
25849
+ }
25850
+ }
25851
+ var DEFAULT_USER_CLIENTS;
25666
25852
  var init_user_workspace = __esm(() => {
25667
25853
  init_js_yaml();
25668
25854
  init_constants();
@@ -25670,8 +25856,7 @@ var init_user_workspace = __esm(() => {
25670
25856
  init_marketplace();
25671
25857
  init_plugin_path();
25672
25858
  init_marketplace_manifest_parser();
25673
- ALL_CLIENTS = [
25674
- "claude",
25859
+ DEFAULT_USER_CLIENTS = [
25675
25860
  "copilot",
25676
25861
  "codex",
25677
25862
  "cursor",
@@ -25683,16 +25868,16 @@ var init_user_workspace = __esm(() => {
25683
25868
  });
25684
25869
 
25685
25870
  // src/core/marketplace.ts
25686
- import { mkdir as mkdir4, readFile as readFile7, readdir as readdir3, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
25871
+ import { mkdir as mkdir5, readFile as readFile7, readdir as readdir3, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
25687
25872
  import { existsSync as existsSync7 } from "node:fs";
25688
- import { basename as basename2, join as join9, resolve as resolve6 } from "node:path";
25873
+ import { basename as basename2, join as join9, resolve as resolve7 } from "node:path";
25689
25874
  function parseLocation(location) {
25690
25875
  const [owner = "", repo = "", ...rest] = location.split("/");
25691
25876
  const branch = rest.length > 0 ? rest.join("/") : undefined;
25692
25877
  return { owner, repo, ...branch !== undefined && { branch } };
25693
25878
  }
25694
25879
  function getAllagentsDir() {
25695
- return resolve6(getHomeDir(), ".allagents");
25880
+ return resolve7(getHomeDir(), ".allagents");
25696
25881
  }
25697
25882
  function getMarketplacesDir() {
25698
25883
  return join9(getAllagentsDir(), "plugins", "marketplaces");
@@ -25716,19 +25901,27 @@ async function saveRegistry(registry) {
25716
25901
  const registryPath = getRegistryPath();
25717
25902
  const dir = getAllagentsDir();
25718
25903
  if (!existsSync7(dir)) {
25719
- await mkdir4(dir, { recursive: true });
25904
+ await mkdir5(dir, { recursive: true });
25720
25905
  }
25721
25906
  await writeFile3(registryPath, `${JSON.stringify(registry, null, 2)}
25722
25907
  `);
25723
25908
  }
25724
- function parseMarketplaceSource(source) {
25725
- if (WELL_KNOWN_MARKETPLACES[source]) {
25726
- return {
25727
- type: "github",
25728
- location: WELL_KNOWN_MARKETPLACES[source],
25729
- name: source
25730
- };
25909
+ function getSourceLocationKey(source) {
25910
+ if (source.type === "github") {
25911
+ const { owner, repo } = parseLocation(source.location);
25912
+ return `${owner}/${repo}`;
25913
+ }
25914
+ return source.location;
25915
+ }
25916
+ function findBySourceLocation(registry, sourceLocation) {
25917
+ for (const entry of Object.values(registry.marketplaces)) {
25918
+ if (getSourceLocationKey(entry.source) === sourceLocation) {
25919
+ return entry;
25920
+ }
25731
25921
  }
25922
+ return null;
25923
+ }
25924
+ function parseMarketplaceSource(source) {
25732
25925
  if (source.startsWith("https://github.com/")) {
25733
25926
  const match = source.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/tree\/(.+))?$/);
25734
25927
  if (match) {
@@ -25746,7 +25939,7 @@ function parseMarketplaceSource(source) {
25746
25939
  return null;
25747
25940
  }
25748
25941
  if (source.startsWith("/") || source.startsWith(".")) {
25749
- const absPath = resolve6(source);
25942
+ const absPath = resolve7(source);
25750
25943
  const name = basename2(absPath) || "local";
25751
25944
  return {
25752
25945
  type: "local",
@@ -25772,7 +25965,7 @@ async function addMarketplace(source, customName, branch) {
25772
25965
  return {
25773
25966
  success: false,
25774
25967
  error: `Invalid marketplace source: ${source}
25775
- Use: GitHub URL, owner/repo, local path, or well-known name`
25968
+ Use: GitHub URL, owner/repo, or local path`
25776
25969
  };
25777
25970
  }
25778
25971
  const effectiveBranch = branch || parsed.branch;
@@ -25800,13 +25993,18 @@ async function addMarketplace(source, customName, branch) {
25800
25993
  error: `Marketplace '${name}' already exists. Use 'update' to refresh it.`
25801
25994
  };
25802
25995
  }
25996
+ const sourceLocation = parsed.type === "github" ? `${parseLocation(parsed.location).owner}/${parseLocation(parsed.location).repo}` : parsed.location;
25997
+ const existingBySource = findBySourceLocation(registry, sourceLocation);
25998
+ if (existingBySource) {
25999
+ return { success: true, marketplace: existingBySource };
26000
+ }
25803
26001
  let marketplacePath;
25804
26002
  if (parsed.type === "github") {
25805
26003
  marketplacePath = join9(getMarketplacesDir(), name);
25806
26004
  if (existsSync7(marketplacePath)) {} else {
25807
26005
  const parentDir = getMarketplacesDir();
25808
26006
  if (!existsSync7(parentDir)) {
25809
- await mkdir4(parentDir, { recursive: true });
26007
+ await mkdir5(parentDir, { recursive: true });
25810
26008
  }
25811
26009
  const { owner, repo } = parseLocation(parsed.location);
25812
26010
  const repoUrl = gitHubUrl(owner, repo);
@@ -25894,6 +26092,9 @@ async function removeMarketplace(name) {
25894
26092
  const entry = registry.marketplaces[name];
25895
26093
  delete registry.marketplaces[name];
25896
26094
  await saveRegistry(registry);
26095
+ if (existsSync7(entry.path)) {
26096
+ await rm3(entry.path, { recursive: true, force: true });
26097
+ }
25897
26098
  const { removeUserPluginsForMarketplace: removeUserPluginsForMarketplace2 } = await Promise.resolve().then(() => (init_user_workspace(), exports_user_workspace));
25898
26099
  const removedUserPlugins = await removeUserPluginsForMarketplace2(name);
25899
26100
  return {
@@ -25916,11 +26117,7 @@ async function findMarketplace(name, sourceLocation) {
25916
26117
  return registry.marketplaces[name];
25917
26118
  }
25918
26119
  if (sourceLocation) {
25919
- for (const entry of Object.values(registry.marketplaces)) {
25920
- if (entry.source.location === sourceLocation) {
25921
- return entry;
25922
- }
25923
- }
26120
+ return findBySourceLocation(registry, sourceLocation);
25924
26121
  }
25925
26122
  return null;
25926
26123
  }
@@ -25997,7 +26194,7 @@ async function getMarketplacePluginsFromManifest(marketplacePath) {
25997
26194
  const resolvedSource = resolvePluginSourcePath(plugin.source, marketplacePath);
25998
26195
  const info = {
25999
26196
  name: plugin.name,
26000
- path: typeof plugin.source === "string" ? resolve6(marketplacePath, plugin.source) : resolvedSource,
26197
+ path: typeof plugin.source === "string" ? resolve7(marketplacePath, plugin.source) : resolvedSource,
26001
26198
  description: plugin.description,
26002
26199
  source: resolvedSource
26003
26200
  };
@@ -26082,7 +26279,7 @@ async function resolvePluginSpec(spec, options2 = {}) {
26082
26279
  const pluginEntry = manifestResult.data.plugins.find((p) => p.name === parsed.plugin);
26083
26280
  if (pluginEntry) {
26084
26281
  if (typeof pluginEntry.source === "string") {
26085
- const resolvedPath = resolve6(marketplacePath, pluginEntry.source);
26282
+ const resolvedPath = resolve7(marketplacePath, pluginEntry.source);
26086
26283
  if (existsSync7(resolvedPath)) {
26087
26284
  return {
26088
26285
  path: resolvedPath,
@@ -26137,7 +26334,7 @@ async function refreshMarketplace(marketplace) {
26137
26334
  delete registry.marketplaces[marketplace.name];
26138
26335
  await saveRegistry(registry);
26139
26336
  if (existsSync7(marketplace.path)) {
26140
- await rm2(marketplace.path, { recursive: true, force: true });
26337
+ await rm3(marketplace.path, { recursive: true, force: true });
26141
26338
  }
26142
26339
  return addMarketplace(`${owner}/${repo}`, marketplace.name, branch);
26143
26340
  }
@@ -26202,35 +26399,22 @@ async function resolvePluginSpecWithAutoRegister(spec, options2 = {}) {
26202
26399
  ...marketplace.name !== marketplaceName && { registeredAs: marketplace.name }
26203
26400
  };
26204
26401
  }
26205
- async function autoRegisterMarketplace(name) {
26206
- const wellKnown = getWellKnownMarketplaces();
26207
- if (wellKnown[name]) {
26208
- console.log(`Auto-registering well-known marketplace: ${name}`);
26209
- const result = await addMarketplace(name);
26210
- if (!result.success) {
26211
- return { success: false, error: result.error || "Unknown error" };
26212
- }
26213
- return { success: true, name };
26214
- }
26215
- if (name.includes("/") && !name.includes("://")) {
26216
- const parts = name.split("/");
26402
+ async function autoRegisterMarketplace(source) {
26403
+ if (source.includes("/") && !source.includes("://")) {
26404
+ const parts = source.split("/");
26217
26405
  if (parts.length === 2 && parts[0] && parts[1]) {
26218
- console.log(`Auto-registering GitHub marketplace: ${name}`);
26219
- const result = await addMarketplace(name);
26406
+ console.log(`Auto-registering GitHub marketplace: ${source}`);
26407
+ const result = await addMarketplace(source);
26220
26408
  if (!result.success) {
26221
26409
  return { success: false, error: result.error || "Unknown error" };
26222
26410
  }
26223
- const registeredName = result.marketplace?.name ?? parts[1];
26224
- return { success: true, name: registeredName };
26411
+ return { success: true, name: result.marketplace?.name ?? parts[1] };
26225
26412
  }
26226
26413
  }
26227
26414
  return {
26228
26415
  success: false,
26229
- error: `Marketplace '${name}' not found.
26230
- Options:
26231
- 1. Use fully qualified name: plugin@owner/repo
26232
- 2. Register first: allagents plugin marketplace add <source>
26233
- 3. Well-known marketplaces: ${Object.keys(wellKnown).join(", ")}`
26416
+ error: `Marketplace '${source}' not found.
26417
+ Use fully qualified format: plugin@owner/repo`
26234
26418
  };
26235
26419
  }
26236
26420
  function isPluginSpec(spec) {
@@ -26240,10 +26424,48 @@ function isPluginSpec(spec) {
26240
26424
  }
26241
26425
  return true;
26242
26426
  }
26243
- function getWellKnownMarketplaces() {
26244
- return { ...WELL_KNOWN_MARKETPLACES };
26427
+ function extractUniqueMarketplaceSources(plugins) {
26428
+ const sources = new Set;
26429
+ for (const plugin of plugins) {
26430
+ if (!isPluginSpec(plugin))
26431
+ continue;
26432
+ const parsed = parsePluginSpec(plugin);
26433
+ if (!parsed)
26434
+ continue;
26435
+ if (parsed.owner && parsed.repo) {
26436
+ sources.add(`${parsed.owner}/${parsed.repo}`);
26437
+ }
26438
+ }
26439
+ return Array.from(sources);
26440
+ }
26441
+ async function ensureMarketplacesRegistered(plugins) {
26442
+ const sources = extractUniqueMarketplaceSources(plugins);
26443
+ const results = [];
26444
+ for (const source of sources) {
26445
+ const parts = source.split("/");
26446
+ const sourceLocation = source;
26447
+ const existing = await findMarketplace(parts[1] ?? "", sourceLocation);
26448
+ if (existing) {
26449
+ results.push({ source, success: true, name: existing.name });
26450
+ continue;
26451
+ }
26452
+ const result = await autoRegisterMarketplace(source);
26453
+ results.push({ source, ...result });
26454
+ }
26455
+ return results;
26456
+ }
26457
+ async function getMarketplaceVersion(marketplacePath) {
26458
+ if (!existsSync7(marketplacePath)) {
26459
+ return null;
26460
+ }
26461
+ try {
26462
+ const git = esm_default(marketplacePath);
26463
+ const log = await git.log({ maxCount: 1, format: { hash: "%h" } });
26464
+ return log.latest?.hash || null;
26465
+ } catch {
26466
+ return null;
26467
+ }
26245
26468
  }
26246
- var WELL_KNOWN_MARKETPLACES;
26247
26469
  var init_marketplace = __esm(() => {
26248
26470
  init_esm();
26249
26471
  init_git();
@@ -26251,13 +26473,10 @@ var init_marketplace = __esm(() => {
26251
26473
  init_plugin();
26252
26474
  init_plugin_path();
26253
26475
  init_constants();
26254
- WELL_KNOWN_MARKETPLACES = {
26255
- "claude-plugins-official": "anthropics/claude-plugins-official"
26256
- };
26257
26476
  });
26258
26477
 
26259
26478
  // src/core/workspace-modify.ts
26260
- import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir5 } from "node:fs/promises";
26479
+ import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir6 } from "node:fs/promises";
26261
26480
  import { existsSync as existsSync8 } from "node:fs";
26262
26481
  import { join as join10 } from "node:path";
26263
26482
  async function setClients(clients, workspacePath = process.cwd()) {
@@ -26291,7 +26510,7 @@ async function ensureWorkspace(workspacePath) {
26291
26510
  plugins: [],
26292
26511
  clients: [...DEFAULT_PROJECT_CLIENTS]
26293
26512
  };
26294
- await mkdir5(configDir, { recursive: true });
26513
+ await mkdir6(configDir, { recursive: true });
26295
26514
  await writeFile4(configPath, dump(defaultConfig, { lineWidth: -1 }), "utf-8");
26296
26515
  }
26297
26516
  async function addPlugin(plugin, workspacePath = process.cwd()) {
@@ -26407,6 +26626,65 @@ async function removePlugin(plugin, workspacePath = process.cwd()) {
26407
26626
  };
26408
26627
  }
26409
26628
  }
26629
+ async function addDisabledSkill(skillKey, workspacePath = process.cwd()) {
26630
+ const configPath = join10(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26631
+ if (!existsSync8(configPath)) {
26632
+ return {
26633
+ success: false,
26634
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
26635
+ };
26636
+ }
26637
+ try {
26638
+ const content = await readFile8(configPath, "utf-8");
26639
+ const config = load(content);
26640
+ const disabledSkills = config.disabledSkills ?? [];
26641
+ if (disabledSkills.includes(skillKey)) {
26642
+ return {
26643
+ success: false,
26644
+ error: `Skill '${skillKey}' is already disabled`
26645
+ };
26646
+ }
26647
+ config.disabledSkills = [...disabledSkills, skillKey];
26648
+ await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26649
+ return { success: true };
26650
+ } catch (error) {
26651
+ return {
26652
+ success: false,
26653
+ error: error instanceof Error ? error.message : String(error)
26654
+ };
26655
+ }
26656
+ }
26657
+ async function removeDisabledSkill(skillKey, workspacePath = process.cwd()) {
26658
+ const configPath = join10(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26659
+ if (!existsSync8(configPath)) {
26660
+ return {
26661
+ success: false,
26662
+ error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}`
26663
+ };
26664
+ }
26665
+ try {
26666
+ const content = await readFile8(configPath, "utf-8");
26667
+ const config = load(content);
26668
+ const disabledSkills = config.disabledSkills ?? [];
26669
+ if (!disabledSkills.includes(skillKey)) {
26670
+ return {
26671
+ success: false,
26672
+ error: `Skill '${skillKey}' is already enabled`
26673
+ };
26674
+ }
26675
+ config.disabledSkills = disabledSkills.filter((s) => s !== skillKey);
26676
+ if (config.disabledSkills.length === 0) {
26677
+ config.disabledSkills = undefined;
26678
+ }
26679
+ await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
26680
+ return { success: true };
26681
+ } catch (error) {
26682
+ return {
26683
+ success: false,
26684
+ error: error instanceof Error ? error.message : String(error)
26685
+ };
26686
+ }
26687
+ }
26410
26688
  var DEFAULT_PROJECT_CLIENTS;
26411
26689
  var init_workspace_modify = __esm(() => {
26412
26690
  init_js_yaml();
@@ -26711,9 +26989,9 @@ var init_sync_state = __esm(() => {
26711
26989
  });
26712
26990
 
26713
26991
  // src/core/sync-state.ts
26714
- import { readFile as readFile10, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
26992
+ import { readFile as readFile10, writeFile as writeFile6, mkdir as mkdir7 } from "node:fs/promises";
26715
26993
  import { existsSync as existsSync10 } from "node:fs";
26716
- import { join as join12, dirname as dirname3 } from "node:path";
26994
+ import { join as join12, dirname as dirname4 } from "node:path";
26717
26995
  function getSyncStatePath(workspacePath) {
26718
26996
  return join12(workspacePath, CONFIG_DIR, SYNC_STATE_FILE);
26719
26997
  }
@@ -26741,7 +27019,7 @@ async function saveSyncState(workspacePath, files) {
26741
27019
  lastSync: new Date().toISOString(),
26742
27020
  files
26743
27021
  };
26744
- await mkdir6(dirname3(statePath), { recursive: true });
27022
+ await mkdir7(dirname4(statePath), { recursive: true });
26745
27023
  await writeFile6(statePath, JSON.stringify(state, null, 2), "utf-8");
26746
27024
  }
26747
27025
  function getPreviouslySyncedFiles(state, client) {
@@ -26756,11 +27034,11 @@ var init_sync_state2 = __esm(() => {
26756
27034
  });
26757
27035
 
26758
27036
  // src/core/vscode-workspace.ts
26759
- import { resolve as resolve7, basename as basename3, isAbsolute as isAbsolute2 } from "node:path";
27037
+ import { resolve as resolve8, basename as basename3, isAbsolute as isAbsolute2 } from "node:path";
26760
27038
  function buildPathPlaceholderMap(repositories, workspacePath) {
26761
27039
  const map2 = new Map;
26762
27040
  for (const repo of repositories) {
26763
- const absolutePath = resolve7(workspacePath, repo.path);
27041
+ const absolutePath = resolve8(workspacePath, repo.path);
26764
27042
  map2.set(repo.path, absolutePath);
26765
27043
  }
26766
27044
  return map2;
@@ -26795,16 +27073,16 @@ function generateVscodeWorkspace(input) {
26795
27073
  const folders = [];
26796
27074
  const seenPaths = new Set;
26797
27075
  folders.push({ path: "." });
26798
- seenPaths.add(resolve7(workspacePath, "."));
27076
+ seenPaths.add(resolve8(workspacePath, "."));
26799
27077
  for (const repo of repositories) {
26800
- const absolutePath = resolve7(workspacePath, repo.path).replace(/\\/g, "/");
27078
+ const absolutePath = resolve8(workspacePath, repo.path).replace(/\\/g, "/");
26801
27079
  folders.push({ path: absolutePath });
26802
27080
  seenPaths.add(absolutePath);
26803
27081
  }
26804
27082
  if (resolvedTemplate && Array.isArray(resolvedTemplate.folders)) {
26805
27083
  for (const folder of resolvedTemplate.folders) {
26806
27084
  const rawPath = folder.path;
26807
- const normalizedPath = (typeof rawPath === "string" && !isAbsolute2(rawPath) ? resolve7(workspacePath, rawPath) : rawPath).replace(/\\/g, "/");
27085
+ const normalizedPath = (typeof rawPath === "string" && !isAbsolute2(rawPath) ? resolve8(workspacePath, rawPath) : rawPath).replace(/\\/g, "/");
26808
27086
  if (!seenPaths.has(normalizedPath)) {
26809
27087
  const entry = { path: normalizedPath };
26810
27088
  if (folder.name)
@@ -26834,10 +27112,10 @@ function getWorkspaceOutputPath(workspacePath, vscodeConfig) {
26834
27112
  const name = vscodeConfig?.output;
26835
27113
  if (name) {
26836
27114
  const filename = name.endsWith(".code-workspace") ? name : `${name}.code-workspace`;
26837
- return resolve7(workspacePath, filename);
27115
+ return resolve8(workspacePath, filename);
26838
27116
  }
26839
- const dirName = basename3(resolve7(workspacePath));
26840
- return resolve7(workspacePath, `${dirName}.code-workspace`);
27117
+ const dirName = basename3(resolve8(workspacePath));
27118
+ return resolve8(workspacePath, `${dirName}.code-workspace`);
26841
27119
  }
26842
27120
  var DEFAULT_SETTINGS;
26843
27121
  var init_vscode_workspace = __esm(() => {
@@ -26847,9 +27125,9 @@ var init_vscode_workspace = __esm(() => {
26847
27125
  });
26848
27126
 
26849
27127
  // src/core/sync.ts
26850
- import { existsSync as existsSync11, readFileSync, writeFileSync } from "node:fs";
26851
- import { rm as rm3, unlink, rmdir, copyFile } from "node:fs/promises";
26852
- import { join as join13, resolve as resolve8, dirname as dirname4, relative } from "node:path";
27128
+ import { existsSync as existsSync11, readFileSync, writeFileSync, lstatSync } from "node:fs";
27129
+ import { rm as rm4, unlink, rmdir, copyFile } from "node:fs/promises";
27130
+ import { join as join13, resolve as resolve9, dirname as dirname5, relative as relative2 } from "node:path";
26853
27131
  function deduplicateClientsByPath(clients, clientMappings = CLIENT_MAPPINGS) {
26854
27132
  const pathToClients = new Map;
26855
27133
  for (const client of clients) {
@@ -26900,8 +27178,11 @@ async function selectivePurgeWorkspace(workspacePath, state, clients, options2)
26900
27178
  continue;
26901
27179
  }
26902
27180
  try {
26903
- if (filePath.endsWith("/")) {
26904
- await rm3(fullPath, { recursive: true, force: true });
27181
+ const stats = lstatSync(fullPath.replace(/\/$/, ""));
27182
+ if (stats.isSymbolicLink()) {
27183
+ await unlink(fullPath.replace(/\/$/, ""));
27184
+ } else if (filePath.endsWith("/")) {
27185
+ await rm4(fullPath, { recursive: true, force: true });
26905
27186
  } else {
26906
27187
  await unlink(fullPath);
26907
27188
  }
@@ -26916,16 +27197,16 @@ async function selectivePurgeWorkspace(workspacePath, state, clients, options2)
26916
27197
  return result;
26917
27198
  }
26918
27199
  async function cleanupEmptyParents(workspacePath, filePath) {
26919
- let parentPath = dirname4(filePath);
27200
+ let parentPath = dirname5(filePath);
26920
27201
  while (parentPath && parentPath !== "." && parentPath !== "/") {
26921
27202
  const fullParentPath = join13(workspacePath, parentPath);
26922
27203
  if (!existsSync11(fullParentPath)) {
26923
- parentPath = dirname4(parentPath);
27204
+ parentPath = dirname5(parentPath);
26924
27205
  continue;
26925
27206
  }
26926
27207
  try {
26927
27208
  await rmdir(fullParentPath);
26928
- parentPath = dirname4(parentPath);
27209
+ parentPath = dirname5(parentPath);
26929
27210
  } catch {
26930
27211
  break;
26931
27212
  }
@@ -27032,11 +27313,11 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
27032
27313
  if (file.source.startsWith("/")) {
27033
27314
  fullPath = file.source;
27034
27315
  } else if (file.source.startsWith("../")) {
27035
- fullPath = resolve8(file.source);
27316
+ fullPath = resolve9(file.source);
27036
27317
  } else if (defaultSourcePath) {
27037
27318
  fullPath = join13(defaultSourcePath, file.source);
27038
27319
  } else {
27039
- fullPath = resolve8(file.source);
27320
+ fullPath = resolve9(file.source);
27040
27321
  }
27041
27322
  if (!existsSync11(fullPath)) {
27042
27323
  errors2.push(`File source not found: ${fullPath}`);
@@ -27065,7 +27346,7 @@ function collectSyncedPaths(copyResults, workspacePath, clients, clientMappings)
27065
27346
  if (copyResult.action !== "copied" && copyResult.action !== "generated") {
27066
27347
  continue;
27067
27348
  }
27068
- const relativePath = relative(workspacePath, copyResult.destination).replace(/\\/g, "/");
27349
+ const relativePath = relative2(workspacePath, copyResult.destination).replace(/\\/g, "/");
27069
27350
  for (const client of clients) {
27070
27351
  const mapping = mappings[client];
27071
27352
  if (mapping.skillsPath && relativePath.startsWith(mapping.skillsPath)) {
@@ -27121,7 +27402,7 @@ async function validatePlugin(pluginSource, workspacePath, offline) {
27121
27402
  success: true
27122
27403
  };
27123
27404
  }
27124
- const resolvedPath = resolve8(workspacePath, pluginSource);
27405
+ const resolvedPath = resolve9(workspacePath, pluginSource);
27125
27406
  if (!existsSync11(resolvedPath)) {
27126
27407
  return {
27127
27408
  plugin: pluginSource,
@@ -27139,13 +27420,55 @@ async function validatePlugin(pluginSource, workspacePath, offline) {
27139
27420
  async function validateAllPlugins(plugins, workspacePath, offline) {
27140
27421
  return Promise.all(plugins.map((plugin) => validatePlugin(plugin, workspacePath, offline)));
27141
27422
  }
27142
- async function copyValidatedPlugin(validatedPlugin, workspacePath, clients, dryRun, skillNameMap, clientMappings) {
27423
+ async function copyValidatedPlugin(validatedPlugin, workspacePath, clients, dryRun, skillNameMap, clientMappings, syncMode = "symlink") {
27143
27424
  const copyResults = [];
27144
27425
  const mappings = clientMappings ?? CLIENT_MAPPINGS;
27145
- const { representativeClients } = deduplicateClientsByPath(clients, mappings);
27146
- for (const client of representativeClients) {
27147
- const results = await copyPluginToWorkspace(validatedPlugin.resolved, workspacePath, client, { dryRun, ...skillNameMap && { skillNameMap }, ...clientMappings && { clientMappings } });
27148
- copyResults.push(...results);
27426
+ const clientList = clients;
27427
+ if (syncMode === "symlink") {
27428
+ const { representativeClients } = deduplicateClientsByPath(clientList, mappings);
27429
+ const canonicalRepresentative = representativeClients.find((c) => mappings[c]?.skillsPath === CANONICAL_SKILLS_PATH);
27430
+ const needsCanonicalCopy = !canonicalRepresentative;
27431
+ const nonUniversalClients = clientList.filter((c) => !isUniversalClient(c));
27432
+ if (needsCanonicalCopy && nonUniversalClients.length > 0) {
27433
+ const canonicalResults = await copyPluginToWorkspace(validatedPlugin.resolved, workspacePath, "copilot", {
27434
+ dryRun,
27435
+ ...skillNameMap && { skillNameMap },
27436
+ syncMode: "copy"
27437
+ });
27438
+ const skillResults = canonicalResults.filter((r) => r.destination.includes(CANONICAL_SKILLS_PATH) && r.action === "copied");
27439
+ copyResults.push(...skillResults);
27440
+ }
27441
+ for (const representative of representativeClients) {
27442
+ if (isUniversalClient(representative)) {
27443
+ const results = await copyPluginToWorkspace(validatedPlugin.resolved, workspacePath, representative, {
27444
+ dryRun,
27445
+ ...skillNameMap && { skillNameMap },
27446
+ ...clientMappings && { clientMappings },
27447
+ syncMode: "copy"
27448
+ });
27449
+ copyResults.push(...results);
27450
+ } else {
27451
+ const results = await copyPluginToWorkspace(validatedPlugin.resolved, workspacePath, representative, {
27452
+ dryRun,
27453
+ ...skillNameMap && { skillNameMap },
27454
+ ...clientMappings && { clientMappings },
27455
+ syncMode: "symlink",
27456
+ canonicalSkillsPath: CANONICAL_SKILLS_PATH
27457
+ });
27458
+ copyResults.push(...results);
27459
+ }
27460
+ }
27461
+ } else {
27462
+ const { representativeClients } = deduplicateClientsByPath(clientList, mappings);
27463
+ for (const client of representativeClients) {
27464
+ const results = await copyPluginToWorkspace(validatedPlugin.resolved, workspacePath, client, {
27465
+ dryRun,
27466
+ ...skillNameMap && { skillNameMap },
27467
+ ...clientMappings && { clientMappings },
27468
+ syncMode: "copy"
27469
+ });
27470
+ copyResults.push(...results);
27471
+ }
27149
27472
  }
27150
27473
  const hasFailures = copyResults.some((r) => r.action === "failed");
27151
27474
  return {
@@ -27155,11 +27478,11 @@ async function copyValidatedPlugin(validatedPlugin, workspacePath, clients, dryR
27155
27478
  copyResults
27156
27479
  };
27157
27480
  }
27158
- async function collectAllSkills(validatedPlugins) {
27481
+ async function collectAllSkills(validatedPlugins, disabledSkills) {
27159
27482
  const allSkills = [];
27160
27483
  for (const plugin of validatedPlugins) {
27161
27484
  const pluginName = plugin.pluginName ?? await getPluginName(plugin.resolved);
27162
- const skills = await collectPluginSkills(plugin.resolved, plugin.plugin);
27485
+ const skills = await collectPluginSkills(plugin.resolved, plugin.plugin, disabledSkills, pluginName);
27163
27486
  for (const skill of skills) {
27164
27487
  allSkills.push({
27165
27488
  folderName: skill.folderName,
@@ -27263,6 +27586,7 @@ async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
27263
27586
  };
27264
27587
  }
27265
27588
  }
27589
+ await ensureMarketplacesRegistered(config.plugins);
27266
27590
  const validatedPlugins = await validateAllPlugins(config.plugins, workspacePath, offline);
27267
27591
  let validatedWorkspaceSource = null;
27268
27592
  if (config.workspace?.source) {
@@ -27307,11 +27631,13 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
27307
27631
  partialSync: !!options2.clients
27308
27632
  });
27309
27633
  }
27310
- const allSkills = await collectAllSkills(validPlugins);
27634
+ const disabledSkillsSet = new Set(config.disabledSkills ?? []);
27635
+ const allSkills = await collectAllSkills(validPlugins, disabledSkillsSet);
27311
27636
  const pluginSkillMaps = buildPluginSkillNameMaps(allSkills);
27637
+ const syncMode = config.syncMode ?? "symlink";
27312
27638
  const pluginResults = await Promise.all(validPlugins.map((validatedPlugin) => {
27313
27639
  const skillNameMap = pluginSkillMaps.get(validatedPlugin.resolved);
27314
- return copyValidatedPlugin(validatedPlugin, workspacePath, clients, dryRun, skillNameMap);
27640
+ return copyValidatedPlugin(validatedPlugin, workspacePath, clients, dryRun, skillNameMap, undefined, syncMode);
27315
27641
  }));
27316
27642
  let workspaceFileResults = [];
27317
27643
  if (config.workspace) {
@@ -27437,7 +27763,7 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
27437
27763
  };
27438
27764
  }
27439
27765
  async function syncUserWorkspace(options2 = {}) {
27440
- const homeDir = resolve8(getHomeDir());
27766
+ const homeDir = resolve9(getHomeDir());
27441
27767
  const config = await getUserWorkspaceConfig();
27442
27768
  if (!config) {
27443
27769
  return {
@@ -27451,6 +27777,7 @@ async function syncUserWorkspace(options2 = {}) {
27451
27777
  }
27452
27778
  const clients = config.clients;
27453
27779
  const { offline = false, dryRun = false } = options2;
27780
+ await ensureMarketplacesRegistered(config.plugins);
27454
27781
  const validatedPlugins = await validateAllPlugins(config.plugins, homeDir, offline);
27455
27782
  const failedValidations = validatedPlugins.filter((v) => !v.success);
27456
27783
  const validPlugins = validatedPlugins.filter((v) => v.success);
@@ -27473,11 +27800,13 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
27473
27800
  if (!dryRun) {
27474
27801
  await selectivePurgeWorkspace(homeDir, previousState, clients);
27475
27802
  }
27476
- const allSkills = await collectAllSkills(validPlugins);
27803
+ const disabledSkillsSet = new Set(config.disabledSkills ?? []);
27804
+ const allSkills = await collectAllSkills(validPlugins, disabledSkillsSet);
27477
27805
  const pluginSkillMaps = buildPluginSkillNameMaps(allSkills);
27806
+ const syncMode = config.syncMode ?? "symlink";
27478
27807
  const pluginResults = await Promise.all(validPlugins.map((vp) => {
27479
27808
  const skillNameMap = pluginSkillMaps.get(vp.resolved);
27480
- return copyValidatedPlugin(vp, homeDir, clients, dryRun, skillNameMap, USER_CLIENT_MAPPINGS);
27809
+ return copyValidatedPlugin(vp, homeDir, clients, dryRun, skillNameMap, USER_CLIENT_MAPPINGS, syncMode);
27481
27810
  }));
27482
27811
  let totalCopied = 0;
27483
27812
  let totalFailed = 0;
@@ -27631,12 +27960,12 @@ var init_github_fetch = __esm(() => {
27631
27960
  });
27632
27961
 
27633
27962
  // src/core/workspace.ts
27634
- import { mkdir as mkdir7, readFile as readFile11, writeFile as writeFile7, copyFile as copyFile2 } from "node:fs/promises";
27963
+ import { mkdir as mkdir8, readFile as readFile11, writeFile as writeFile7, copyFile as copyFile2 } from "node:fs/promises";
27635
27964
  import { existsSync as existsSync13 } from "node:fs";
27636
- import { join as join15, resolve as resolve9, dirname as dirname5, relative as relative2, sep as sep2, isAbsolute as isAbsolute3 } from "node:path";
27965
+ import { join as join15, resolve as resolve10, dirname as dirname6, relative as relative3, sep as sep2, isAbsolute as isAbsolute3 } from "node:path";
27637
27966
  import { fileURLToPath } from "node:url";
27638
27967
  async function initWorkspace(targetPath = ".", options2 = {}) {
27639
- const absoluteTarget = resolve9(targetPath);
27968
+ const absoluteTarget = resolve10(targetPath);
27640
27969
  const configDir = join15(absoluteTarget, CONFIG_DIR);
27641
27970
  const configPath = join15(configDir, WORKSPACE_CONFIG_FILE);
27642
27971
  if (existsSync13(configPath)) {
@@ -27644,13 +27973,13 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
27644
27973
  Found existing ${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE}`);
27645
27974
  }
27646
27975
  const currentFilePath = fileURLToPath(import.meta.url);
27647
- const currentFileDir = dirname5(currentFilePath);
27976
+ const currentFileDir = dirname6(currentFilePath);
27648
27977
  const isProduction = currentFilePath.includes(`${sep2}dist${sep2}`);
27649
27978
  const defaultTemplatePath = isProduction ? join15(currentFileDir, "templates", "default") : join15(currentFileDir, "..", "templates", "default");
27650
27979
  let githubTempDir;
27651
27980
  try {
27652
- await mkdir7(absoluteTarget, { recursive: true });
27653
- await mkdir7(configDir, { recursive: true });
27981
+ await mkdir8(absoluteTarget, { recursive: true });
27982
+ await mkdir8(configDir, { recursive: true });
27654
27983
  let workspaceYamlContent;
27655
27984
  let sourceDir;
27656
27985
  if (options2.from) {
@@ -27682,7 +28011,7 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
27682
28011
  }
27683
28012
  console.log(`✓ Using workspace.yaml from: ${options2.from}`);
27684
28013
  } else {
27685
- const fromPath = resolve9(options2.from);
28014
+ const fromPath = resolve10(options2.from);
27686
28015
  if (!existsSync13(fromPath)) {
27687
28016
  throw new Error(`Template not found: ${fromPath}`);
27688
28017
  }
@@ -27704,9 +28033,9 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
27704
28033
  }
27705
28034
  } else {
27706
28035
  sourceYamlPath = fromPath;
27707
- const parentDir = dirname5(fromPath);
28036
+ const parentDir = dirname6(fromPath);
27708
28037
  if (parentDir.endsWith(CONFIG_DIR)) {
27709
- sourceDir = dirname5(parentDir);
28038
+ sourceDir = dirname6(parentDir);
27710
28039
  } else {
27711
28040
  sourceDir = parentDir;
27712
28041
  }
@@ -27718,7 +28047,7 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
27718
28047
  if (workspace?.source) {
27719
28048
  const source = workspace.source;
27720
28049
  if (!isGitHubUrl(source) && !isAbsolute3(source)) {
27721
- workspace.source = resolve9(sourceDir, source);
28050
+ workspace.source = resolve10(sourceDir, source);
27722
28051
  workspaceYamlContent = dump(parsed2, { lineWidth: -1 });
27723
28052
  }
27724
28053
  }
@@ -27834,7 +28163,7 @@ Syncing plugins...`);
27834
28163
  if (targetPath !== ".") {
27835
28164
  console.log(`
27836
28165
  Next steps:`);
27837
- console.log(` cd ${relative2(process.cwd(), absoluteTarget)}`);
28166
+ console.log(` cd ${relative3(process.cwd(), absoluteTarget)}`);
27838
28167
  }
27839
28168
  return {
27840
28169
  path: absoluteTarget,
@@ -28045,12 +28374,12 @@ var require_isexe = __commonJS((exports, module) => {
28045
28374
  if (typeof Promise !== "function") {
28046
28375
  throw new TypeError("callback not provided");
28047
28376
  }
28048
- return new Promise(function(resolve11, reject) {
28377
+ return new Promise(function(resolve13, reject) {
28049
28378
  isexe(path, options2 || {}, function(er, is) {
28050
28379
  if (er) {
28051
28380
  reject(er);
28052
28381
  } else {
28053
- resolve11(is);
28382
+ resolve13(is);
28054
28383
  }
28055
28384
  });
28056
28385
  });
@@ -28112,27 +28441,27 @@ var require_which = __commonJS((exports, module) => {
28112
28441
  opt = {};
28113
28442
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
28114
28443
  const found = [];
28115
- const step = (i2) => new Promise((resolve11, reject) => {
28444
+ const step = (i2) => new Promise((resolve13, reject) => {
28116
28445
  if (i2 === pathEnv.length)
28117
- return opt.all && found.length ? resolve11(found) : reject(getNotFoundError(cmd));
28446
+ return opt.all && found.length ? resolve13(found) : reject(getNotFoundError(cmd));
28118
28447
  const ppRaw = pathEnv[i2];
28119
28448
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
28120
28449
  const pCmd = path.join(pathPart, cmd);
28121
28450
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
28122
- resolve11(subStep(p, i2, 0));
28451
+ resolve13(subStep(p, i2, 0));
28123
28452
  });
28124
- const subStep = (p, i2, ii) => new Promise((resolve11, reject) => {
28453
+ const subStep = (p, i2, ii) => new Promise((resolve13, reject) => {
28125
28454
  if (ii === pathExt.length)
28126
- return resolve11(step(i2 + 1));
28455
+ return resolve13(step(i2 + 1));
28127
28456
  const ext = pathExt[ii];
28128
28457
  isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
28129
28458
  if (!er && is) {
28130
28459
  if (opt.all)
28131
28460
  found.push(p + ext);
28132
28461
  else
28133
- return resolve11(p + ext);
28462
+ return resolve13(p + ext);
28134
28463
  }
28135
- return resolve11(subStep(p, i2, ii + 1));
28464
+ return resolve13(subStep(p, i2, ii + 1));
28136
28465
  });
28137
28466
  });
28138
28467
  return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
@@ -28173,8 +28502,8 @@ var require_which = __commonJS((exports, module) => {
28173
28502
  var require_path_key = __commonJS((exports, module) => {
28174
28503
  var pathKey = (options2 = {}) => {
28175
28504
  const environment = options2.env || process.env;
28176
- const platform = options2.platform || process.platform;
28177
- if (platform !== "win32") {
28505
+ const platform2 = options2.platform || process.platform;
28506
+ if (platform2 !== "win32") {
28178
28507
  return "PATH";
28179
28508
  }
28180
28509
  return Object.keys(environment).reverse().find((key) => key.toUpperCase() === "PATH") || "Path";
@@ -28250,8 +28579,8 @@ var require_shebang_regex = __commonJS((exports, module) => {
28250
28579
  // node_modules/shebang-command/index.js
28251
28580
  var require_shebang_command = __commonJS((exports, module) => {
28252
28581
  var shebangRegex = require_shebang_regex();
28253
- module.exports = (string3 = "") => {
28254
- const match = string3.match(shebangRegex);
28582
+ module.exports = (string4 = "") => {
28583
+ const match = string4.match(shebangRegex);
28255
28584
  if (!match) {
28256
28585
  return null;
28257
28586
  }
@@ -28268,12 +28597,12 @@ var require_shebang_command = __commonJS((exports, module) => {
28268
28597
  var require_readShebang = __commonJS((exports, module) => {
28269
28598
  var fs = __require("fs");
28270
28599
  var shebangCommand = require_shebang_command();
28271
- function readShebang(command3) {
28600
+ function readShebang(command4) {
28272
28601
  const size = 150;
28273
28602
  const buffer = Buffer.alloc(size);
28274
28603
  let fd;
28275
28604
  try {
28276
- fd = fs.openSync(command3, "r");
28605
+ fd = fs.openSync(command4, "r");
28277
28606
  fs.readSync(fd, buffer, 0, size, 0);
28278
28607
  fs.closeSync(fd);
28279
28608
  } catch (e) {}
@@ -28319,7 +28648,7 @@ var require_parse5 = __commonJS((exports, module) => {
28319
28648
  }
28320
28649
  return parsed;
28321
28650
  }
28322
- function parse2(command3, args, options2) {
28651
+ function parse2(command4, args, options2) {
28323
28652
  if (args && !Array.isArray(args)) {
28324
28653
  options2 = args;
28325
28654
  args = null;
@@ -28327,12 +28656,12 @@ var require_parse5 = __commonJS((exports, module) => {
28327
28656
  args = args ? args.slice(0) : [];
28328
28657
  options2 = Object.assign({}, options2);
28329
28658
  const parsed = {
28330
- command: command3,
28659
+ command: command4,
28331
28660
  args,
28332
28661
  options: options2,
28333
28662
  file: undefined,
28334
28663
  original: {
28335
- command: command3,
28664
+ command: command4,
28336
28665
  args
28337
28666
  }
28338
28667
  };
@@ -28393,14 +28722,14 @@ var require_cross_spawn = __commonJS((exports, module) => {
28393
28722
  var cp2 = __require("child_process");
28394
28723
  var parse2 = require_parse5();
28395
28724
  var enoent = require_enoent();
28396
- function spawn2(command3, args, options2) {
28397
- const parsed = parse2(command3, args, options2);
28725
+ function spawn2(command4, args, options2) {
28726
+ const parsed = parse2(command4, args, options2);
28398
28727
  const spawned = cp2.spawn(parsed.command, parsed.args, parsed.options);
28399
28728
  enoent.hookChildProcess(spawned, parsed);
28400
28729
  return spawned;
28401
28730
  }
28402
- function spawnSync(command3, args, options2) {
28403
- const parsed = parse2(command3, args, options2);
28731
+ function spawnSync(command4, args, options2) {
28732
+ const parsed = parse2(command4, args, options2);
28404
28733
  const result = cp2.spawnSync(parsed.command, parsed.args, parsed.options);
28405
28734
  result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
28406
28735
  return result;
@@ -28431,9 +28760,9 @@ function stripFinalNewline(input) {
28431
28760
  function pathKey(options2 = {}) {
28432
28761
  const {
28433
28762
  env: env2 = process.env,
28434
- platform = process.platform
28763
+ platform: platform2 = process.platform
28435
28764
  } = options2;
28436
- if (platform !== "win32") {
28765
+ if (platform2 !== "win32") {
28437
28766
  return "PATH";
28438
28767
  }
28439
28768
  return Object.keys(env2).reverse().find((key) => key.toUpperCase() === "PATH") || "Path";
@@ -28943,7 +29272,7 @@ var getErrorPrefix = ({ timedOut, timeout, errorCode, signal, signalDescription,
28943
29272
  error,
28944
29273
  signal,
28945
29274
  exitCode,
28946
- command: command3,
29275
+ command: command4,
28947
29276
  escapedCommand,
28948
29277
  timedOut,
28949
29278
  isCanceled,
@@ -28955,7 +29284,7 @@ var getErrorPrefix = ({ timedOut, timeout, errorCode, signal, signalDescription,
28955
29284
  const signalDescription = signal === undefined ? undefined : signalsByName[signal].description;
28956
29285
  const errorCode = error && error.code;
28957
29286
  const prefix = getErrorPrefix({ timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled });
28958
- const execaMessage = `Command ${prefix}: ${command3}`;
29287
+ const execaMessage = `Command ${prefix}: ${command4}`;
28959
29288
  const isError = Object.prototype.toString.call(error) === "[object Error]";
28960
29289
  const shortMessage = isError ? `${execaMessage}
28961
29290
  ${error.message}` : execaMessage;
@@ -28968,7 +29297,7 @@ ${error.message}` : execaMessage;
28968
29297
  error = new Error(message);
28969
29298
  }
28970
29299
  error.shortMessage = shortMessage;
28971
- error.command = command3;
29300
+ error.command = command4;
28972
29301
  error.escapedCommand = escapedCommand;
28973
29302
  error.exitCode = exitCode;
28974
29303
  error.signal = signal;
@@ -29265,7 +29594,7 @@ var DEFAULT_FORCE_KILL_TIMEOUT, spawnedKill = (kill, signal = "SIGTERM", options
29265
29594
  return spawnedPromise;
29266
29595
  }
29267
29596
  let timeoutId;
29268
- const timeoutPromise = new Promise((resolve11, reject) => {
29597
+ const timeoutPromise = new Promise((resolve13, reject) => {
29269
29598
  timeoutId = setTimeout(() => {
29270
29599
  timeoutKill(spawned, killSignal, reject);
29271
29600
  }, timeout);
@@ -29655,9 +29984,9 @@ var nativePromisePrototype, descriptors, mergePromise = (spawned, promise) => {
29655
29984
  const value = typeof promise === "function" ? (...args) => Reflect.apply(descriptor.value, promise(), args) : descriptor.value.bind(promise);
29656
29985
  Reflect.defineProperty(spawned, property, { ...descriptor, value });
29657
29986
  }
29658
- }, getSpawnedPromise = (spawned) => new Promise((resolve11, reject) => {
29987
+ }, getSpawnedPromise = (spawned) => new Promise((resolve13, reject) => {
29659
29988
  spawned.on("exit", (exitCode, signal) => {
29660
- resolve11({ exitCode, signal });
29989
+ resolve13({ exitCode, signal });
29661
29990
  });
29662
29991
  spawned.on("error", (error) => {
29663
29992
  reject(error);
@@ -29758,7 +30087,7 @@ import childProcess from "node:child_process";
29758
30087
  import process7 from "node:process";
29759
30088
  function execa(file, args, options2) {
29760
30089
  const parsed = handleArguments(file, args, options2);
29761
- const command3 = joinCommand(file, args);
30090
+ const command4 = joinCommand(file, args);
29762
30091
  const escapedCommand = getEscapedCommand(file, args);
29763
30092
  logCommand(escapedCommand, parsed.options);
29764
30093
  validateTimeout(parsed.options);
@@ -29772,7 +30101,7 @@ function execa(file, args, options2) {
29772
30101
  stdout: "",
29773
30102
  stderr: "",
29774
30103
  all: "",
29775
- command: command3,
30104
+ command: command4,
29776
30105
  escapedCommand,
29777
30106
  parsed,
29778
30107
  timedOut: false,
@@ -29801,7 +30130,7 @@ function execa(file, args, options2) {
29801
30130
  stdout,
29802
30131
  stderr,
29803
30132
  all,
29804
- command: command3,
30133
+ command: command4,
29805
30134
  escapedCommand,
29806
30135
  parsed,
29807
30136
  timedOut,
@@ -29814,7 +30143,7 @@ function execa(file, args, options2) {
29814
30143
  throw returnedError;
29815
30144
  }
29816
30145
  return {
29817
- command: command3,
30146
+ command: command4,
29818
30147
  escapedCommand,
29819
30148
  exitCode: 0,
29820
30149
  stdout,
@@ -29835,7 +30164,7 @@ function execa(file, args, options2) {
29835
30164
  }
29836
30165
  function execaSync(file, args, options2) {
29837
30166
  const parsed = handleArguments(file, args, options2);
29838
- const command3 = joinCommand(file, args);
30167
+ const command4 = joinCommand(file, args);
29839
30168
  const escapedCommand = getEscapedCommand(file, args);
29840
30169
  logCommand(escapedCommand, parsed.options);
29841
30170
  const input = handleInputSync(parsed.options);
@@ -29848,7 +30177,7 @@ function execaSync(file, args, options2) {
29848
30177
  stdout: "",
29849
30178
  stderr: "",
29850
30179
  all: "",
29851
- command: command3,
30180
+ command: command4,
29852
30181
  escapedCommand,
29853
30182
  parsed,
29854
30183
  timedOut: false,
@@ -29865,7 +30194,7 @@ function execaSync(file, args, options2) {
29865
30194
  error: result.error,
29866
30195
  signal: result.signal,
29867
30196
  exitCode: result.status,
29868
- command: command3,
30197
+ command: command4,
29869
30198
  escapedCommand,
29870
30199
  parsed,
29871
30200
  timedOut: result.error && result.error.code === "ETIMEDOUT",
@@ -29878,7 +30207,7 @@ function execaSync(file, args, options2) {
29878
30207
  throw error;
29879
30208
  }
29880
30209
  return {
29881
- command: command3,
30210
+ command: command4,
29882
30211
  escapedCommand,
29883
30212
  exitCode: 0,
29884
30213
  stdout,
@@ -29973,7 +30302,7 @@ var package_default;
29973
30302
  var init_package = __esm(() => {
29974
30303
  package_default = {
29975
30304
  name: "allagents",
29976
- version: "0.17.2",
30305
+ version: "0.20.0",
29977
30306
  description: "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
29978
30307
  type: "module",
29979
30308
  bin: {
@@ -30042,13 +30371,13 @@ var init_package = __esm(() => {
30042
30371
  });
30043
30372
 
30044
30373
  // src/cli/update-check.ts
30045
- import { readFile as readFile13 } from "node:fs/promises";
30046
- import { join as join19 } from "node:path";
30374
+ import { readFile as readFile14 } from "node:fs/promises";
30375
+ import { join as join20 } from "node:path";
30047
30376
  import { spawn as spawn2 } from "node:child_process";
30048
30377
  async function getCachedUpdateInfo(path3) {
30049
- const filePath = path3 ?? join19(getHomeDir(), CONFIG_DIR, CACHE_FILE);
30378
+ const filePath = path3 ?? join20(getHomeDir(), CONFIG_DIR, CACHE_FILE);
30050
30379
  try {
30051
- const raw = await readFile13(filePath, "utf-8");
30380
+ const raw = await readFile14(filePath, "utf-8");
30052
30381
  const data = JSON.parse(raw);
30053
30382
  if (typeof data.latestVersion === "string" && typeof data.lastCheckedAt === "string") {
30054
30383
  return data;
@@ -30084,8 +30413,8 @@ function buildNotice(currentVersion, latestVersion) {
30084
30413
  Run \`allagents self update\` to upgrade.`;
30085
30414
  }
30086
30415
  function backgroundUpdateCheck() {
30087
- const dir = join19(getHomeDir(), CONFIG_DIR);
30088
- const filePath = join19(dir, CACHE_FILE);
30416
+ const dir = join20(getHomeDir(), CONFIG_DIR);
30417
+ const filePath = join20(dir, CACHE_FILE);
30089
30418
  const script = `
30090
30419
  const https = require('https');
30091
30420
  const fs = require('fs');
@@ -30136,17 +30465,17 @@ var require_picocolors = __commonJS((exports, module) => {
30136
30465
  var env2 = p.env || {};
30137
30466
  var isColorSupported = !(!!env2.NO_COLOR || argv.includes("--no-color")) && (!!env2.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env2.TERM !== "dumb" || !!env2.CI);
30138
30467
  var formatter = (open, close, replace = open) => (input) => {
30139
- let string3 = "" + input, index = string3.indexOf(close, open.length);
30140
- return ~index ? open + replaceClose(string3, close, replace, index) + close : open + string3 + close;
30468
+ let string4 = "" + input, index = string4.indexOf(close, open.length);
30469
+ return ~index ? open + replaceClose(string4, close, replace, index) + close : open + string4 + close;
30141
30470
  };
30142
- var replaceClose = (string3, close, replace, index) => {
30471
+ var replaceClose = (string4, close, replace, index) => {
30143
30472
  let result = "", cursor = 0;
30144
30473
  do {
30145
- result += string3.substring(cursor, index) + replace;
30474
+ result += string4.substring(cursor, index) + replace;
30146
30475
  cursor = index + close.length;
30147
- index = string3.indexOf(close, cursor);
30476
+ index = string4.indexOf(close, cursor);
30148
30477
  } while (~index);
30149
- return result + string3.substring(cursor);
30478
+ return result + string4.substring(cursor);
30150
30479
  };
30151
30480
  var createColors = (enabled = isColorSupported) => {
30152
30481
  let f = enabled ? formatter : () => String;
@@ -31921,15 +32250,15 @@ class TuiCache {
31921
32250
  }
31922
32251
 
31923
32252
  // src/cli/tui/context.ts
31924
- import { existsSync as existsSync17 } from "node:fs";
31925
- import { join as join20 } from "node:path";
32253
+ import { existsSync as existsSync18 } from "node:fs";
32254
+ import { join as join21 } from "node:path";
31926
32255
  async function getTuiContext(cwd = process.cwd(), cache2) {
31927
32256
  const cachedContext = cache2?.getContext();
31928
32257
  if (cachedContext) {
31929
32258
  return cachedContext;
31930
32259
  }
31931
- const configPath = join20(cwd, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
31932
- const hasWorkspace = existsSync17(configPath) && !isUserConfigPath(cwd);
32260
+ const configPath = join21(cwd, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
32261
+ const hasWorkspace = existsSync18(configPath) && !isUserConfigPath(cwd);
31933
32262
  let projectPluginCount = 0;
31934
32263
  if (hasWorkspace) {
31935
32264
  try {
@@ -32563,7 +32892,7 @@ __export(exports_wizard, {
32563
32892
  runWizard: () => runWizard,
32564
32893
  buildMenuOptions: () => buildMenuOptions
32565
32894
  });
32566
- import { relative as relative3 } from "node:path";
32895
+ import { relative as relative4 } from "node:path";
32567
32896
  function buildMenuOptions(context) {
32568
32897
  const options2 = [];
32569
32898
  if (!context.hasWorkspace) {
@@ -32598,7 +32927,7 @@ function buildMenuOptions(context) {
32598
32927
  function buildSummary(context) {
32599
32928
  const lines = [];
32600
32929
  if (context.hasWorkspace && context.workspacePath) {
32601
- const relPath = relative3(process.cwd(), context.workspacePath) || ".";
32930
+ const relPath = relative4(process.cwd(), context.workspacePath) || ".";
32602
32931
  lines.push(`Workspace: ${relPath}`);
32603
32932
  lines.push(`Project plugins: ${context.projectPluginCount}`);
32604
32933
  } else {
@@ -32686,7 +33015,7 @@ var init_wizard = __esm(() => {
32686
33015
  });
32687
33016
 
32688
33017
  // src/cli/index.ts
32689
- var import_cmd_ts5 = __toESM(require_cjs(), 1);
33018
+ var import_cmd_ts6 = __toESM(require_cjs(), 1);
32690
33019
 
32691
33020
  // src/cli/help.ts
32692
33021
  var import_cmd_ts = __toESM(require_cjs(), 1);
@@ -32737,7 +33066,7 @@ init_sync();
32737
33066
  init_status2();
32738
33067
  var import_cmd_ts2 = __toESM(require_cjs(), 1);
32739
33068
  import { existsSync as existsSync16 } from "node:fs";
32740
- import { join as join18, resolve as resolve10 } from "node:path";
33069
+ import { join as join18, resolve as resolve11 } from "node:path";
32741
33070
 
32742
33071
  // src/core/prune.ts
32743
33072
  init_js_yaml();
@@ -33304,7 +33633,7 @@ var repoAddCmd = import_cmd_ts2.command({
33304
33633
  },
33305
33634
  handler: async ({ path: repoPath, description }) => {
33306
33635
  try {
33307
- const resolvedPath = resolve10(process.cwd(), repoPath);
33636
+ const resolvedPath = resolve11(process.cwd(), repoPath);
33308
33637
  const remote = await detectRemote(resolvedPath);
33309
33638
  const result = await addRepository(repoPath, {
33310
33639
  source: remote?.source,
@@ -33459,7 +33788,7 @@ init_marketplace();
33459
33788
  init_sync();
33460
33789
  init_workspace_modify();
33461
33790
  init_user_workspace();
33462
- var import_cmd_ts3 = __toESM(require_cjs(), 1);
33791
+ var import_cmd_ts4 = __toESM(require_cjs(), 1);
33463
33792
 
33464
33793
  // src/cli/metadata/plugin.ts
33465
33794
  var marketplaceListMeta = {
@@ -33626,6 +33955,474 @@ var pluginUninstallMeta = {
33626
33955
  }
33627
33956
  };
33628
33957
 
33958
+ // src/cli/commands/plugin-skills.ts
33959
+ init_sync();
33960
+ init_workspace_modify();
33961
+ init_user_workspace();
33962
+ var import_cmd_ts3 = __toESM(require_cjs(), 1);
33963
+
33964
+ // src/core/skills.ts
33965
+ init_js_yaml();
33966
+ init_constants();
33967
+ init_plugin();
33968
+ init_plugin_path();
33969
+ init_marketplace();
33970
+ import { existsSync as existsSync17 } from "node:fs";
33971
+ import { readFile as readFile13, readdir as readdir4 } from "node:fs/promises";
33972
+ import { join as join19, resolve as resolve12 } from "node:path";
33973
+ async function resolvePluginPath(pluginSource, workspacePath) {
33974
+ if (isPluginSpec(pluginSource)) {
33975
+ const resolved2 = await resolvePluginSpecWithAutoRegister(pluginSource);
33976
+ return resolved2.success ? resolved2.path ?? null : null;
33977
+ }
33978
+ if (isGitHubUrl(pluginSource)) {
33979
+ const parsed = parseGitHubUrl(pluginSource);
33980
+ const result = await fetchPlugin(pluginSource, {
33981
+ offline: true,
33982
+ ...parsed?.branch && { branch: parsed.branch }
33983
+ });
33984
+ if (!result.success)
33985
+ return null;
33986
+ return parsed?.subpath ? join19(result.cachePath, parsed.subpath) : result.cachePath;
33987
+ }
33988
+ const resolved = resolve12(workspacePath, pluginSource);
33989
+ return existsSync17(resolved) ? resolved : null;
33990
+ }
33991
+ async function getAllSkillsFromPlugins(workspacePath = process.cwd()) {
33992
+ const configPath = join19(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
33993
+ if (!existsSync17(configPath)) {
33994
+ return [];
33995
+ }
33996
+ const content = await readFile13(configPath, "utf-8");
33997
+ const config = load(content);
33998
+ const disabledSkills = new Set(config.disabledSkills ?? []);
33999
+ const skills = [];
34000
+ for (const pluginSource of config.plugins) {
34001
+ const pluginPath = await resolvePluginPath(pluginSource, workspacePath);
34002
+ if (!pluginPath)
34003
+ continue;
34004
+ const pluginName = await getPluginName(pluginPath);
34005
+ const skillsDir = join19(pluginPath, "skills");
34006
+ if (!existsSync17(skillsDir))
34007
+ continue;
34008
+ const entries = await readdir4(skillsDir, { withFileTypes: true });
34009
+ const skillDirs = entries.filter((e) => e.isDirectory());
34010
+ for (const entry of skillDirs) {
34011
+ const skillKey = `${pluginName}:${entry.name}`;
34012
+ skills.push({
34013
+ name: entry.name,
34014
+ pluginName,
34015
+ pluginSource,
34016
+ path: join19(skillsDir, entry.name),
34017
+ disabled: disabledSkills.has(skillKey)
34018
+ });
34019
+ }
34020
+ }
34021
+ return skills;
34022
+ }
34023
+ async function findSkillByName(skillName, workspacePath = process.cwd()) {
34024
+ const allSkills = await getAllSkillsFromPlugins(workspacePath);
34025
+ return allSkills.filter((s) => s.name === skillName);
34026
+ }
34027
+
34028
+ // src/cli/metadata/plugin-skills.ts
34029
+ var skillsListMeta = {
34030
+ command: "plugin skills list",
34031
+ description: "List all skills from installed plugins",
34032
+ whenToUse: "To see available skills and their enabled/disabled status",
34033
+ examples: [
34034
+ "allagents plugin skills list",
34035
+ "allagents plugin skills list --scope user"
34036
+ ],
34037
+ expectedOutput: "Lists skills grouped by plugin with enabled/disabled status",
34038
+ options: [
34039
+ { flag: "--scope", short: "-s", type: "string", description: 'Scope: "project" (default) or "user"' }
34040
+ ],
34041
+ outputSchema: {
34042
+ skills: [{ name: "string", plugin: "string", disabled: "boolean" }]
34043
+ }
34044
+ };
34045
+ var skillsRemoveMeta = {
34046
+ command: "plugin skills remove",
34047
+ description: "Disable a skill (exclude from sync)",
34048
+ whenToUse: "To prevent a specific skill from being synced to your workspace",
34049
+ examples: [
34050
+ "allagents plugin skills remove brainstorming",
34051
+ "allagents plugin skills remove brainstorming --plugin superpowers",
34052
+ "allagents plugin skills remove brainstorming --scope user"
34053
+ ],
34054
+ expectedOutput: "Confirms skill was disabled and runs sync",
34055
+ positionals: [
34056
+ { name: "skill", type: "string", required: true, description: "Skill name to disable" }
34057
+ ],
34058
+ options: [
34059
+ { flag: "--scope", short: "-s", type: "string", description: 'Scope: "project" (default) or "user"' },
34060
+ { flag: "--plugin", short: "-p", type: "string", description: "Plugin name (required if skill exists in multiple plugins)" }
34061
+ ],
34062
+ outputSchema: {
34063
+ skill: "string",
34064
+ plugin: "string",
34065
+ syncResult: { copied: "number", failed: "number" }
34066
+ }
34067
+ };
34068
+ var skillsAddMeta = {
34069
+ command: "plugin skills add",
34070
+ description: "Re-enable a previously disabled skill",
34071
+ whenToUse: "To re-enable a skill that was previously disabled",
34072
+ examples: [
34073
+ "allagents plugin skills add brainstorming",
34074
+ "allagents plugin skills add brainstorming --plugin superpowers"
34075
+ ],
34076
+ expectedOutput: "Confirms skill was enabled and runs sync",
34077
+ positionals: [
34078
+ { name: "skill", type: "string", required: true, description: "Skill name to enable" }
34079
+ ],
34080
+ options: [
34081
+ { flag: "--scope", short: "-s", type: "string", description: 'Scope: "project" (default) or "user"' },
34082
+ { flag: "--plugin", short: "-p", type: "string", description: "Plugin name (required if skill exists in multiple plugins)" }
34083
+ ],
34084
+ outputSchema: {
34085
+ skill: "string",
34086
+ plugin: "string",
34087
+ syncResult: { copied: "number", failed: "number" }
34088
+ }
34089
+ };
34090
+
34091
+ // src/cli/commands/plugin-skills.ts
34092
+ init_constants();
34093
+ function groupSkillsByPlugin(skills) {
34094
+ const grouped = new Map;
34095
+ for (const skill of skills) {
34096
+ const existing = grouped.get(skill.pluginName);
34097
+ if (existing) {
34098
+ existing.skills.push({ name: skill.name, disabled: skill.disabled });
34099
+ } else {
34100
+ grouped.set(skill.pluginName, {
34101
+ source: skill.pluginSource,
34102
+ skills: [{ name: skill.name, disabled: skill.disabled }]
34103
+ });
34104
+ }
34105
+ }
34106
+ return grouped;
34107
+ }
34108
+ var listCmd = import_cmd_ts3.command({
34109
+ name: "list",
34110
+ description: buildDescription(skillsListMeta),
34111
+ args: {
34112
+ scope: import_cmd_ts3.option({
34113
+ type: import_cmd_ts3.optional(import_cmd_ts3.string),
34114
+ long: "scope",
34115
+ short: "s",
34116
+ description: 'Scope: "project" (default) or "user"'
34117
+ })
34118
+ },
34119
+ handler: async ({ scope }) => {
34120
+ try {
34121
+ const isUser = scope === "user" || !scope && isUserConfigPath(process.cwd());
34122
+ const workspacePath = isUser ? getHomeDir() : process.cwd();
34123
+ const skills = await getAllSkillsFromPlugins(workspacePath);
34124
+ if (isJsonMode()) {
34125
+ jsonOutput({
34126
+ success: true,
34127
+ command: "plugin skills list",
34128
+ data: {
34129
+ scope: isUser ? "user" : "project",
34130
+ skills: skills.map((s) => ({
34131
+ name: s.name,
34132
+ plugin: s.pluginName,
34133
+ disabled: s.disabled
34134
+ }))
34135
+ }
34136
+ });
34137
+ return;
34138
+ }
34139
+ if (skills.length === 0) {
34140
+ console.log("No skills found. Install a plugin first with:");
34141
+ console.log(" allagents plugin install <plugin>");
34142
+ return;
34143
+ }
34144
+ const grouped = groupSkillsByPlugin(skills);
34145
+ for (const [pluginName, data] of grouped) {
34146
+ console.log(`
34147
+ ${pluginName} (${data.source}):`);
34148
+ for (const skill of data.skills) {
34149
+ const icon = skill.disabled ? "✗" : "✓";
34150
+ const status = skill.disabled ? " (disabled)" : "";
34151
+ console.log(` ${icon} ${skill.name}${status}`);
34152
+ }
34153
+ }
34154
+ console.log();
34155
+ } catch (error) {
34156
+ if (error instanceof Error) {
34157
+ if (isJsonMode()) {
34158
+ jsonOutput({ success: false, command: "plugin skills list", error: error.message });
34159
+ process.exit(1);
34160
+ }
34161
+ console.error(`Error: ${error.message}`);
34162
+ process.exit(1);
34163
+ }
34164
+ throw error;
34165
+ }
34166
+ }
34167
+ });
34168
+ var removeCmd = import_cmd_ts3.command({
34169
+ name: "remove",
34170
+ description: buildDescription(skillsRemoveMeta),
34171
+ args: {
34172
+ skill: import_cmd_ts3.positional({ type: import_cmd_ts3.string, displayName: "skill" }),
34173
+ scope: import_cmd_ts3.option({
34174
+ type: import_cmd_ts3.optional(import_cmd_ts3.string),
34175
+ long: "scope",
34176
+ short: "s",
34177
+ description: 'Scope: "project" (default) or "user"'
34178
+ }),
34179
+ plugin: import_cmd_ts3.option({
34180
+ type: import_cmd_ts3.optional(import_cmd_ts3.string),
34181
+ long: "plugin",
34182
+ short: "p",
34183
+ description: "Plugin name (required if skill exists in multiple plugins)"
34184
+ })
34185
+ },
34186
+ handler: async ({ skill, scope, plugin }) => {
34187
+ try {
34188
+ const isUser = scope === "user" || !scope && isUserConfigPath(process.cwd());
34189
+ const workspacePath = isUser ? getHomeDir() : process.cwd();
34190
+ const matches = await findSkillByName(skill, workspacePath);
34191
+ if (matches.length === 0) {
34192
+ const allSkills = await getAllSkillsFromPlugins(workspacePath);
34193
+ const skillNames = [...new Set(allSkills.map((s) => s.name))].join(", ");
34194
+ const error = `Skill '${skill}' not found in any installed plugin.
34195
+
34196
+ Available skills: ${skillNames || "none"}`;
34197
+ if (isJsonMode()) {
34198
+ jsonOutput({ success: false, command: "plugin skills remove", error });
34199
+ process.exit(1);
34200
+ }
34201
+ console.error(`Error: ${error}`);
34202
+ process.exit(1);
34203
+ }
34204
+ let targetSkill = matches[0];
34205
+ if (!targetSkill) {
34206
+ throw new Error("Unexpected empty matches array");
34207
+ }
34208
+ if (matches.length > 1) {
34209
+ if (!plugin) {
34210
+ const pluginList = matches.map((m) => ` - ${m.pluginName} (${m.pluginSource})`).join(`
34211
+ `);
34212
+ const error = `'${skill}' exists in multiple plugins:
34213
+ ${pluginList}
34214
+
34215
+ Use --plugin to specify: allagents plugin skills remove ${skill} --plugin <name>`;
34216
+ if (isJsonMode()) {
34217
+ jsonOutput({ success: false, command: "plugin skills remove", error });
34218
+ process.exit(1);
34219
+ }
34220
+ console.error(`Error: ${error}`);
34221
+ process.exit(1);
34222
+ }
34223
+ const filtered = matches.find((m) => m.pluginName === plugin);
34224
+ if (!filtered) {
34225
+ const error = `Plugin '${plugin}' not found. Installed plugins: ${matches.map((m) => m.pluginName).join(", ")}`;
34226
+ if (isJsonMode()) {
34227
+ jsonOutput({ success: false, command: "plugin skills remove", error });
34228
+ process.exit(1);
34229
+ }
34230
+ console.error(`Error: ${error}`);
34231
+ process.exit(1);
34232
+ }
34233
+ targetSkill = filtered;
34234
+ }
34235
+ if (targetSkill.disabled) {
34236
+ const msg = `Skill '${skill}' is already disabled.`;
34237
+ if (isJsonMode()) {
34238
+ jsonOutput({ success: false, command: "plugin skills remove", error: msg });
34239
+ process.exit(1);
34240
+ }
34241
+ console.log(msg);
34242
+ return;
34243
+ }
34244
+ const skillKey = `${targetSkill.pluginName}:${skill}`;
34245
+ const result = isUser ? await addUserDisabledSkill(skillKey) : await addDisabledSkill(skillKey, workspacePath);
34246
+ if (!result.success) {
34247
+ if (isJsonMode()) {
34248
+ jsonOutput({ success: false, command: "plugin skills remove", error: result.error ?? "Unknown error" });
34249
+ process.exit(1);
34250
+ }
34251
+ console.error(`Error: ${result.error}`);
34252
+ process.exit(1);
34253
+ }
34254
+ if (!isJsonMode()) {
34255
+ console.log(`✓ Disabled skill: ${skill} (${targetSkill.pluginName})`);
34256
+ console.log(`
34257
+ Syncing workspace...
34258
+ `);
34259
+ }
34260
+ const syncResult = isUser ? await syncUserWorkspace() : await syncWorkspace(workspacePath);
34261
+ if (isJsonMode()) {
34262
+ jsonOutput({
34263
+ success: syncResult.success,
34264
+ command: "plugin skills remove",
34265
+ data: {
34266
+ skill,
34267
+ plugin: targetSkill.pluginName,
34268
+ syncResult: {
34269
+ copied: syncResult.totalCopied,
34270
+ failed: syncResult.totalFailed
34271
+ }
34272
+ }
34273
+ });
34274
+ if (!syncResult.success)
34275
+ process.exit(1);
34276
+ return;
34277
+ }
34278
+ console.log("Sync complete.");
34279
+ } catch (error) {
34280
+ if (error instanceof Error) {
34281
+ if (isJsonMode()) {
34282
+ jsonOutput({ success: false, command: "plugin skills remove", error: error.message });
34283
+ process.exit(1);
34284
+ }
34285
+ console.error(`Error: ${error.message}`);
34286
+ process.exit(1);
34287
+ }
34288
+ throw error;
34289
+ }
34290
+ }
34291
+ });
34292
+ var addCmd = import_cmd_ts3.command({
34293
+ name: "add",
34294
+ description: buildDescription(skillsAddMeta),
34295
+ args: {
34296
+ skill: import_cmd_ts3.positional({ type: import_cmd_ts3.string, displayName: "skill" }),
34297
+ scope: import_cmd_ts3.option({
34298
+ type: import_cmd_ts3.optional(import_cmd_ts3.string),
34299
+ long: "scope",
34300
+ short: "s",
34301
+ description: 'Scope: "project" (default) or "user"'
34302
+ }),
34303
+ plugin: import_cmd_ts3.option({
34304
+ type: import_cmd_ts3.optional(import_cmd_ts3.string),
34305
+ long: "plugin",
34306
+ short: "p",
34307
+ description: "Plugin name (required if skill exists in multiple plugins)"
34308
+ })
34309
+ },
34310
+ handler: async ({ skill, scope, plugin }) => {
34311
+ try {
34312
+ const isUser = scope === "user" || !scope && isUserConfigPath(process.cwd());
34313
+ const workspacePath = isUser ? getHomeDir() : process.cwd();
34314
+ const matches = await findSkillByName(skill, workspacePath);
34315
+ if (matches.length === 0) {
34316
+ const allSkills = await getAllSkillsFromPlugins(workspacePath);
34317
+ const skillNames = [...new Set(allSkills.map((s) => s.name))].join(", ");
34318
+ const error = `Skill '${skill}' not found in any installed plugin.
34319
+
34320
+ Available skills: ${skillNames || "none"}`;
34321
+ if (isJsonMode()) {
34322
+ jsonOutput({ success: false, command: "plugin skills add", error });
34323
+ process.exit(1);
34324
+ }
34325
+ console.error(`Error: ${error}`);
34326
+ process.exit(1);
34327
+ }
34328
+ let targetSkill = matches[0];
34329
+ if (!targetSkill) {
34330
+ throw new Error("Unexpected empty matches array");
34331
+ }
34332
+ if (matches.length > 1) {
34333
+ if (!plugin) {
34334
+ const pluginList = matches.map((m) => ` - ${m.pluginName} (${m.pluginSource})`).join(`
34335
+ `);
34336
+ const error = `'${skill}' exists in multiple plugins:
34337
+ ${pluginList}
34338
+
34339
+ Use --plugin to specify: allagents plugin skills add ${skill} --plugin <name>`;
34340
+ if (isJsonMode()) {
34341
+ jsonOutput({ success: false, command: "plugin skills add", error });
34342
+ process.exit(1);
34343
+ }
34344
+ console.error(`Error: ${error}`);
34345
+ process.exit(1);
34346
+ }
34347
+ const filtered = matches.find((m) => m.pluginName === plugin);
34348
+ if (!filtered) {
34349
+ const error = `Plugin '${plugin}' not found. Installed plugins: ${matches.map((m) => m.pluginName).join(", ")}`;
34350
+ if (isJsonMode()) {
34351
+ jsonOutput({ success: false, command: "plugin skills add", error });
34352
+ process.exit(1);
34353
+ }
34354
+ console.error(`Error: ${error}`);
34355
+ process.exit(1);
34356
+ }
34357
+ targetSkill = filtered;
34358
+ }
34359
+ if (!targetSkill.disabled) {
34360
+ const msg = `Skill '${skill}' is already enabled.`;
34361
+ if (isJsonMode()) {
34362
+ jsonOutput({ success: false, command: "plugin skills add", error: msg });
34363
+ process.exit(1);
34364
+ }
34365
+ console.log(msg);
34366
+ return;
34367
+ }
34368
+ const skillKey = `${targetSkill.pluginName}:${skill}`;
34369
+ const result = isUser ? await removeUserDisabledSkill(skillKey) : await removeDisabledSkill(skillKey, workspacePath);
34370
+ if (!result.success) {
34371
+ if (isJsonMode()) {
34372
+ jsonOutput({ success: false, command: "plugin skills add", error: result.error ?? "Unknown error" });
34373
+ process.exit(1);
34374
+ }
34375
+ console.error(`Error: ${result.error}`);
34376
+ process.exit(1);
34377
+ }
34378
+ if (!isJsonMode()) {
34379
+ console.log(`✓ Enabled skill: ${skill} (${targetSkill.pluginName})`);
34380
+ console.log(`
34381
+ Syncing workspace...
34382
+ `);
34383
+ }
34384
+ const syncResult = isUser ? await syncUserWorkspace() : await syncWorkspace(workspacePath);
34385
+ if (isJsonMode()) {
34386
+ jsonOutput({
34387
+ success: syncResult.success,
34388
+ command: "plugin skills add",
34389
+ data: {
34390
+ skill,
34391
+ plugin: targetSkill.pluginName,
34392
+ syncResult: {
34393
+ copied: syncResult.totalCopied,
34394
+ failed: syncResult.totalFailed
34395
+ }
34396
+ }
34397
+ });
34398
+ if (!syncResult.success)
34399
+ process.exit(1);
34400
+ return;
34401
+ }
34402
+ console.log("Sync complete.");
34403
+ } catch (error) {
34404
+ if (error instanceof Error) {
34405
+ if (isJsonMode()) {
34406
+ jsonOutput({ success: false, command: "plugin skills add", error: error.message });
34407
+ process.exit(1);
34408
+ }
34409
+ console.error(`Error: ${error.message}`);
34410
+ process.exit(1);
34411
+ }
34412
+ throw error;
34413
+ }
34414
+ }
34415
+ });
34416
+ var skillsCmd = conciseSubcommands({
34417
+ name: "skills",
34418
+ description: "Manage individual skills from plugins",
34419
+ cmds: {
34420
+ list: listCmd,
34421
+ remove: removeCmd,
34422
+ add: addCmd
34423
+ }
34424
+ });
34425
+
33629
34426
  // src/cli/commands/plugin.ts
33630
34427
  function buildSyncData2(result) {
33631
34428
  return {
@@ -33745,7 +34542,7 @@ User sync complete:`);
33745
34542
  }
33746
34543
  return { ok: result.success && result.totalFailed === 0, syncData };
33747
34544
  }
33748
- var marketplaceListCmd = import_cmd_ts3.command({
34545
+ var marketplaceListCmd = import_cmd_ts4.command({
33749
34546
  name: "list",
33750
34547
  description: buildDescription(marketplaceListMeta),
33751
34548
  args: {},
@@ -33766,11 +34563,9 @@ var marketplaceListCmd = import_cmd_ts3.command({
33766
34563
  console.log("Add a marketplace with:");
33767
34564
  console.log(` allagents plugin marketplace add <source>
33768
34565
  `);
33769
- console.log("Well-known marketplaces:");
33770
- const wellKnown = getWellKnownMarketplaces();
33771
- for (const [name, repo] of Object.entries(wellKnown)) {
33772
- console.log(` ${name} → ${repo}`);
33773
- }
34566
+ console.log("Examples:");
34567
+ console.log(" allagents plugin marketplace add owner/repo");
34568
+ console.log(" allagents plugin marketplace add https://github.com/owner/repo");
33774
34569
  return;
33775
34570
  }
33776
34571
  console.log(`Registered marketplaces:
@@ -33798,13 +34593,13 @@ var marketplaceListCmd = import_cmd_ts3.command({
33798
34593
  }
33799
34594
  }
33800
34595
  });
33801
- var marketplaceAddCmd = import_cmd_ts3.command({
34596
+ var marketplaceAddCmd = import_cmd_ts4.command({
33802
34597
  name: "add",
33803
34598
  description: buildDescription(marketplaceAddMeta),
33804
34599
  args: {
33805
- source: import_cmd_ts3.positional({ type: import_cmd_ts3.string, displayName: "source" }),
33806
- name: import_cmd_ts3.option({ type: import_cmd_ts3.optional(import_cmd_ts3.string), long: "name", short: "n", description: "Custom name for the marketplace" }),
33807
- branch: import_cmd_ts3.option({ type: import_cmd_ts3.optional(import_cmd_ts3.string), long: "branch", short: "b", description: "Branch to checkout after cloning" })
34600
+ source: import_cmd_ts4.positional({ type: import_cmd_ts4.string, displayName: "source" }),
34601
+ name: import_cmd_ts4.option({ type: import_cmd_ts4.optional(import_cmd_ts4.string), long: "name", short: "n", description: "Custom name for the marketplace" }),
34602
+ branch: import_cmd_ts4.option({ type: import_cmd_ts4.optional(import_cmd_ts4.string), long: "branch", short: "b", description: "Branch to checkout after cloning" })
33808
34603
  },
33809
34604
  handler: async ({ source, name, branch }) => {
33810
34605
  try {
@@ -33849,11 +34644,11 @@ Error: ${result.error}`);
33849
34644
  }
33850
34645
  }
33851
34646
  });
33852
- var marketplaceRemoveCmd = import_cmd_ts3.command({
34647
+ var marketplaceRemoveCmd = import_cmd_ts4.command({
33853
34648
  name: "remove",
33854
34649
  description: buildDescription(marketplaceRemoveMeta),
33855
34650
  args: {
33856
- name: import_cmd_ts3.positional({ type: import_cmd_ts3.string, displayName: "name" })
34651
+ name: import_cmd_ts4.positional({ type: import_cmd_ts4.string, displayName: "name" })
33857
34652
  },
33858
34653
  handler: async ({ name }) => {
33859
34654
  try {
@@ -33877,14 +34672,13 @@ var marketplaceRemoveCmd = import_cmd_ts3.command({
33877
34672
  });
33878
34673
  return;
33879
34674
  }
33880
- console.log(`✓ Marketplace '${name}' removed from registry`);
34675
+ console.log(`✓ Marketplace '${name}' removed`);
33881
34676
  if (result.removedUserPlugins && result.removedUserPlugins.length > 0) {
33882
34677
  console.log(` Removed ${result.removedUserPlugins.length} user plugin(s):`);
33883
34678
  for (const p of result.removedUserPlugins) {
33884
34679
  console.log(` - ${p}`);
33885
34680
  }
33886
34681
  }
33887
- console.log(` Note: Files at ${result.marketplace?.path} were not deleted`);
33888
34682
  } catch (error) {
33889
34683
  if (error instanceof Error) {
33890
34684
  if (isJsonMode()) {
@@ -33898,11 +34692,11 @@ var marketplaceRemoveCmd = import_cmd_ts3.command({
33898
34692
  }
33899
34693
  }
33900
34694
  });
33901
- var marketplaceUpdateCmd = import_cmd_ts3.command({
34695
+ var marketplaceUpdateCmd = import_cmd_ts4.command({
33902
34696
  name: "update",
33903
34697
  description: buildDescription(marketplaceUpdateMeta),
33904
34698
  args: {
33905
- name: import_cmd_ts3.positional({ type: import_cmd_ts3.optional(import_cmd_ts3.string), displayName: "name" })
34699
+ name: import_cmd_ts4.positional({ type: import_cmd_ts4.optional(import_cmd_ts4.string), displayName: "name" })
33906
34700
  },
33907
34701
  handler: async ({ name }) => {
33908
34702
  try {
@@ -33968,11 +34762,11 @@ var marketplaceCmd = conciseSubcommands({
33968
34762
  update: marketplaceUpdateCmd
33969
34763
  }
33970
34764
  });
33971
- var pluginListCmd = import_cmd_ts3.command({
34765
+ var pluginListCmd = import_cmd_ts4.command({
33972
34766
  name: "list",
33973
34767
  description: buildDescription(pluginListMeta),
33974
34768
  args: {
33975
- marketplace: import_cmd_ts3.positional({ type: import_cmd_ts3.optional(import_cmd_ts3.string), displayName: "marketplace" })
34769
+ marketplace: import_cmd_ts4.positional({ type: import_cmd_ts4.optional(import_cmd_ts4.string), displayName: "marketplace" })
33976
34770
  },
33977
34771
  handler: async ({ marketplace }) => {
33978
34772
  try {
@@ -34001,13 +34795,36 @@ var pluginListCmd = import_cmd_ts3.command({
34001
34795
  console.error(`Marketplace '${marketplace}' not found`);
34002
34796
  process.exit(1);
34003
34797
  }
34798
+ const userPlugins = await getInstalledUserPlugins();
34799
+ const projectPlugins = await getInstalledProjectPlugins(process.cwd());
34800
+ const installedMap = new Map;
34801
+ for (const p of userPlugins) {
34802
+ installedMap.set(`${p.name}@${p.marketplace}`, p);
34803
+ }
34804
+ for (const p of projectPlugins) {
34805
+ installedMap.set(`${p.name}@${p.marketplace}`, p);
34806
+ }
34807
+ const versionMap = new Map;
34808
+ for (const mp of toList) {
34809
+ const version = await getMarketplaceVersion(mp.path);
34810
+ versionMap.set(mp.name, version);
34811
+ }
34004
34812
  if (isJsonMode()) {
34005
34813
  const allPlugins = [];
34006
34814
  const allWarnings = [];
34007
34815
  for (const mp of toList) {
34008
34816
  const result = await listMarketplacePlugins(mp.name);
34817
+ const version = versionMap.get(mp.name) ?? null;
34009
34818
  for (const plugin of result.plugins) {
34010
- allPlugins.push({ name: plugin.name, marketplace: mp.name });
34819
+ const key = `${plugin.name}@${mp.name}`;
34820
+ const installed = installedMap.get(key);
34821
+ allPlugins.push({
34822
+ name: plugin.name,
34823
+ marketplace: mp.name,
34824
+ version,
34825
+ scope: installed?.scope ?? null,
34826
+ enabled: !!installed
34827
+ });
34011
34828
  }
34012
34829
  for (const warning of result.warnings) {
34013
34830
  allWarnings.push(`${mp.name}: ${warning}`);
@@ -34025,29 +34842,52 @@ var pluginListCmd = import_cmd_ts3.command({
34025
34842
  return;
34026
34843
  }
34027
34844
  let totalPlugins = 0;
34845
+ const allPluginEntries = [];
34028
34846
  for (const mp of toList) {
34029
34847
  const result = await listMarketplacePlugins(mp.name);
34030
- if (result.plugins.length === 0 && result.warnings.length === 0) {
34031
- console.log(`${mp.name}: (no plugins found)`);
34032
- continue;
34033
- }
34034
- console.log(`${mp.name}:`);
34848
+ const version = versionMap.get(mp.name) ?? null;
34035
34849
  for (const warning of result.warnings) {
34036
- console.log(` Warning: ${warning}`);
34037
- }
34038
- if (result.plugins.length === 0) {
34039
- console.log(" (no plugins found)");
34850
+ allPluginEntries.push({
34851
+ name: "",
34852
+ marketplace: mp.name,
34853
+ version,
34854
+ installed: undefined,
34855
+ warning
34856
+ });
34040
34857
  }
34041
34858
  for (const plugin of result.plugins) {
34042
- console.log(` - ${plugin.name}@${mp.name}`);
34859
+ const key = `${plugin.name}@${mp.name}`;
34860
+ const installed = installedMap.get(key);
34861
+ allPluginEntries.push({
34862
+ name: plugin.name,
34863
+ marketplace: mp.name,
34864
+ version,
34865
+ installed
34866
+ });
34043
34867
  totalPlugins++;
34044
34868
  }
34045
- console.log();
34046
34869
  }
34047
34870
  if (totalPlugins === 0) {
34048
34871
  console.log("No plugins found in registered marketplaces.");
34049
- } else {
34050
- console.log(`Total: ${totalPlugins} plugin(s)`);
34872
+ return;
34873
+ }
34874
+ console.log(`Installed plugins:
34875
+ `);
34876
+ for (const entry of allPluginEntries) {
34877
+ if (entry.warning) {
34878
+ console.log(` Warning: ${entry.warning}`);
34879
+ continue;
34880
+ }
34881
+ const isEnabled = !!entry.installed;
34882
+ const statusIcon = isEnabled ? "✓" : "✗";
34883
+ const statusText = isEnabled ? "enabled" : "disabled";
34884
+ console.log(` ❯ ${entry.name}@${entry.marketplace}`);
34885
+ console.log(` Version: ${entry.version ?? "unknown"}`);
34886
+ if (entry.installed) {
34887
+ console.log(` Scope: ${entry.installed.scope}`);
34888
+ }
34889
+ console.log(` Status: ${statusIcon} ${statusText}`);
34890
+ console.log();
34051
34891
  }
34052
34892
  } catch (error) {
34053
34893
  if (error instanceof Error) {
@@ -34062,11 +34902,11 @@ var pluginListCmd = import_cmd_ts3.command({
34062
34902
  }
34063
34903
  }
34064
34904
  });
34065
- var pluginValidateCmd = import_cmd_ts3.command({
34905
+ var pluginValidateCmd = import_cmd_ts4.command({
34066
34906
  name: "validate",
34067
34907
  description: buildDescription(pluginValidateMeta),
34068
34908
  args: {
34069
- path: import_cmd_ts3.positional({ type: import_cmd_ts3.string, displayName: "path" })
34909
+ path: import_cmd_ts4.positional({ type: import_cmd_ts4.string, displayName: "path" })
34070
34910
  },
34071
34911
  handler: async ({ path }) => {
34072
34912
  if (isJsonMode()) {
@@ -34081,12 +34921,12 @@ var pluginValidateCmd = import_cmd_ts3.command({
34081
34921
  console.log("(validation not yet implemented)");
34082
34922
  }
34083
34923
  });
34084
- var pluginInstallCmd = import_cmd_ts3.command({
34924
+ var pluginInstallCmd = import_cmd_ts4.command({
34085
34925
  name: "install",
34086
34926
  description: buildDescription(pluginInstallMeta),
34087
34927
  args: {
34088
- plugin: import_cmd_ts3.positional({ type: import_cmd_ts3.string, displayName: "plugin" }),
34089
- scope: import_cmd_ts3.option({ type: import_cmd_ts3.optional(import_cmd_ts3.string), long: "scope", short: "s", description: 'Installation scope: "project" (default) or "user"' })
34928
+ plugin: import_cmd_ts4.positional({ type: import_cmd_ts4.string, displayName: "plugin" }),
34929
+ scope: import_cmd_ts4.option({ type: import_cmd_ts4.optional(import_cmd_ts4.string), long: "scope", short: "s", description: 'Installation scope: "project" (default) or "user"' })
34090
34930
  },
34091
34931
  handler: async ({ plugin, scope }) => {
34092
34932
  try {
@@ -34139,13 +34979,13 @@ var pluginInstallCmd = import_cmd_ts3.command({
34139
34979
  }
34140
34980
  }
34141
34981
  });
34142
- var pluginUninstallCmd = import_cmd_ts3.command({
34982
+ var pluginUninstallCmd = import_cmd_ts4.command({
34143
34983
  name: "uninstall",
34144
34984
  description: buildDescription(pluginUninstallMeta),
34145
34985
  aliases: ["remove"],
34146
34986
  args: {
34147
- plugin: import_cmd_ts3.positional({ type: import_cmd_ts3.string, displayName: "plugin" }),
34148
- scope: import_cmd_ts3.option({ type: import_cmd_ts3.optional(import_cmd_ts3.string), long: "scope", short: "s", description: 'Installation scope: "project" (default) or "user"' })
34987
+ plugin: import_cmd_ts4.positional({ type: import_cmd_ts4.string, displayName: "plugin" }),
34988
+ scope: import_cmd_ts4.option({ type: import_cmd_ts4.optional(import_cmd_ts4.string), long: "scope", short: "s", description: 'Installation scope: "project" (default) or "user"' })
34149
34989
  },
34150
34990
  handler: async ({ plugin, scope }) => {
34151
34991
  try {
@@ -34275,13 +35115,14 @@ var pluginCmd = conciseSubcommands({
34275
35115
  uninstall: pluginUninstallCmd,
34276
35116
  marketplace: marketplaceCmd,
34277
35117
  list: pluginListCmd,
34278
- validate: pluginValidateCmd
35118
+ validate: pluginValidateCmd,
35119
+ skills: skillsCmd
34279
35120
  }
34280
35121
  });
34281
35122
 
34282
35123
  // src/cli/commands/self.ts
34283
35124
  init_execa();
34284
- var import_cmd_ts4 = __toESM(require_cjs(), 1);
35125
+ var import_cmd_ts5 = __toESM(require_cjs(), 1);
34285
35126
 
34286
35127
  // src/cli/metadata/self.ts
34287
35128
  var updateMeta = {
@@ -34319,12 +35160,12 @@ function detectPackageManager() {
34319
35160
  function getCurrentVersion() {
34320
35161
  return package_default.version;
34321
35162
  }
34322
- var updateCmd = import_cmd_ts4.command({
35163
+ var updateCmd = import_cmd_ts5.command({
34323
35164
  name: "update",
34324
35165
  description: buildDescription(updateMeta),
34325
35166
  args: {
34326
- npm: import_cmd_ts4.flag({ long: "npm", description: "Force update using npm" }),
34327
- bun: import_cmd_ts4.flag({ long: "bun", description: "Force update using bun" })
35167
+ npm: import_cmd_ts5.flag({ long: "npm", description: "Force update using npm" }),
35168
+ bun: import_cmd_ts5.flag({ long: "bun", description: "Force update using bun" })
34328
35169
  },
34329
35170
  handler: async ({ npm, bun }) => {
34330
35171
  try {
@@ -34525,5 +35366,5 @@ if (agentHelp) {
34525
35366
  const { runWizard: runWizard2 } = await Promise.resolve().then(() => (init_wizard(), exports_wizard));
34526
35367
  await runWizard2();
34527
35368
  } else {
34528
- import_cmd_ts5.run(app, finalArgs);
35369
+ import_cmd_ts6.run(app, finalArgs);
34529
35370
  }