allagents 0.11.1 → 0.12.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.
package/dist/index.js CHANGED
@@ -22432,27 +22432,180 @@ var init_marketplace_manifest_parser = __esm(() => {
22432
22432
  init_marketplace_manifest();
22433
22433
  });
22434
22434
 
22435
- // src/core/marketplace.ts
22436
- import { mkdir as mkdir3, readFile as readFile6, readdir as readdir3, writeFile as writeFile2 } from "node:fs/promises";
22435
+ // src/core/user-workspace.ts
22436
+ var exports_user_workspace = {};
22437
+ __export(exports_user_workspace, {
22438
+ removeUserPluginsForMarketplace: () => removeUserPluginsForMarketplace,
22439
+ removeUserPlugin: () => removeUserPlugin,
22440
+ getUserWorkspaceConfigPath: () => getUserWorkspaceConfigPath,
22441
+ getUserWorkspaceConfig: () => getUserWorkspaceConfig,
22442
+ getUserPluginsForMarketplace: () => getUserPluginsForMarketplace,
22443
+ ensureUserWorkspace: () => ensureUserWorkspace,
22444
+ addUserPlugin: () => addUserPlugin
22445
+ });
22446
+ import { readFile as readFile6, writeFile as writeFile2, mkdir as mkdir3 } from "node:fs/promises";
22437
22447
  import { existsSync as existsSync5 } from "node:fs";
22438
- import { basename as basename2, join as join6, resolve as resolve4 } from "node:path";
22448
+ import { join as join6 } from "node:path";
22449
+ function getUserWorkspaceConfigPath() {
22450
+ return join6(getAllagentsDir(), WORKSPACE_CONFIG_FILE);
22451
+ }
22452
+ async function ensureUserWorkspace() {
22453
+ const configPath = getUserWorkspaceConfigPath();
22454
+ if (existsSync5(configPath))
22455
+ return;
22456
+ const defaultConfig = {
22457
+ repositories: [],
22458
+ plugins: [],
22459
+ clients: [...ALL_CLIENTS]
22460
+ };
22461
+ await mkdir3(getAllagentsDir(), { recursive: true });
22462
+ await writeFile2(configPath, dump(defaultConfig, { lineWidth: -1 }), "utf-8");
22463
+ }
22464
+ async function getUserWorkspaceConfig() {
22465
+ const configPath = getUserWorkspaceConfigPath();
22466
+ if (!existsSync5(configPath))
22467
+ return null;
22468
+ try {
22469
+ const content = await readFile6(configPath, "utf-8");
22470
+ return load(content);
22471
+ } catch {
22472
+ return null;
22473
+ }
22474
+ }
22475
+ async function addUserPlugin(plugin) {
22476
+ await ensureUserWorkspace();
22477
+ const configPath = getUserWorkspaceConfigPath();
22478
+ if (isPluginSpec(plugin)) {
22479
+ const resolved = await resolvePluginSpecWithAutoRegister(plugin);
22480
+ if (!resolved.success) {
22481
+ return { success: false, error: resolved.error || "Unknown error" };
22482
+ }
22483
+ const normalizedPlugin = resolved.registeredAs ? plugin.replace(/@[^@]+$/, `@${resolved.registeredAs}`) : plugin;
22484
+ return addPluginToUserConfig(normalizedPlugin, configPath, resolved.registeredAs);
22485
+ }
22486
+ if (isGitHubUrl(plugin)) {
22487
+ const validation = validatePluginSource(plugin);
22488
+ if (!validation.valid) {
22489
+ return { success: false, error: validation.error || "Invalid GitHub URL" };
22490
+ }
22491
+ const verifyResult = await verifyGitHubUrlExists(plugin);
22492
+ if (!verifyResult.exists) {
22493
+ return { success: false, error: verifyResult.error || `GitHub URL not found: ${plugin}` };
22494
+ }
22495
+ } else {
22496
+ if (!existsSync5(plugin)) {
22497
+ return {
22498
+ success: false,
22499
+ error: `Plugin not found at ${plugin}`
22500
+ };
22501
+ }
22502
+ }
22503
+ return addPluginToUserConfig(plugin, configPath);
22504
+ }
22505
+ async function removeUserPlugin(plugin) {
22506
+ await ensureUserWorkspace();
22507
+ const configPath = getUserWorkspaceConfigPath();
22508
+ try {
22509
+ const content = await readFile6(configPath, "utf-8");
22510
+ const config = load(content);
22511
+ let index = config.plugins.indexOf(plugin);
22512
+ if (index === -1) {
22513
+ index = config.plugins.findIndex((p) => p.startsWith(`${plugin}@`) || p === plugin);
22514
+ }
22515
+ if (index === -1) {
22516
+ return { success: false, error: `Plugin not found in user config: ${plugin}` };
22517
+ }
22518
+ config.plugins.splice(index, 1);
22519
+ await writeFile2(configPath, dump(config, { lineWidth: -1 }), "utf-8");
22520
+ return { success: true };
22521
+ } catch (error) {
22522
+ return {
22523
+ success: false,
22524
+ error: error instanceof Error ? error.message : String(error)
22525
+ };
22526
+ }
22527
+ }
22528
+ async function getUserPluginsForMarketplace(marketplaceName) {
22529
+ const config = await getUserWorkspaceConfig();
22530
+ if (!config)
22531
+ return [];
22532
+ return config.plugins.filter((p) => {
22533
+ const parsed = parsePluginSpec(p);
22534
+ return parsed?.marketplaceName === marketplaceName;
22535
+ });
22536
+ }
22537
+ async function removeUserPluginsForMarketplace(marketplaceName) {
22538
+ const config = await getUserWorkspaceConfig();
22539
+ if (!config)
22540
+ return [];
22541
+ const matching = config.plugins.filter((p) => {
22542
+ const parsed = parsePluginSpec(p);
22543
+ return parsed?.marketplaceName === marketplaceName;
22544
+ });
22545
+ if (matching.length === 0)
22546
+ return [];
22547
+ const configPath = getUserWorkspaceConfigPath();
22548
+ config.plugins = config.plugins.filter((p) => !matching.includes(p));
22549
+ await writeFile2(configPath, dump(config, { lineWidth: -1 }), "utf-8");
22550
+ return matching;
22551
+ }
22552
+ async function addPluginToUserConfig(plugin, configPath, autoRegistered) {
22553
+ try {
22554
+ const content = await readFile6(configPath, "utf-8");
22555
+ const config = load(content);
22556
+ if (config.plugins.includes(plugin)) {
22557
+ return { success: false, error: `Plugin already exists in user config: ${plugin}` };
22558
+ }
22559
+ config.plugins.push(plugin);
22560
+ await writeFile2(configPath, dump(config, { lineWidth: -1 }), "utf-8");
22561
+ return { success: true, ...autoRegistered && { autoRegistered } };
22562
+ } catch (error) {
22563
+ return {
22564
+ success: false,
22565
+ error: error instanceof Error ? error.message : String(error)
22566
+ };
22567
+ }
22568
+ }
22569
+ var ALL_CLIENTS;
22570
+ var init_user_workspace = __esm(() => {
22571
+ init_js_yaml();
22572
+ init_constants();
22573
+ init_marketplace();
22574
+ init_marketplace();
22575
+ init_plugin_path();
22576
+ ALL_CLIENTS = [
22577
+ "claude",
22578
+ "copilot",
22579
+ "codex",
22580
+ "cursor",
22581
+ "opencode",
22582
+ "gemini",
22583
+ "factory",
22584
+ "ampcode"
22585
+ ];
22586
+ });
22587
+
22588
+ // src/core/marketplace.ts
22589
+ import { mkdir as mkdir4, readFile as readFile7, readdir as readdir3, writeFile as writeFile3 } from "node:fs/promises";
22590
+ import { existsSync as existsSync6 } from "node:fs";
22591
+ import { basename as basename2, join as join7, resolve as resolve4 } from "node:path";
22439
22592
  function getAllagentsDir() {
22440
22593
  const homeDir = process.env.HOME || process.env.USERPROFILE || "~";
22441
22594
  return resolve4(homeDir, ".allagents");
22442
22595
  }
22443
22596
  function getMarketplacesDir() {
22444
- return join6(getAllagentsDir(), "marketplaces");
22597
+ return join7(getAllagentsDir(), "marketplaces");
22445
22598
  }
22446
22599
  function getRegistryPath() {
22447
- return join6(getAllagentsDir(), "marketplaces.json");
22600
+ return join7(getAllagentsDir(), "marketplaces.json");
22448
22601
  }
22449
22602
  async function loadRegistry() {
22450
22603
  const registryPath = getRegistryPath();
22451
- if (!existsSync5(registryPath)) {
22604
+ if (!existsSync6(registryPath)) {
22452
22605
  return { version: 1, marketplaces: {} };
22453
22606
  }
22454
22607
  try {
22455
- const content = await readFile6(registryPath, "utf-8");
22608
+ const content = await readFile7(registryPath, "utf-8");
22456
22609
  return JSON.parse(content);
22457
22610
  } catch {
22458
22611
  return { version: 1, marketplaces: {} };
@@ -22461,10 +22614,10 @@ async function loadRegistry() {
22461
22614
  async function saveRegistry(registry) {
22462
22615
  const registryPath = getRegistryPath();
22463
22616
  const dir = getAllagentsDir();
22464
- if (!existsSync5(dir)) {
22465
- await mkdir3(dir, { recursive: true });
22617
+ if (!existsSync6(dir)) {
22618
+ await mkdir4(dir, { recursive: true });
22466
22619
  }
22467
- await writeFile2(registryPath, `${JSON.stringify(registry, null, 2)}
22620
+ await writeFile3(registryPath, `${JSON.stringify(registry, null, 2)}
22468
22621
  `);
22469
22622
  }
22470
22623
  function parseMarketplaceSource(source) {
@@ -22529,8 +22682,8 @@ async function addMarketplace(source, customName) {
22529
22682
  }
22530
22683
  let marketplacePath;
22531
22684
  if (parsed.type === "github") {
22532
- marketplacePath = join6(getMarketplacesDir(), name);
22533
- if (existsSync5(marketplacePath)) {} else {
22685
+ marketplacePath = join7(getMarketplacesDir(), name);
22686
+ if (existsSync6(marketplacePath)) {} else {
22534
22687
  try {
22535
22688
  await execa("gh", ["--version"]);
22536
22689
  } catch {
@@ -22541,8 +22694,8 @@ async function addMarketplace(source, customName) {
22541
22694
  };
22542
22695
  }
22543
22696
  const parentDir = getMarketplacesDir();
22544
- if (!existsSync5(parentDir)) {
22545
- await mkdir3(parentDir, { recursive: true });
22697
+ if (!existsSync6(parentDir)) {
22698
+ await mkdir4(parentDir, { recursive: true });
22546
22699
  }
22547
22700
  try {
22548
22701
  await execa("gh", ["repo", "clone", parsed.location, marketplacePath]);
@@ -22562,7 +22715,7 @@ async function addMarketplace(source, customName) {
22562
22715
  }
22563
22716
  } else {
22564
22717
  marketplacePath = parsed.location;
22565
- if (!existsSync5(marketplacePath)) {
22718
+ if (!existsSync6(marketplacePath)) {
22566
22719
  return {
22567
22720
  success: false,
22568
22721
  error: `Local directory not found: ${marketplacePath}`
@@ -22612,9 +22765,12 @@ async function removeMarketplace(name) {
22612
22765
  const entry = registry.marketplaces[name];
22613
22766
  delete registry.marketplaces[name];
22614
22767
  await saveRegistry(registry);
22768
+ const { removeUserPluginsForMarketplace: removeUserPluginsForMarketplace2 } = await Promise.resolve().then(() => (init_user_workspace(), exports_user_workspace));
22769
+ const removedUserPlugins = await removeUserPluginsForMarketplace2(name);
22615
22770
  return {
22616
22771
  success: true,
22617
- marketplace: entry
22772
+ marketplace: entry,
22773
+ removedUserPlugins
22618
22774
  };
22619
22775
  }
22620
22776
  async function listMarketplaces() {
@@ -22640,7 +22796,7 @@ async function updateMarketplace(name) {
22640
22796
  });
22641
22797
  continue;
22642
22798
  }
22643
- if (!existsSync5(marketplace.path)) {
22799
+ if (!existsSync6(marketplace.path)) {
22644
22800
  results.push({
22645
22801
  name: marketplace.name,
22646
22802
  success: false,
@@ -22714,15 +22870,15 @@ async function listMarketplacePlugins(name) {
22714
22870
  if (manifestResult.plugins.length > 0) {
22715
22871
  return manifestResult;
22716
22872
  }
22717
- const pluginsDir = join6(marketplace.path, "plugins");
22718
- if (!existsSync5(pluginsDir)) {
22873
+ const pluginsDir = join7(marketplace.path, "plugins");
22874
+ if (!existsSync6(pluginsDir)) {
22719
22875
  return { plugins: [], warnings: manifestResult.warnings };
22720
22876
  }
22721
22877
  try {
22722
22878
  const entries = await readdir3(pluginsDir, { withFileTypes: true });
22723
22879
  const plugins = entries.filter((e) => e.isDirectory()).map((e) => ({
22724
22880
  name: e.name,
22725
- path: join6(pluginsDir, e.name)
22881
+ path: join7(pluginsDir, e.name)
22726
22882
  })).sort((a, b) => a.name.localeCompare(b.name));
22727
22883
  return { plugins, warnings: manifestResult.warnings };
22728
22884
  } catch {
@@ -22779,7 +22935,7 @@ async function resolvePluginSpec(spec, options2 = {}) {
22779
22935
  if (pluginEntry) {
22780
22936
  if (typeof pluginEntry.source === "string") {
22781
22937
  const resolvedPath = resolve4(marketplacePath, pluginEntry.source);
22782
- if (existsSync5(resolvedPath)) {
22938
+ if (existsSync6(resolvedPath)) {
22783
22939
  return {
22784
22940
  path: resolvedPath,
22785
22941
  marketplace: marketplaceName,
@@ -22800,8 +22956,8 @@ async function resolvePluginSpec(spec, options2 = {}) {
22800
22956
  }
22801
22957
  }
22802
22958
  const subpath = options2.subpath ?? parsed.subpath ?? "plugins";
22803
- const pluginPath = join6(marketplacePath, subpath, parsed.plugin);
22804
- if (!existsSync5(pluginPath)) {
22959
+ const pluginPath = join7(marketplacePath, subpath, parsed.plugin);
22960
+ if (!existsSync6(pluginPath)) {
22805
22961
  return null;
22806
22962
  }
22807
22963
  return {
@@ -22921,19 +23077,19 @@ var init_sync_state = __esm(() => {
22921
23077
  });
22922
23078
 
22923
23079
  // src/core/sync-state.ts
22924
- import { readFile as readFile7, writeFile as writeFile3, mkdir as mkdir4 } from "node:fs/promises";
22925
- import { existsSync as existsSync6 } from "node:fs";
22926
- import { join as join7, dirname as dirname3 } from "node:path";
23080
+ import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir5 } from "node:fs/promises";
23081
+ import { existsSync as existsSync7 } from "node:fs";
23082
+ import { join as join8, dirname as dirname3 } from "node:path";
22927
23083
  function getSyncStatePath(workspacePath) {
22928
- return join7(workspacePath, CONFIG_DIR, SYNC_STATE_FILE);
23084
+ return join8(workspacePath, CONFIG_DIR, SYNC_STATE_FILE);
22929
23085
  }
22930
23086
  async function loadSyncState(workspacePath) {
22931
23087
  const statePath = getSyncStatePath(workspacePath);
22932
- if (!existsSync6(statePath)) {
23088
+ if (!existsSync7(statePath)) {
22933
23089
  return null;
22934
23090
  }
22935
23091
  try {
22936
- const content = await readFile7(statePath, "utf-8");
23092
+ const content = await readFile8(statePath, "utf-8");
22937
23093
  const parsed = JSON.parse(content);
22938
23094
  const result = SyncStateSchema.safeParse(parsed);
22939
23095
  if (!result.success) {
@@ -22951,8 +23107,8 @@ async function saveSyncState(workspacePath, files) {
22951
23107
  lastSync: new Date().toISOString(),
22952
23108
  files
22953
23109
  };
22954
- await mkdir4(dirname3(statePath), { recursive: true });
22955
- await writeFile3(statePath, JSON.stringify(state, null, 2), "utf-8");
23110
+ await mkdir5(dirname3(statePath), { recursive: true });
23111
+ await writeFile4(statePath, JSON.stringify(state, null, 2), "utf-8");
22956
23112
  }
22957
23113
  function getPreviouslySyncedFiles(state, client) {
22958
23114
  if (!state) {
@@ -22965,129 +23121,24 @@ var init_sync_state2 = __esm(() => {
22965
23121
  init_sync_state();
22966
23122
  });
22967
23123
 
22968
- // src/core/user-workspace.ts
22969
- import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir5 } from "node:fs/promises";
22970
- import { existsSync as existsSync7 } from "node:fs";
22971
- import { join as join8 } from "node:path";
22972
- function getUserWorkspaceConfigPath() {
22973
- return join8(getAllagentsDir(), WORKSPACE_CONFIG_FILE);
22974
- }
22975
- async function ensureUserWorkspace() {
22976
- const configPath = getUserWorkspaceConfigPath();
22977
- if (existsSync7(configPath))
22978
- return;
22979
- const defaultConfig = {
22980
- repositories: [],
22981
- plugins: [],
22982
- clients: [...ALL_CLIENTS]
22983
- };
22984
- await mkdir5(getAllagentsDir(), { recursive: true });
22985
- await writeFile4(configPath, dump(defaultConfig, { lineWidth: -1 }), "utf-8");
22986
- }
22987
- async function getUserWorkspaceConfig() {
22988
- const configPath = getUserWorkspaceConfigPath();
22989
- if (!existsSync7(configPath))
22990
- return null;
22991
- try {
22992
- const content = await readFile8(configPath, "utf-8");
22993
- return load(content);
22994
- } catch {
22995
- return null;
22996
- }
22997
- }
22998
- async function addUserPlugin(plugin) {
22999
- await ensureUserWorkspace();
23000
- const configPath = getUserWorkspaceConfigPath();
23001
- if (isPluginSpec(plugin)) {
23002
- const resolved = await resolvePluginSpecWithAutoRegister(plugin);
23003
- if (!resolved.success) {
23004
- return { success: false, error: resolved.error || "Unknown error" };
23005
- }
23006
- const normalizedPlugin = resolved.registeredAs ? plugin.replace(/@[^@]+$/, `@${resolved.registeredAs}`) : plugin;
23007
- return addPluginToUserConfig(normalizedPlugin, configPath, resolved.registeredAs);
23008
- }
23009
- if (isGitHubUrl(plugin)) {
23010
- const validation = validatePluginSource(plugin);
23011
- if (!validation.valid) {
23012
- return { success: false, error: validation.error || "Invalid GitHub URL" };
23013
- }
23014
- const verifyResult = await verifyGitHubUrlExists(plugin);
23015
- if (!verifyResult.exists) {
23016
- return { success: false, error: verifyResult.error || `GitHub URL not found: ${plugin}` };
23017
- }
23018
- } else {
23019
- if (!existsSync7(plugin)) {
23020
- return {
23021
- success: false,
23022
- error: `Plugin not found at ${plugin}`
23023
- };
23024
- }
23025
- }
23026
- return addPluginToUserConfig(plugin, configPath);
23027
- }
23028
- async function removeUserPlugin(plugin) {
23029
- await ensureUserWorkspace();
23030
- const configPath = getUserWorkspaceConfigPath();
23031
- try {
23032
- const content = await readFile8(configPath, "utf-8");
23033
- const config = load(content);
23034
- let index = config.plugins.indexOf(plugin);
23035
- if (index === -1) {
23036
- index = config.plugins.findIndex((p) => p.startsWith(`${plugin}@`) || p === plugin);
23037
- }
23038
- if (index === -1) {
23039
- return { success: false, error: `Plugin not found in user config: ${plugin}` };
23040
- }
23041
- config.plugins.splice(index, 1);
23042
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
23043
- return { success: true };
23044
- } catch (error) {
23045
- return {
23046
- success: false,
23047
- error: error instanceof Error ? error.message : String(error)
23048
- };
23049
- }
23050
- }
23051
- async function addPluginToUserConfig(plugin, configPath, autoRegistered) {
23052
- try {
23053
- const content = await readFile8(configPath, "utf-8");
23054
- const config = load(content);
23055
- if (config.plugins.includes(plugin)) {
23056
- return { success: false, error: `Plugin already exists in user config: ${plugin}` };
23057
- }
23058
- config.plugins.push(plugin);
23059
- await writeFile4(configPath, dump(config, { lineWidth: -1 }), "utf-8");
23060
- return { success: true, ...autoRegistered && { autoRegistered } };
23061
- } catch (error) {
23062
- return {
23063
- success: false,
23064
- error: error instanceof Error ? error.message : String(error)
23065
- };
23066
- }
23067
- }
23068
- var ALL_CLIENTS;
23069
- var init_user_workspace = __esm(() => {
23070
- init_js_yaml();
23071
- init_constants();
23072
- init_marketplace();
23073
- init_marketplace();
23074
- init_plugin_path();
23075
- ALL_CLIENTS = [
23076
- "claude",
23077
- "copilot",
23078
- "codex",
23079
- "cursor",
23080
- "opencode",
23081
- "gemini",
23082
- "factory",
23083
- "ampcode"
23084
- ];
23085
- });
23086
-
23087
23124
  // src/core/sync.ts
23088
23125
  import { existsSync as existsSync8 } from "node:fs";
23089
23126
  import { rm, unlink, rmdir, copyFile } from "node:fs/promises";
23090
23127
  import { join as join9, resolve as resolve5, dirname as dirname4, relative } from "node:path";
23128
+ function mergeSyncResults(a, b) {
23129
+ const warnings = [...a.warnings || [], ...b.warnings || []];
23130
+ const purgedPaths = [...a.purgedPaths || [], ...b.purgedPaths || []];
23131
+ return {
23132
+ success: a.success && b.success,
23133
+ pluginResults: [...a.pluginResults, ...b.pluginResults],
23134
+ totalCopied: a.totalCopied + b.totalCopied,
23135
+ totalFailed: a.totalFailed + b.totalFailed,
23136
+ totalSkipped: a.totalSkipped + b.totalSkipped,
23137
+ totalGenerated: a.totalGenerated + b.totalGenerated,
23138
+ ...warnings.length > 0 && { warnings },
23139
+ ...purgedPaths.length > 0 && { purgedPaths }
23140
+ };
23141
+ }
23091
23142
  async function selectivePurgeWorkspace(workspacePath, state, clients, options2) {
23092
23143
  if (!state) {
23093
23144
  return [];
@@ -23462,24 +23513,20 @@ async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
23462
23513
  }
23463
23514
  }
23464
23515
  const failedValidations = validatedPlugins.filter((v) => !v.success);
23465
- if (failedValidations.length > 0) {
23466
- const errors2 = failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
23467
- `);
23516
+ const validPlugins = validatedPlugins.filter((v) => v.success);
23517
+ const warnings = failedValidations.map((v) => `${v.plugin}: ${v.error} (skipped)`);
23518
+ if (validPlugins.length === 0 && config.plugins.length > 0) {
23468
23519
  return {
23469
23520
  success: false,
23470
- pluginResults: failedValidations.map((v) => ({
23471
- plugin: v.plugin,
23472
- resolved: v.resolved,
23473
- success: false,
23474
- copyResults: [],
23475
- ...v.error && { error: v.error }
23476
- })),
23521
+ pluginResults: [],
23477
23522
  totalCopied: 0,
23478
23523
  totalFailed: failedValidations.length,
23479
23524
  totalSkipped: 0,
23480
23525
  totalGenerated: 0,
23481
- error: `Plugin validation failed (workspace unchanged):
23482
- ${errors2}`
23526
+ warnings,
23527
+ error: `All plugins failed validation (workspace unchanged):
23528
+ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
23529
+ `)}`
23483
23530
  };
23484
23531
  }
23485
23532
  const previousState = await loadSyncState(workspacePath);
@@ -23492,9 +23539,9 @@ ${errors2}`
23492
23539
  partialSync: !!options2.clients
23493
23540
  });
23494
23541
  }
23495
- const allSkills = await collectAllSkills(validatedPlugins);
23542
+ const allSkills = await collectAllSkills(validPlugins);
23496
23543
  const pluginSkillMaps = buildPluginSkillNameMaps(allSkills);
23497
- const pluginResults = await Promise.all(validatedPlugins.map((validatedPlugin) => {
23544
+ const pluginResults = await Promise.all(validPlugins.map((validatedPlugin) => {
23498
23545
  const skillNameMap = pluginSkillMaps.get(validatedPlugin.resolved);
23499
23546
  return copyValidatedPlugin(validatedPlugin, workspacePath, clients, dryRun, skillNameMap);
23500
23547
  }));
@@ -23611,7 +23658,8 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
23611
23658
  totalFailed,
23612
23659
  totalSkipped,
23613
23660
  totalGenerated,
23614
- purgedPaths
23661
+ purgedPaths,
23662
+ ...warnings.length > 0 && { warnings }
23615
23663
  };
23616
23664
  }
23617
23665
  async function syncUserWorkspace(options2 = {}) {
@@ -23631,33 +23679,29 @@ async function syncUserWorkspace(options2 = {}) {
23631
23679
  const { offline = false, dryRun = false } = options2;
23632
23680
  const validatedPlugins = await validateAllPlugins(config.plugins, homeDir, offline);
23633
23681
  const failedValidations = validatedPlugins.filter((v) => !v.success);
23634
- if (failedValidations.length > 0) {
23635
- const errors2 = failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
23636
- `);
23682
+ const validPlugins = validatedPlugins.filter((v) => v.success);
23683
+ const warnings = failedValidations.map((v) => `${v.plugin}: ${v.error} (skipped)`);
23684
+ if (validPlugins.length === 0 && config.plugins.length > 0) {
23637
23685
  return {
23638
23686
  success: false,
23639
- pluginResults: failedValidations.map((v) => ({
23640
- plugin: v.plugin,
23641
- resolved: v.resolved,
23642
- success: false,
23643
- copyResults: [],
23644
- ...v.error && { error: v.error }
23645
- })),
23687
+ pluginResults: [],
23646
23688
  totalCopied: 0,
23647
23689
  totalFailed: failedValidations.length,
23648
23690
  totalSkipped: 0,
23649
23691
  totalGenerated: 0,
23650
- error: `Plugin validation failed:
23651
- ${errors2}`
23692
+ warnings,
23693
+ error: `All plugins failed validation:
23694
+ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
23695
+ `)}`
23652
23696
  };
23653
23697
  }
23654
23698
  const previousState = await loadSyncState(homeDir);
23655
23699
  if (!dryRun) {
23656
23700
  await selectivePurgeWorkspace(homeDir, previousState, clients);
23657
23701
  }
23658
- const allSkills = await collectAllSkills(validatedPlugins);
23702
+ const allSkills = await collectAllSkills(validPlugins);
23659
23703
  const pluginSkillMaps = buildPluginSkillNameMaps(allSkills);
23660
- const pluginResults = await Promise.all(validatedPlugins.map((vp) => {
23704
+ const pluginResults = await Promise.all(validPlugins.map((vp) => {
23661
23705
  const skillNameMap = pluginSkillMaps.get(vp.resolved);
23662
23706
  return copyValidatedPlugin(vp, homeDir, clients, dryRun, skillNameMap, USER_CLIENT_MAPPINGS);
23663
23707
  }));
@@ -23694,7 +23738,8 @@ ${errors2}`
23694
23738
  totalCopied,
23695
23739
  totalFailed,
23696
23740
  totalSkipped,
23697
- totalGenerated
23741
+ totalGenerated,
23742
+ ...warnings.length > 0 && { warnings }
23698
23743
  };
23699
23744
  }
23700
23745
  var init_sync = __esm(() => {
@@ -24042,9 +24087,11 @@ async function getWorkspaceStatus(workspacePath = process.cwd()) {
24042
24087
  plugins.push(status);
24043
24088
  }
24044
24089
  }
24090
+ const userPlugins = await getUserPluginStatuses();
24045
24091
  return {
24046
24092
  success: true,
24047
24093
  plugins,
24094
+ userPlugins,
24048
24095
  clients: config.clients
24049
24096
  };
24050
24097
  } catch (error) {
@@ -24077,6 +24124,21 @@ function getPluginStatus(parsed) {
24077
24124
  path: parsed.normalized
24078
24125
  };
24079
24126
  }
24127
+ async function getUserPluginStatuses() {
24128
+ const config = await getUserWorkspaceConfig();
24129
+ if (!config)
24130
+ return [];
24131
+ const statuses = [];
24132
+ for (const pluginSource of config.plugins) {
24133
+ if (isPluginSpec(pluginSource)) {
24134
+ statuses.push(await getMarketplacePluginStatus(pluginSource));
24135
+ } else {
24136
+ const parsed = parsePluginSource(pluginSource, process.env.HOME || "~");
24137
+ statuses.push(getPluginStatus(parsed));
24138
+ }
24139
+ }
24140
+ return statuses;
24141
+ }
24080
24142
  async function getMarketplacePluginStatus(spec) {
24081
24143
  const resolved = await resolvePluginSpecWithAutoRegister(spec);
24082
24144
  return {
@@ -24091,15 +24153,16 @@ var init_status = __esm(() => {
24091
24153
  init_workspace_parser();
24092
24154
  init_plugin_path();
24093
24155
  init_marketplace();
24156
+ init_user_workspace();
24094
24157
  });
24095
24158
 
24096
24159
  // src/core/workspace-modify.ts
24097
- import { readFile as readFile10, writeFile as writeFile6 } from "node:fs/promises";
24098
- import { existsSync as existsSync11 } from "node:fs";
24099
- import { join as join12 } from "node:path";
24160
+ import { readFile as readFile11, writeFile as writeFile7 } from "node:fs/promises";
24161
+ import { existsSync as existsSync13 } from "node:fs";
24162
+ import { join as join14 } from "node:path";
24100
24163
  async function addPlugin(plugin, workspacePath = process.cwd()) {
24101
- const configPath = join12(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
24102
- if (!existsSync11(configPath)) {
24164
+ const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
24165
+ if (!existsSync13(configPath)) {
24103
24166
  return {
24104
24167
  success: false,
24105
24168
  error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
@@ -24132,8 +24195,8 @@ async function addPlugin(plugin, workspacePath = process.cwd()) {
24132
24195
  };
24133
24196
  }
24134
24197
  } else {
24135
- const fullPath = join12(workspacePath, plugin);
24136
- if (!existsSync11(fullPath) && !existsSync11(plugin)) {
24198
+ const fullPath = join14(workspacePath, plugin);
24199
+ if (!existsSync13(fullPath) && !existsSync13(plugin)) {
24137
24200
  return {
24138
24201
  success: false,
24139
24202
  error: `Plugin not found at ${plugin}`
@@ -24144,7 +24207,7 @@ async function addPlugin(plugin, workspacePath = process.cwd()) {
24144
24207
  }
24145
24208
  async function addPluginToConfig(plugin, configPath, autoRegistered) {
24146
24209
  try {
24147
- const content = await readFile10(configPath, "utf-8");
24210
+ const content = await readFile11(configPath, "utf-8");
24148
24211
  const config = load(content);
24149
24212
  if (config.plugins.includes(plugin)) {
24150
24213
  return {
@@ -24154,7 +24217,7 @@ async function addPluginToConfig(plugin, configPath, autoRegistered) {
24154
24217
  }
24155
24218
  config.plugins.push(plugin);
24156
24219
  const newContent = dump(config, { lineWidth: -1 });
24157
- await writeFile6(configPath, newContent, "utf-8");
24220
+ await writeFile7(configPath, newContent, "utf-8");
24158
24221
  return {
24159
24222
  success: true,
24160
24223
  ...autoRegistered && { autoRegistered }
@@ -24167,8 +24230,8 @@ async function addPluginToConfig(plugin, configPath, autoRegistered) {
24167
24230
  }
24168
24231
  }
24169
24232
  async function removePlugin(plugin, workspacePath = process.cwd()) {
24170
- const configPath = join12(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
24171
- if (!existsSync11(configPath)) {
24233
+ const configPath = join14(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
24234
+ if (!existsSync13(configPath)) {
24172
24235
  return {
24173
24236
  success: false,
24174
24237
  error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
@@ -24176,7 +24239,7 @@ async function removePlugin(plugin, workspacePath = process.cwd()) {
24176
24239
  };
24177
24240
  }
24178
24241
  try {
24179
- const content = await readFile10(configPath, "utf-8");
24242
+ const content = await readFile11(configPath, "utf-8");
24180
24243
  const config = load(content);
24181
24244
  let index = config.plugins.indexOf(plugin);
24182
24245
  if (index === -1 && isPluginSpec(plugin) === false) {
@@ -24190,7 +24253,7 @@ async function removePlugin(plugin, workspacePath = process.cwd()) {
24190
24253
  }
24191
24254
  config.plugins.splice(index, 1);
24192
24255
  const newContent = dump(config, { lineWidth: -1 });
24193
- await writeFile6(configPath, newContent, "utf-8");
24256
+ await writeFile7(configPath, newContent, "utf-8");
24194
24257
  return { success: true };
24195
24258
  } catch (error) {
24196
24259
  return {
@@ -24211,7 +24274,7 @@ var package_default;
24211
24274
  var init_package = __esm(() => {
24212
24275
  package_default = {
24213
24276
  name: "allagents",
24214
- version: "0.11.1",
24277
+ version: "0.12.0",
24215
24278
  description: "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
24216
24279
  type: "module",
24217
24280
  bin: {
@@ -25483,11 +25546,11 @@ var init_dist2 = __esm(() => {
25483
25546
  });
25484
25547
 
25485
25548
  // src/cli/tui/context.ts
25486
- import { existsSync as existsSync12 } from "node:fs";
25487
- import { join as join13 } from "node:path";
25549
+ import { existsSync as existsSync14 } from "node:fs";
25550
+ import { join as join15 } from "node:path";
25488
25551
  async function getTuiContext(cwd = process.cwd()) {
25489
- const configPath = join13(cwd, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
25490
- const hasWorkspace = existsSync12(configPath);
25552
+ const configPath = join15(cwd, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
25553
+ const hasWorkspace = existsSync14(configPath);
25491
25554
  let projectPluginCount = 0;
25492
25555
  if (hasWorkspace) {
25493
25556
  try {
@@ -25659,27 +25722,58 @@ var init_status2 = __esm(() => {
25659
25722
  });
25660
25723
 
25661
25724
  // src/cli/tui/actions/plugins.ts
25725
+ async function installSelectedPlugin(pluginRef, context) {
25726
+ let scope = "user";
25727
+ if (context.hasWorkspace) {
25728
+ const scopeChoice = await qt({
25729
+ message: "Install scope",
25730
+ options: [
25731
+ { label: "Project (this workspace)", value: "project" },
25732
+ { label: "User (global)", value: "user" }
25733
+ ]
25734
+ });
25735
+ if (Ct(scopeChoice)) {
25736
+ return false;
25737
+ }
25738
+ scope = scopeChoice;
25739
+ }
25740
+ const s = Ie();
25741
+ s.start("Installing plugin...");
25742
+ if (scope === "project" && context.workspacePath) {
25743
+ const result = await addPlugin(pluginRef, context.workspacePath);
25744
+ if (!result.success) {
25745
+ s.stop("Installation failed");
25746
+ kt2(result.error ?? "Unknown error", "Error");
25747
+ return false;
25748
+ }
25749
+ s.stop("Plugin added");
25750
+ const syncS = Ie();
25751
+ syncS.start("Syncing...");
25752
+ await syncWorkspace(context.workspacePath);
25753
+ syncS.stop("Sync complete");
25754
+ } else {
25755
+ const result = await addUserPlugin(pluginRef);
25756
+ if (!result.success) {
25757
+ s.stop("Installation failed");
25758
+ kt2(result.error ?? "Unknown error", "Error");
25759
+ return false;
25760
+ }
25761
+ s.stop("Plugin added");
25762
+ const syncS = Ie();
25763
+ syncS.start("Syncing...");
25764
+ await syncUserWorkspace();
25765
+ syncS.stop("Sync complete");
25766
+ }
25767
+ kt2(`Installed: ${pluginRef}`, "Success");
25768
+ return true;
25769
+ }
25662
25770
  async function runInstallPlugin(context) {
25663
25771
  try {
25664
- let marketplaces = await listMarketplaces();
25772
+ const marketplaces = await listMarketplaces();
25665
25773
  if (marketplaces.length === 0) {
25666
- kt2("No marketplaces registered. Add one to browse plugins.", "Marketplace");
25667
- const source = await Qt({
25668
- message: "Marketplace source (GitHub URL, owner/repo, or name)",
25669
- placeholder: "e.g., anthropics/claude-plugins-official"
25670
- });
25671
- if (Ct(source)) {
25672
- return;
25673
- }
25674
- const s2 = Ie();
25675
- s2.start("Adding marketplace...");
25676
- const result = await addMarketplace(source);
25677
- s2.stop(result.success ? "Marketplace added" : "Failed to add marketplace");
25678
- if (!result.success) {
25679
- kt2(result.error ?? "Unknown error", "Error");
25680
- return;
25681
- }
25682
- marketplaces = await listMarketplaces();
25774
+ kt2(`No marketplaces registered.
25775
+ Use "Manage marketplaces" to add one first.`, "Marketplace");
25776
+ return;
25683
25777
  }
25684
25778
  const allPlugins = [];
25685
25779
  for (const marketplace of marketplaces) {
@@ -25703,48 +25797,7 @@ async function runInstallPlugin(context) {
25703
25797
  if (Ct(selected)) {
25704
25798
  return;
25705
25799
  }
25706
- let scope = "user";
25707
- if (context.hasWorkspace) {
25708
- const scopeChoice = await qt({
25709
- message: "Install scope",
25710
- options: [
25711
- { label: "Project (this workspace)", value: "project" },
25712
- { label: "User (global)", value: "user" }
25713
- ]
25714
- });
25715
- if (Ct(scopeChoice)) {
25716
- return;
25717
- }
25718
- scope = scopeChoice;
25719
- }
25720
- const s = Ie();
25721
- s.start("Installing plugin...");
25722
- if (scope === "project" && context.workspacePath) {
25723
- const result = await addPlugin(selected, context.workspacePath);
25724
- if (!result.success) {
25725
- s.stop("Installation failed");
25726
- kt2(result.error ?? "Unknown error", "Error");
25727
- return;
25728
- }
25729
- s.stop("Plugin added");
25730
- const syncS = Ie();
25731
- syncS.start("Syncing...");
25732
- await syncWorkspace(context.workspacePath);
25733
- syncS.stop("Sync complete");
25734
- } else {
25735
- const result = await addUserPlugin(selected);
25736
- if (!result.success) {
25737
- s.stop("Installation failed");
25738
- kt2(result.error ?? "Unknown error", "Error");
25739
- return;
25740
- }
25741
- s.stop("Plugin added");
25742
- const syncS = Ie();
25743
- syncS.start("Syncing...");
25744
- await syncUserWorkspace();
25745
- syncS.stop("Sync complete");
25746
- }
25747
- kt2(`Installed: ${selected}`, "Success");
25800
+ await installSelectedPlugin(selected, context);
25748
25801
  } catch (error) {
25749
25802
  const message = error instanceof Error ? error.message : String(error);
25750
25803
  kt2(message, "Error");
@@ -25815,6 +25868,129 @@ async function runManagePlugins(context) {
25815
25868
  kt2(message, "Error");
25816
25869
  }
25817
25870
  }
25871
+ async function runBrowseMarketplaces(context) {
25872
+ try {
25873
+ while (true) {
25874
+ const marketplaces = await listMarketplaces();
25875
+ const options2 = [
25876
+ { label: "+ Add marketplace", value: "__add__" },
25877
+ ...marketplaces.map((m) => ({
25878
+ label: `${m.name} (${m.source.type}: ${m.source.location})`,
25879
+ value: m.name
25880
+ })),
25881
+ { label: "Back", value: "__back__" }
25882
+ ];
25883
+ const selected = await qt({
25884
+ message: "Marketplaces",
25885
+ options: options2
25886
+ });
25887
+ if (Ct(selected) || selected === "__back__") {
25888
+ return;
25889
+ }
25890
+ if (selected === "__add__") {
25891
+ const source = await Qt({
25892
+ message: "Marketplace source (GitHub URL, owner/repo, or name)",
25893
+ placeholder: "e.g., anthropics/claude-plugins-official"
25894
+ });
25895
+ if (Ct(source)) {
25896
+ continue;
25897
+ }
25898
+ const s = Ie();
25899
+ s.start("Adding marketplace...");
25900
+ const result = await addMarketplace(source);
25901
+ s.stop(result.success ? "Marketplace added" : "Failed to add marketplace");
25902
+ if (!result.success) {
25903
+ kt2(result.error ?? "Unknown error", "Error");
25904
+ }
25905
+ continue;
25906
+ }
25907
+ await runMarketplaceDetail(selected, context);
25908
+ }
25909
+ } catch (error) {
25910
+ const message = error instanceof Error ? error.message : String(error);
25911
+ kt2(message, "Error");
25912
+ }
25913
+ }
25914
+ async function runMarketplaceDetail(marketplaceName, context) {
25915
+ while (true) {
25916
+ const action = await qt({
25917
+ message: `Marketplace: ${marketplaceName}`,
25918
+ options: [
25919
+ { label: "Browse plugins", value: "browse" },
25920
+ { label: "Update marketplace", value: "update" },
25921
+ { label: "Remove marketplace", value: "remove" },
25922
+ { label: "Back", value: "back" }
25923
+ ]
25924
+ });
25925
+ if (Ct(action) || action === "back") {
25926
+ return;
25927
+ }
25928
+ if (action === "browse") {
25929
+ try {
25930
+ const result = await listMarketplacePlugins(marketplaceName);
25931
+ if (result.plugins.length === 0) {
25932
+ kt2("No plugins found in this marketplace.", "Plugins");
25933
+ continue;
25934
+ }
25935
+ const pluginOptions = result.plugins.map((plugin) => {
25936
+ const label = plugin.description ? `${plugin.name} - ${plugin.description}` : plugin.name;
25937
+ return { label, value: plugin.name };
25938
+ });
25939
+ pluginOptions.push({ label: "Back", value: "__back__" });
25940
+ const selectedPlugin = await qt({
25941
+ message: "Select a plugin to install",
25942
+ options: pluginOptions
25943
+ });
25944
+ if (Ct(selectedPlugin) || selectedPlugin === "__back__") {
25945
+ continue;
25946
+ }
25947
+ const pluginRef = `${selectedPlugin}@${marketplaceName}`;
25948
+ await installSelectedPlugin(pluginRef, context);
25949
+ } catch (error) {
25950
+ const message = error instanceof Error ? error.message : String(error);
25951
+ kt2(message, "Error");
25952
+ }
25953
+ continue;
25954
+ }
25955
+ if (action === "update") {
25956
+ try {
25957
+ const s = Ie();
25958
+ s.start("Updating marketplace...");
25959
+ const results = await updateMarketplace(marketplaceName);
25960
+ const summary = results.map((r) => `${r.success ? "✓" : "✗"} ${r.name}${r.error ? ` - ${r.error}` : ""}`).join(`
25961
+ `);
25962
+ s.stop("Update complete");
25963
+ kt2(summary || "Marketplace updated.", "Update");
25964
+ } catch (error) {
25965
+ const message = error instanceof Error ? error.message : String(error);
25966
+ kt2(message, "Error");
25967
+ }
25968
+ continue;
25969
+ }
25970
+ if (action === "remove") {
25971
+ const confirmed = await Mt2({
25972
+ message: `Remove marketplace "${marketplaceName}"?`
25973
+ });
25974
+ if (Ct(confirmed) || !confirmed) {
25975
+ continue;
25976
+ }
25977
+ try {
25978
+ const s = Ie();
25979
+ s.start("Removing marketplace...");
25980
+ const result = await removeMarketplace(marketplaceName);
25981
+ s.stop(result.success ? "Marketplace removed" : "Failed to remove marketplace");
25982
+ if (!result.success) {
25983
+ kt2(result.error ?? "Unknown error", "Error");
25984
+ continue;
25985
+ }
25986
+ } catch (error) {
25987
+ const message = error instanceof Error ? error.message : String(error);
25988
+ kt2(message, "Error");
25989
+ }
25990
+ return;
25991
+ }
25992
+ }
25993
+ }
25818
25994
  var init_plugins = __esm(() => {
25819
25995
  init_dist2();
25820
25996
  init_workspace_modify();
@@ -25862,7 +26038,7 @@ function buildMenuOptions(context) {
25862
26038
  const options2 = [];
25863
26039
  if (!context.hasWorkspace) {
25864
26040
  options2.push({ label: "Initialize workspace", value: "init" });
25865
- options2.push({ label: "Browse marketplace", value: "marketplace" });
26041
+ options2.push({ label: "Manage marketplaces", value: "marketplace" });
25866
26042
  options2.push({
25867
26043
  label: "Install plugin (user scope)",
25868
26044
  value: "install"
@@ -25876,12 +26052,12 @@ function buildMenuOptions(context) {
25876
26052
  options2.push({ label: "View status", value: "status" });
25877
26053
  options2.push({ label: "Install plugin", value: "install" });
25878
26054
  options2.push({ label: "Manage plugins", value: "manage" });
25879
- options2.push({ label: "Browse marketplace", value: "marketplace" });
26055
+ options2.push({ label: "Manage marketplaces", value: "marketplace" });
25880
26056
  } else {
25881
26057
  options2.push({ label: "View status", value: "status" });
25882
26058
  options2.push({ label: "Install plugin", value: "install" });
25883
26059
  options2.push({ label: "Manage plugins", value: "manage" });
25884
- options2.push({ label: "Browse marketplace", value: "marketplace" });
26060
+ options2.push({ label: "Manage marketplaces", value: "marketplace" });
25885
26061
  options2.push({ label: "Check for updates", value: "update" });
25886
26062
  }
25887
26063
  options2.push({ label: "Exit", value: "exit" });
@@ -25936,7 +26112,7 @@ async function runWizard() {
25936
26112
  await runManagePlugins(context);
25937
26113
  break;
25938
26114
  case "marketplace":
25939
- await runInstallPlugin(context);
26115
+ await runBrowseMarketplaces(context);
25940
26116
  break;
25941
26117
  case "update":
25942
26118
  await runUpdate();
@@ -26011,6 +26187,68 @@ init_workspace();
26011
26187
  init_sync();
26012
26188
  init_status();
26013
26189
  var import_cmd_ts2 = __toESM(require_cjs(), 1);
26190
+ import { existsSync as existsSync12 } from "node:fs";
26191
+ import { join as join13 } from "node:path";
26192
+
26193
+ // src/core/prune.ts
26194
+ init_js_yaml();
26195
+ init_constants();
26196
+ init_marketplace();
26197
+ init_user_workspace();
26198
+ import { readFile as readFile10, writeFile as writeFile6 } from "node:fs/promises";
26199
+ import { existsSync as existsSync11 } from "node:fs";
26200
+ import { join as join12 } from "node:path";
26201
+ async function isOrphanedPlugin(pluginSpec) {
26202
+ if (!isPluginSpec(pluginSpec))
26203
+ return false;
26204
+ const parsed = parsePluginSpec(pluginSpec);
26205
+ if (!parsed)
26206
+ return false;
26207
+ const marketplace = await getMarketplace(parsed.marketplaceName);
26208
+ return marketplace === null;
26209
+ }
26210
+ async function prunePlugins(plugins) {
26211
+ const removed = [];
26212
+ const kept = [];
26213
+ for (const plugin of plugins) {
26214
+ if (await isOrphanedPlugin(plugin)) {
26215
+ removed.push(plugin);
26216
+ } else {
26217
+ kept.push(plugin);
26218
+ }
26219
+ }
26220
+ return { removed, kept };
26221
+ }
26222
+ async function pruneOrphanedPlugins(workspacePath) {
26223
+ let projectResult = { removed: [], kept: [] };
26224
+ const projectConfigPath = join12(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
26225
+ if (existsSync11(projectConfigPath)) {
26226
+ const content = await readFile10(projectConfigPath, "utf-8");
26227
+ const config = load(content);
26228
+ projectResult = await prunePlugins(config.plugins);
26229
+ if (projectResult.removed.length > 0) {
26230
+ config.plugins = projectResult.kept;
26231
+ await writeFile6(projectConfigPath, dump(config, { lineWidth: -1 }), "utf-8");
26232
+ }
26233
+ }
26234
+ let userResult = { removed: [], kept: [] };
26235
+ const userConfig = await getUserWorkspaceConfig();
26236
+ if (userConfig) {
26237
+ userResult = await prunePlugins(userConfig.plugins);
26238
+ if (userResult.removed.length > 0) {
26239
+ userConfig.plugins = userResult.kept;
26240
+ const userConfigPath = getUserWorkspaceConfigPath();
26241
+ await writeFile6(userConfigPath, dump(userConfig, { lineWidth: -1 }), "utf-8");
26242
+ }
26243
+ }
26244
+ return {
26245
+ project: projectResult,
26246
+ user: userResult
26247
+ };
26248
+ }
26249
+
26250
+ // src/cli/commands/workspace.ts
26251
+ init_user_workspace();
26014
26252
 
26015
26253
  // src/cli/json-output.ts
26016
26254
  var jsonMode = false;
@@ -26066,15 +26304,13 @@ var syncMeta = {
26066
26304
  "allagents workspace sync",
26067
26305
  "allagents workspace sync --dry-run",
26068
26306
  "allagents workspace sync --client claude",
26069
- "allagents workspace sync --offline",
26070
- "allagents workspace sync --scope user"
26307
+ "allagents workspace sync --offline"
26071
26308
  ],
26072
26309
  expectedOutput: "Lists synced files with status per plugin. Exit 0 on success, exit 1 if any files failed.",
26073
26310
  options: [
26074
26311
  { flag: "--offline", type: "boolean", description: "Use cached plugins without fetching latest from remote" },
26075
26312
  { flag: "--dry-run", short: "-n", type: "boolean", description: "Simulate sync without making changes" },
26076
- { flag: "--client", short: "-c", type: "string", description: "Sync only the specified client (e.g., opencode, claude)" },
26077
- { flag: "--scope", short: "-s", type: "string", description: 'Sync scope: "project" (default) or "user"' }
26313
+ { flag: "--client", short: "-c", type: "string", description: "Sync only the specified client (e.g., opencode, claude)" }
26078
26314
  ],
26079
26315
  outputSchema: {
26080
26316
  copied: "number",
@@ -26084,6 +26320,19 @@ var syncMeta = {
26084
26320
  plugins: [{ plugin: "string", success: "boolean", copied: "number", generated: "number", failed: "number" }]
26085
26321
  }
26086
26322
  };
26323
+ var pruneMeta = {
26324
+ command: "workspace prune",
26325
+ description: "Remove orphaned plugin references",
26326
+ whenToUse: "After removing a marketplace to clean up stale plugin references in workspace configs",
26327
+ examples: [
26328
+ "allagents workspace prune"
26329
+ ],
26330
+ expectedOutput: "Lists removed orphaned plugins from both project and user scopes. Exit 0 on success, exit 1 on error.",
26331
+ outputSchema: {
26332
+ project: { removed: ["string"], kept: ["string"] },
26333
+ user: { removed: ["string"], kept: ["string"] }
26334
+ }
26335
+ };
26087
26336
  var statusMeta = {
26088
26337
  command: "workspace status",
26089
26338
  description: "Show sync status of plugins",
@@ -26175,37 +26424,52 @@ var syncCmd = import_cmd_ts2.command({
26175
26424
  args: {
26176
26425
  offline: import_cmd_ts2.flag({ long: "offline", description: "Use cached plugins without fetching latest from remote" }),
26177
26426
  dryRun: import_cmd_ts2.flag({ long: "dry-run", short: "n", description: "Simulate sync without making changes" }),
26178
- client: import_cmd_ts2.option({ type: import_cmd_ts2.optional(import_cmd_ts2.string), long: "client", short: "c", description: "Sync only the specified client (e.g., opencode, claude)" }),
26179
- scope: import_cmd_ts2.option({ type: import_cmd_ts2.optional(import_cmd_ts2.string), long: "scope", short: "s", description: 'Sync scope: "project" (default) or "user"' })
26427
+ client: import_cmd_ts2.option({ type: import_cmd_ts2.optional(import_cmd_ts2.string), long: "client", short: "c", description: "Sync only the specified client (e.g., opencode, claude)" })
26180
26428
  },
26181
- handler: async ({ offline, dryRun, client, scope }) => {
26429
+ handler: async ({ offline, dryRun, client }) => {
26182
26430
  try {
26183
- const isUser = scope === "user";
26184
- if (!isJsonMode()) {
26185
- if (dryRun) {
26186
- console.log(`Dry run mode - no changes will be made
26431
+ if (!isJsonMode() && dryRun) {
26432
+ console.log(`Dry run mode - no changes will be made
26187
26433
  `);
26188
- }
26189
- if (client) {
26190
- console.log(`Syncing client: ${client}
26434
+ }
26435
+ if (!isJsonMode() && client) {
26436
+ console.log(`Syncing client: ${client}
26191
26437
  `);
26438
+ }
26439
+ const userConfigExists = !!await getUserWorkspaceConfig();
26440
+ const projectConfigPath = join13(process.cwd(), ".allagents", "workspace.yaml");
26441
+ const projectConfigExists = existsSync12(projectConfigPath);
26442
+ if (!userConfigExists && !projectConfigExists) {
26443
+ await ensureUserWorkspace();
26444
+ if (isJsonMode()) {
26445
+ jsonOutput({ success: true, command: "workspace sync", data: { message: "No plugins configured" } });
26446
+ } else {
26447
+ console.log("No plugins configured. Run `allagents plugin install <plugin>` to get started.");
26192
26448
  }
26193
- console.log(`Syncing ${isUser ? "user" : ""} workspace...
26449
+ return;
26450
+ }
26451
+ let combined = null;
26452
+ if (userConfigExists) {
26453
+ if (!isJsonMode()) {
26454
+ console.log(`Syncing user workspace...
26194
26455
  `);
26456
+ }
26457
+ const userResult = await syncUserWorkspace({ offline, dryRun });
26458
+ combined = userResult;
26195
26459
  }
26196
- const result = isUser ? await syncUserWorkspace({ offline, dryRun }) : await syncWorkspace(process.cwd(), {
26197
- offline,
26198
- dryRun,
26199
- ...client && { clients: [client] }
26200
- });
26201
- if (!result.success && result.error) {
26202
- if (isJsonMode()) {
26203
- jsonOutput({ success: false, command: "workspace sync", error: result.error });
26204
- process.exit(1);
26460
+ if (projectConfigExists) {
26461
+ if (!isJsonMode()) {
26462
+ console.log(`Syncing project workspace...
26463
+ `);
26205
26464
  }
26206
- console.error(`Error: ${result.error}`);
26207
- process.exit(1);
26465
+ const projectResult = await syncWorkspace(process.cwd(), {
26466
+ offline,
26467
+ dryRun,
26468
+ ...client && { clients: [client] }
26469
+ });
26470
+ combined = combined ? mergeSyncResults(combined, projectResult) : projectResult;
26208
26471
  }
26472
+ const result = combined;
26209
26473
  if (isJsonMode()) {
26210
26474
  const syncData = buildSyncData(result);
26211
26475
  const success = result.success && result.totalFailed === 0;
@@ -26239,12 +26503,10 @@ var syncCmd = import_cmd_ts2.command({
26239
26503
  const copied = pluginResult.copyResults.filter((r) => r.action === "copied").length;
26240
26504
  const generated = pluginResult.copyResults.filter((r) => r.action === "generated").length;
26241
26505
  const failed = pluginResult.copyResults.filter((r) => r.action === "failed").length;
26242
- if (copied > 0) {
26506
+ if (copied > 0)
26243
26507
  console.log(` Copied: ${copied} files`);
26244
- }
26245
- if (generated > 0) {
26508
+ if (generated > 0)
26246
26509
  console.log(` Generated: ${generated} files`);
26247
- }
26248
26510
  if (failed > 0) {
26249
26511
  console.log(` Failed: ${failed} files`);
26250
26512
  for (const failedResult of pluginResult.copyResults.filter((r) => r.action === "failed")) {
@@ -26252,18 +26514,22 @@ var syncCmd = import_cmd_ts2.command({
26252
26514
  }
26253
26515
  }
26254
26516
  }
26517
+ if (result.warnings && result.warnings.length > 0) {
26518
+ console.log(`
26519
+ Warnings:`);
26520
+ for (const warning of result.warnings) {
26521
+ console.log(` ⚠ ${warning}`);
26522
+ }
26523
+ }
26255
26524
  console.log(`
26256
26525
  Sync complete${dryRun ? " (dry run)" : ""}:`);
26257
26526
  console.log(` Total ${dryRun ? "would copy" : "copied"}: ${result.totalCopied}`);
26258
- if (result.totalGenerated > 0) {
26527
+ if (result.totalGenerated > 0)
26259
26528
  console.log(` Total generated: ${result.totalGenerated}`);
26260
- }
26261
- if (result.totalFailed > 0) {
26529
+ if (result.totalFailed > 0)
26262
26530
  console.log(` Total failed: ${result.totalFailed}`);
26263
- }
26264
- if (result.totalSkipped > 0) {
26531
+ if (result.totalSkipped > 0)
26265
26532
  console.log(` Total skipped: ${result.totalSkipped}`);
26266
- }
26267
26533
  if (!result.success || result.totalFailed > 0) {
26268
26534
  process.exit(1);
26269
26535
  }
@@ -26299,11 +26565,11 @@ var statusCmd = import_cmd_ts2.command({
26299
26565
  jsonOutput({
26300
26566
  success: true,
26301
26567
  command: "workspace status",
26302
- data: { plugins: result.plugins, clients: result.clients }
26568
+ data: { plugins: result.plugins, userPlugins: result.userPlugins ?? [], clients: result.clients }
26303
26569
  });
26304
26570
  return;
26305
26571
  }
26306
- console.log(`Plugins (${result.plugins.length}):`);
26572
+ console.log(`Project Plugins (${result.plugins.length}):`);
26307
26573
  if (result.plugins.length === 0) {
26308
26574
  console.log(" No plugins configured");
26309
26575
  } else {
@@ -26321,6 +26587,27 @@ var statusCmd = import_cmd_ts2.command({
26321
26587
  console.log(` ${status} ${plugin.source}${suffix}`);
26322
26588
  }
26323
26589
  }
26590
+ if (result.userPlugins) {
26591
+ console.log(`
26592
+ User Plugins (${result.userPlugins.length}):`);
26593
+ if (result.userPlugins.length === 0) {
26594
+ console.log(" No user plugins configured");
26595
+ } else {
26596
+ for (const plugin of result.userPlugins) {
26597
+ const status = plugin.available ? "✓" : "✗";
26598
+ let typeLabel;
26599
+ if (plugin.type === "marketplace") {
26600
+ typeLabel = plugin.available ? undefined : "not synced";
26601
+ } else if (plugin.type === "github") {
26602
+ typeLabel = plugin.available ? "cached" : "not cached";
26603
+ } else {
26604
+ typeLabel = "local";
26605
+ }
26606
+ const suffix = typeLabel ? ` (${typeLabel})` : "";
26607
+ console.log(` ${status} ${plugin.source}${suffix}`);
26608
+ }
26609
+ }
26610
+ }
26324
26611
  console.log(`
26325
26612
  Clients (${result.clients.length}):`);
26326
26613
  if (result.clients.length === 0) {
@@ -26341,13 +26628,61 @@ Clients (${result.clients.length}):`);
26341
26628
  }
26342
26629
  }
26343
26630
  });
26631
+ var pruneCmd = import_cmd_ts2.command({
26632
+ name: "prune",
26633
+ description: buildDescription(pruneMeta),
26634
+ args: {},
26635
+ handler: async () => {
26636
+ try {
26637
+ const result = await pruneOrphanedPlugins(process.cwd());
26638
+ if (isJsonMode()) {
26639
+ jsonOutput({
26640
+ success: true,
26641
+ command: "workspace prune",
26642
+ data: result
26643
+ });
26644
+ return;
26645
+ }
26646
+ const totalRemoved = result.project.removed.length + result.user.removed.length;
26647
+ if (totalRemoved === 0) {
26648
+ console.log("No orphaned plugins found.");
26649
+ return;
26650
+ }
26651
+ if (result.project.removed.length > 0) {
26652
+ console.log(`Project plugins pruned (${result.project.removed.length}):`);
26653
+ for (const p of result.project.removed) {
26654
+ console.log(` - ${p}`);
26655
+ }
26656
+ }
26657
+ if (result.user.removed.length > 0) {
26658
+ console.log(`User plugins pruned (${result.user.removed.length}):`);
26659
+ for (const p of result.user.removed) {
26660
+ console.log(` - ${p}`);
26661
+ }
26662
+ }
26663
+ console.log(`
26664
+ ✓ Removed ${totalRemoved} orphaned plugin(s)`);
26665
+ } catch (error) {
26666
+ if (error instanceof Error) {
26667
+ if (isJsonMode()) {
26668
+ jsonOutput({ success: false, command: "workspace prune", error: error.message });
26669
+ process.exit(1);
26670
+ }
26671
+ console.error(`Error: ${error.message}`);
26672
+ process.exit(1);
26673
+ }
26674
+ throw error;
26675
+ }
26676
+ }
26677
+ });
26344
26678
  var workspaceCmd = conciseSubcommands({
26345
26679
  name: "workspace",
26346
26680
  description: "Manage AI agent workspaces - initialize, sync, and configure plugins",
26347
26681
  cmds: {
26348
26682
  init: initCmd,
26349
26683
  sync: syncCmd,
26350
- status: statusCmd
26684
+ status: statusCmd,
26685
+ prune: pruneCmd
26351
26686
  }
26352
26687
  });
26353
26688
 
@@ -26772,6 +27107,12 @@ var marketplaceRemoveCmd = import_cmd_ts3.command({
26772
27107
  return;
26773
27108
  }
26774
27109
  console.log(`✓ Marketplace '${name}' removed from registry`);
27110
+ if (result.removedUserPlugins && result.removedUserPlugins.length > 0) {
27111
+ console.log(` Removed ${result.removedUserPlugins.length} user plugin(s):`);
27112
+ for (const p of result.removedUserPlugins) {
27113
+ console.log(` - ${p}`);
27114
+ }
27115
+ }
26775
27116
  console.log(` Note: Files at ${result.marketplace?.path} were not deleted`);
26776
27117
  } catch (error) {
26777
27118
  if (error instanceof Error) {
@@ -13,3 +13,6 @@ plugins: []
13
13
 
14
14
  clients:
15
15
  - claude
16
+ - copilot
17
+ - codex
18
+ - opencode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "allagents",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
5
5
  "type": "module",
6
6
  "bin": {