allagents 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -5
  2. package/dist/index.js +465 -276
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -66,9 +66,9 @@ allagents workspace init my-workspace --from owner/repo/path/to/template
66
66
  # Add a marketplace (or let auto-registration handle it)
67
67
  allagents plugin marketplace add anthropics/claude-plugins-official
68
68
 
69
- # Add plugins to workspace
70
- allagents workspace plugin add code-review@claude-plugins-official
71
- allagents workspace plugin add my-plugin@someuser/their-repo
69
+ # Install plugins to workspace
70
+ allagents workspace plugin install code-review@claude-plugins-official
71
+ allagents workspace plugin install my-plugin@someuser/their-repo
72
72
 
73
73
  # Sync plugins to workspace
74
74
  allagents workspace sync
@@ -113,8 +113,8 @@ allagents workspace sync [options]
113
113
  # Show status of workspace and plugins
114
114
  allagents workspace status
115
115
 
116
- # Add a plugin to .allagents/workspace.yaml (auto-registers marketplace if needed)
117
- allagents workspace plugin add <plugin@marketplace>
116
+ # Install a plugin to .allagents/workspace.yaml (auto-registers marketplace if needed)
117
+ allagents workspace plugin install <plugin@marketplace>
118
118
 
119
119
  # Remove a plugin from .allagents/workspace.yaml
120
120
  allagents workspace plugin remove <plugin>
package/dist/index.js CHANGED
@@ -11055,13 +11055,13 @@ var {
11055
11055
 
11056
11056
  // src/cli/index.ts
11057
11057
  import { readFileSync as readFileSync3 } from "node:fs";
11058
- import { dirname as dirname7, join as join12 } from "node:path";
11058
+ import { dirname as dirname7, join as join13 } from "node:path";
11059
11059
  import { fileURLToPath as fileURLToPath4 } from "node:url";
11060
11060
 
11061
11061
  // src/core/workspace.ts
11062
- import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile4, copyFile as copyFile2 } from "node:fs/promises";
11063
- import { existsSync as existsSync7 } from "node:fs";
11064
- import { join as join8, resolve as resolve5, dirname as dirname5, relative as relative2, sep, isAbsolute as isAbsolute2 } from "node:path";
11062
+ import { mkdir as mkdir5, readFile as readFile8, writeFile as writeFile4, copyFile as copyFile2 } from "node:fs/promises";
11063
+ import { existsSync as existsSync8 } from "node:fs";
11064
+ import { join as join9, resolve as resolve6, dirname as dirname5, relative as relative2, sep, isAbsolute as isAbsolute2 } from "node:path";
11065
11065
  import { fileURLToPath as fileURLToPath2 } from "node:url";
11066
11066
 
11067
11067
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -13712,9 +13712,9 @@ var safeLoadAll = renamed("safeLoadAll", "loadAll");
13712
13712
  var safeDump = renamed("safeDump", "dump");
13713
13713
 
13714
13714
  // src/core/sync.ts
13715
- import { existsSync as existsSync6 } from "node:fs";
13715
+ import { existsSync as existsSync7 } from "node:fs";
13716
13716
  import { rm, unlink, rmdir, copyFile } from "node:fs/promises";
13717
- import { join as join7, resolve as resolve4, dirname as dirname4, relative } from "node:path";
13717
+ import { join as join8, resolve as resolve5, dirname as dirname4, relative } from "node:path";
13718
13718
 
13719
13719
  // src/constants.ts
13720
13720
  var CONFIG_DIR = ".allagents";
@@ -19787,11 +19787,10 @@ import { existsSync as existsSync2 } from "node:fs";
19787
19787
  import { join as join3 } from "node:path";
19788
19788
 
19789
19789
  // src/models/skill-metadata.ts
19790
- var skillNameRegex = /^[a-z0-9-]{1,64}$/;
19791
19790
  var SkillMetadataSchema = exports_external.object({
19792
- name: exports_external.string().regex(skillNameRegex, "Skill name must be lowercase, alphanumeric + hyphens, max 64 chars"),
19791
+ name: exports_external.string().min(1, "Skill name is required").max(128, "Skill name too long"),
19793
19792
  description: exports_external.string().min(1, "Description is required"),
19794
- "allowed-tools": exports_external.array(exports_external.string()).optional(),
19793
+ "allowed-tools": exports_external.union([exports_external.array(exports_external.string()), exports_external.string().transform((s) => [s])]).optional(),
19795
19794
  model: exports_external.string().optional()
19796
19795
  });
19797
19796
 
@@ -20413,29 +20412,120 @@ function resolveSkillNames(skills) {
20413
20412
  }
20414
20413
 
20415
20414
  // src/core/marketplace.ts
20416
- import { mkdir as mkdir3, readFile as readFile5, readdir as readdir3, writeFile as writeFile2 } from "node:fs/promises";
20415
+ import { mkdir as mkdir3, readFile as readFile6, readdir as readdir3, writeFile as writeFile2 } from "node:fs/promises";
20416
+ import { existsSync as existsSync5 } from "node:fs";
20417
+ import { basename as basename2, join as join6, resolve as resolve4 } from "node:path";
20418
+
20419
+ // src/utils/marketplace-manifest-parser.ts
20420
+ import { readFile as readFile5 } from "node:fs/promises";
20417
20421
  import { existsSync as existsSync4 } from "node:fs";
20418
- import { basename as basename2, join as join5, resolve as resolve3 } from "node:path";
20422
+ import { join as join5, resolve as resolve3 } from "node:path";
20423
+
20424
+ // src/models/marketplace-manifest.ts
20425
+ var UrlSourceSchema = exports_external.object({
20426
+ source: exports_external.literal("url"),
20427
+ url: exports_external.string().url()
20428
+ });
20429
+ var PluginSourceRefSchema = exports_external.union([exports_external.string(), UrlSourceSchema]);
20430
+ var ContactSchema = exports_external.object({
20431
+ name: exports_external.string(),
20432
+ email: exports_external.string().optional()
20433
+ });
20434
+ var LspServerSchema = exports_external.object({
20435
+ command: exports_external.string(),
20436
+ args: exports_external.array(exports_external.string()).optional(),
20437
+ extensionToLanguage: exports_external.record(exports_external.string()).optional(),
20438
+ startupTimeout: exports_external.number().optional()
20439
+ });
20440
+ var MarketplacePluginEntrySchema = exports_external.object({
20441
+ name: exports_external.string().min(1),
20442
+ description: exports_external.string().min(1),
20443
+ source: PluginSourceRefSchema,
20444
+ version: exports_external.string().optional(),
20445
+ author: ContactSchema.optional(),
20446
+ category: exports_external.string().optional(),
20447
+ homepage: exports_external.string().optional(),
20448
+ strict: exports_external.boolean().optional(),
20449
+ tags: exports_external.array(exports_external.string()).optional(),
20450
+ lspServers: exports_external.record(LspServerSchema).optional()
20451
+ });
20452
+ var MarketplaceManifestSchema = exports_external.object({
20453
+ $schema: exports_external.string().optional(),
20454
+ name: exports_external.string().min(1),
20455
+ version: exports_external.string().optional(),
20456
+ description: exports_external.string().min(1),
20457
+ owner: ContactSchema.optional(),
20458
+ plugins: exports_external.array(MarketplacePluginEntrySchema)
20459
+ });
20460
+
20461
+ // src/utils/marketplace-manifest-parser.ts
20462
+ var MANIFEST_PATH = ".claude-plugin/marketplace.json";
20463
+ async function parseMarketplaceManifest(marketplacePath) {
20464
+ const manifestPath = join5(marketplacePath, MANIFEST_PATH);
20465
+ if (!existsSync4(manifestPath)) {
20466
+ return {
20467
+ success: false,
20468
+ error: `Marketplace manifest not found: ${manifestPath}`
20469
+ };
20470
+ }
20471
+ let raw;
20472
+ try {
20473
+ raw = await readFile5(manifestPath, "utf-8");
20474
+ } catch (err) {
20475
+ return {
20476
+ success: false,
20477
+ error: `Failed to read marketplace manifest: ${err instanceof Error ? err.message : String(err)}`
20478
+ };
20479
+ }
20480
+ let json2;
20481
+ try {
20482
+ json2 = JSON.parse(raw);
20483
+ } catch {
20484
+ return {
20485
+ success: false,
20486
+ error: "Failed to parse marketplace.json as JSON: invalid syntax"
20487
+ };
20488
+ }
20489
+ const result = MarketplaceManifestSchema.safeParse(json2);
20490
+ if (!result.success) {
20491
+ const issues = result.error.issues.map((i2) => ` ${i2.path.join(".")}: ${i2.message}`).join(`
20492
+ `);
20493
+ return {
20494
+ success: false,
20495
+ error: `Marketplace manifest validation failed:
20496
+ ${issues}`
20497
+ };
20498
+ }
20499
+ return { success: true, data: result.data };
20500
+ }
20501
+ function resolvePluginSourcePath(source, marketplacePath) {
20502
+ if (typeof source === "object") {
20503
+ return source.url;
20504
+ }
20505
+ return resolve3(marketplacePath, source);
20506
+ }
20507
+
20508
+ // src/core/marketplace.ts
20419
20509
  var WELL_KNOWN_MARKETPLACES = {
20420
20510
  "claude-plugins-official": "anthropics/claude-plugins-official"
20421
20511
  };
20422
20512
  function getAllagentsDir() {
20423
20513
  const homeDir = process.env.HOME || process.env.USERPROFILE || "~";
20424
- return resolve3(homeDir, ".allagents");
20514
+ return resolve4(homeDir, ".allagents");
20425
20515
  }
20426
20516
  function getMarketplacesDir() {
20427
- return join5(getAllagentsDir(), "marketplaces");
20517
+ return join6(getAllagentsDir(), "marketplaces");
20428
20518
  }
20429
20519
  function getRegistryPath() {
20430
- return join5(getAllagentsDir(), "marketplaces.json");
20520
+ return join6(getAllagentsDir(), "marketplaces.json");
20431
20521
  }
20432
20522
  async function loadRegistry() {
20433
20523
  const registryPath = getRegistryPath();
20434
- if (!existsSync4(registryPath)) {
20524
+ if (!existsSync5(registryPath)) {
20435
20525
  return { version: 1, marketplaces: {} };
20436
20526
  }
20437
20527
  try {
20438
- const content = await readFile5(registryPath, "utf-8");
20528
+ const content = await readFile6(registryPath, "utf-8");
20439
20529
  return JSON.parse(content);
20440
20530
  } catch {
20441
20531
  return { version: 1, marketplaces: {} };
@@ -20444,7 +20534,7 @@ async function loadRegistry() {
20444
20534
  async function saveRegistry(registry) {
20445
20535
  const registryPath = getRegistryPath();
20446
20536
  const dir = getAllagentsDir();
20447
- if (!existsSync4(dir)) {
20537
+ if (!existsSync5(dir)) {
20448
20538
  await mkdir3(dir, { recursive: true });
20449
20539
  }
20450
20540
  await writeFile2(registryPath, `${JSON.stringify(registry, null, 2)}
@@ -20473,7 +20563,7 @@ function parseMarketplaceSource(source) {
20473
20563
  return null;
20474
20564
  }
20475
20565
  if (source.startsWith("/") || source.startsWith(".")) {
20476
- const absPath = resolve3(source);
20566
+ const absPath = resolve4(source);
20477
20567
  const name = basename2(absPath) || "local";
20478
20568
  return {
20479
20569
  type: "local",
@@ -20502,7 +20592,7 @@ async function addMarketplace(source, customName) {
20502
20592
  Use: GitHub URL, owner/repo, local path, or well-known name`
20503
20593
  };
20504
20594
  }
20505
- const name = customName || parsed.name;
20595
+ let name = customName || parsed.name;
20506
20596
  const registry = await loadRegistry();
20507
20597
  if (registry.marketplaces[name]) {
20508
20598
  return {
@@ -20512,8 +20602,8 @@ async function addMarketplace(source, customName) {
20512
20602
  }
20513
20603
  let marketplacePath;
20514
20604
  if (parsed.type === "github") {
20515
- marketplacePath = join5(getMarketplacesDir(), name);
20516
- if (existsSync4(marketplacePath)) {} else {
20605
+ marketplacePath = join6(getMarketplacesDir(), name);
20606
+ if (existsSync5(marketplacePath)) {} else {
20517
20607
  try {
20518
20608
  await execa("gh", ["--version"]);
20519
20609
  } catch {
@@ -20524,7 +20614,7 @@ async function addMarketplace(source, customName) {
20524
20614
  };
20525
20615
  }
20526
20616
  const parentDir = getMarketplacesDir();
20527
- if (!existsSync4(parentDir)) {
20617
+ if (!existsSync5(parentDir)) {
20528
20618
  await mkdir3(parentDir, { recursive: true });
20529
20619
  }
20530
20620
  try {
@@ -20545,13 +20635,29 @@ async function addMarketplace(source, customName) {
20545
20635
  }
20546
20636
  } else {
20547
20637
  marketplacePath = parsed.location;
20548
- if (!existsSync4(marketplacePath)) {
20638
+ if (!existsSync5(marketplacePath)) {
20549
20639
  return {
20550
20640
  success: false,
20551
20641
  error: `Local directory not found: ${marketplacePath}`
20552
20642
  };
20553
20643
  }
20554
20644
  }
20645
+ if (!customName) {
20646
+ const manifestResult = await parseMarketplaceManifest(marketplacePath);
20647
+ if (manifestResult.success && manifestResult.data.name) {
20648
+ const manifestName = manifestResult.data.name;
20649
+ if (manifestName !== name) {
20650
+ const existing = registry.marketplaces[manifestName];
20651
+ if (existing) {
20652
+ return {
20653
+ success: true,
20654
+ marketplace: existing
20655
+ };
20656
+ }
20657
+ name = manifestName;
20658
+ }
20659
+ }
20660
+ }
20555
20661
  const entry = {
20556
20662
  name,
20557
20663
  source: {
@@ -20607,7 +20713,7 @@ async function updateMarketplace(name) {
20607
20713
  });
20608
20714
  continue;
20609
20715
  }
20610
- if (!existsSync4(marketplace.path)) {
20716
+ if (!existsSync5(marketplace.path)) {
20611
20717
  results.push({
20612
20718
  name: marketplace.name,
20613
20719
  success: false,
@@ -20634,20 +20740,44 @@ async function updateMarketplace(name) {
20634
20740
  await saveRegistry(registry);
20635
20741
  return results;
20636
20742
  }
20743
+ async function getMarketplacePluginsFromManifest(marketplacePath) {
20744
+ const result = await parseMarketplaceManifest(marketplacePath);
20745
+ if (!result.success) {
20746
+ return [];
20747
+ }
20748
+ return result.data.plugins.map((plugin) => {
20749
+ const resolvedSource = resolvePluginSourcePath(plugin.source, marketplacePath);
20750
+ const info = {
20751
+ name: plugin.name,
20752
+ path: typeof plugin.source === "string" ? resolve4(marketplacePath, plugin.source) : resolvedSource,
20753
+ description: plugin.description,
20754
+ source: resolvedSource
20755
+ };
20756
+ if (plugin.category)
20757
+ info.category = plugin.category;
20758
+ if (plugin.homepage)
20759
+ info.homepage = plugin.homepage;
20760
+ return info;
20761
+ });
20762
+ }
20637
20763
  async function listMarketplacePlugins(name) {
20638
20764
  const marketplace = await getMarketplace(name);
20639
20765
  if (!marketplace) {
20640
20766
  return [];
20641
20767
  }
20642
- const pluginsDir = join5(marketplace.path, "plugins");
20643
- if (!existsSync4(pluginsDir)) {
20768
+ const manifestPlugins = await getMarketplacePluginsFromManifest(marketplace.path);
20769
+ if (manifestPlugins.length > 0) {
20770
+ return manifestPlugins;
20771
+ }
20772
+ const pluginsDir = join6(marketplace.path, "plugins");
20773
+ if (!existsSync5(pluginsDir)) {
20644
20774
  return [];
20645
20775
  }
20646
20776
  try {
20647
20777
  const entries = await readdir3(pluginsDir, { withFileTypes: true });
20648
20778
  return entries.filter((e) => e.isDirectory()).map((e) => ({
20649
20779
  name: e.name,
20650
- path: join5(pluginsDir, e.name)
20780
+ path: join6(pluginsDir, e.name)
20651
20781
  })).sort((a, b) => a.name.localeCompare(b.name));
20652
20782
  } catch {
20653
20783
  return [];
@@ -20688,21 +20818,114 @@ async function resolvePluginSpec(spec, options2 = {}) {
20688
20818
  if (!parsed) {
20689
20819
  return null;
20690
20820
  }
20691
- const marketplace = await getMarketplace(parsed.marketplaceName);
20821
+ const marketplaceName = options2.marketplaceNameOverride ?? parsed.marketplaceName;
20822
+ const marketplace = await getMarketplace(marketplaceName);
20692
20823
  if (!marketplace) {
20693
20824
  return null;
20694
20825
  }
20826
+ const manifestResult = await parseMarketplaceManifest(marketplace.path);
20827
+ if (manifestResult.success) {
20828
+ const pluginEntry = manifestResult.data.plugins.find((p) => p.name === parsed.plugin);
20829
+ if (pluginEntry) {
20830
+ const resolvedSource = typeof pluginEntry.source === "string" ? resolve4(marketplace.path, pluginEntry.source) : pluginEntry.source.url;
20831
+ if (typeof resolvedSource === "string" && existsSync5(resolvedSource)) {
20832
+ return {
20833
+ path: resolvedSource,
20834
+ marketplace: marketplaceName,
20835
+ plugin: parsed.plugin
20836
+ };
20837
+ }
20838
+ }
20839
+ }
20695
20840
  const subpath = options2.subpath ?? parsed.subpath ?? "plugins";
20696
- const pluginPath = join5(marketplace.path, subpath, parsed.plugin);
20697
- if (!existsSync4(pluginPath)) {
20841
+ const pluginPath = join6(marketplace.path, subpath, parsed.plugin);
20842
+ if (!existsSync5(pluginPath)) {
20698
20843
  return null;
20699
20844
  }
20700
20845
  return {
20701
20846
  path: pluginPath,
20702
- marketplace: parsed.marketplaceName,
20847
+ marketplace: marketplaceName,
20703
20848
  plugin: parsed.plugin
20704
20849
  };
20705
20850
  }
20851
+ async function resolvePluginSpecWithAutoRegister(spec) {
20852
+ const parsed = parsePluginSpec(spec);
20853
+ if (!parsed) {
20854
+ return {
20855
+ success: false,
20856
+ error: `Invalid plugin spec format: ${spec}
20857
+ Expected: plugin@marketplace or plugin@owner/repo[/subpath]`
20858
+ };
20859
+ }
20860
+ const { plugin: pluginName, marketplaceName, owner, repo, subpath } = parsed;
20861
+ let marketplace = await getMarketplace(marketplaceName);
20862
+ if (!marketplace) {
20863
+ const sourceToRegister = owner && repo ? `${owner}/${repo}` : marketplaceName;
20864
+ const autoRegResult = await autoRegisterMarketplace(sourceToRegister);
20865
+ if (!autoRegResult.success) {
20866
+ return {
20867
+ success: false,
20868
+ error: autoRegResult.error || "Unknown error"
20869
+ };
20870
+ }
20871
+ marketplace = await getMarketplace(autoRegResult.name ?? marketplaceName);
20872
+ }
20873
+ if (!marketplace) {
20874
+ return {
20875
+ success: false,
20876
+ error: `Marketplace '${marketplaceName}' not found`
20877
+ };
20878
+ }
20879
+ const expectedSubpath = subpath ?? "plugins";
20880
+ const resolved = await resolvePluginSpec(spec, {
20881
+ ...subpath && { subpath },
20882
+ marketplaceNameOverride: marketplace.name
20883
+ });
20884
+ if (!resolved) {
20885
+ return {
20886
+ success: false,
20887
+ error: `Plugin '${pluginName}' not found in marketplace '${marketplaceName}'
20888
+ Expected at: ${marketplace.path}/${expectedSubpath}/${pluginName}/`
20889
+ };
20890
+ }
20891
+ return {
20892
+ success: true,
20893
+ path: resolved.path,
20894
+ pluginName: resolved.plugin,
20895
+ ...marketplace.name !== marketplaceName && { registeredAs: marketplace.name }
20896
+ };
20897
+ }
20898
+ async function autoRegisterMarketplace(name) {
20899
+ const wellKnown = getWellKnownMarketplaces();
20900
+ if (wellKnown[name]) {
20901
+ console.log(`Auto-registering well-known marketplace: ${name}`);
20902
+ const result = await addMarketplace(name);
20903
+ if (!result.success) {
20904
+ return { success: false, error: result.error || "Unknown error" };
20905
+ }
20906
+ return { success: true, name };
20907
+ }
20908
+ if (name.includes("/") && !name.includes("://")) {
20909
+ const parts = name.split("/");
20910
+ if (parts.length === 2 && parts[0] && parts[1]) {
20911
+ console.log(`Auto-registering GitHub marketplace: ${name}`);
20912
+ const result = await addMarketplace(name);
20913
+ if (!result.success) {
20914
+ return { success: false, error: result.error || "Unknown error" };
20915
+ }
20916
+ const registeredName = result.marketplace?.name ?? parts[1];
20917
+ return { success: true, name: registeredName };
20918
+ }
20919
+ }
20920
+ return {
20921
+ success: false,
20922
+ error: `Marketplace '${name}' not found.
20923
+ Options:
20924
+ 1. Use fully qualified name: plugin@owner/repo
20925
+ 2. Register first: allagents plugin marketplace add <source>
20926
+ 3. Well-known marketplaces: ${Object.keys(wellKnown).join(", ")}`
20927
+ };
20928
+ }
20706
20929
  function isPluginSpec(spec) {
20707
20930
  const atIndex = spec.lastIndexOf("@");
20708
20931
  if (atIndex === -1 || atIndex === 0 || atIndex === spec.length - 1) {
@@ -20715,9 +20938,9 @@ function getWellKnownMarketplaces() {
20715
20938
  }
20716
20939
 
20717
20940
  // src/core/sync-state.ts
20718
- import { readFile as readFile6, writeFile as writeFile3, mkdir as mkdir4 } from "node:fs/promises";
20719
- import { existsSync as existsSync5 } from "node:fs";
20720
- import { join as join6, dirname as dirname3 } from "node:path";
20941
+ import { readFile as readFile7, writeFile as writeFile3, mkdir as mkdir4 } from "node:fs/promises";
20942
+ import { existsSync as existsSync6 } from "node:fs";
20943
+ import { join as join7, dirname as dirname3 } from "node:path";
20721
20944
 
20722
20945
  // src/models/sync-state.ts
20723
20946
  var SyncStateSchema = exports_external.object({
@@ -20728,15 +20951,15 @@ var SyncStateSchema = exports_external.object({
20728
20951
 
20729
20952
  // src/core/sync-state.ts
20730
20953
  function getSyncStatePath(workspacePath) {
20731
- return join6(workspacePath, CONFIG_DIR, SYNC_STATE_FILE);
20954
+ return join7(workspacePath, CONFIG_DIR, SYNC_STATE_FILE);
20732
20955
  }
20733
20956
  async function loadSyncState(workspacePath) {
20734
20957
  const statePath = getSyncStatePath(workspacePath);
20735
- if (!existsSync5(statePath)) {
20958
+ if (!existsSync6(statePath)) {
20736
20959
  return null;
20737
20960
  }
20738
20961
  try {
20739
- const content = await readFile6(statePath, "utf-8");
20962
+ const content = await readFile7(statePath, "utf-8");
20740
20963
  const parsed = JSON.parse(content);
20741
20964
  const result = SyncStateSchema.safeParse(parsed);
20742
20965
  if (!result.success) {
@@ -20765,19 +20988,19 @@ function getPreviouslySyncedFiles(state, client) {
20765
20988
  }
20766
20989
 
20767
20990
  // src/core/sync.ts
20768
- async function selectivePurgeWorkspace(workspacePath, state, clients) {
20991
+ async function selectivePurgeWorkspace(workspacePath, state, clients, options2) {
20769
20992
  if (!state) {
20770
20993
  return [];
20771
20994
  }
20772
20995
  const result = [];
20773
20996
  const previousClients = Object.keys(state.files);
20774
- const clientsToProcess = [...new Set([...clients, ...previousClients])];
20997
+ const clientsToProcess = options2?.partialSync ? clients : [...new Set([...clients, ...previousClients])];
20775
20998
  for (const client of clientsToProcess) {
20776
20999
  const previousFiles = getPreviouslySyncedFiles(state, client);
20777
21000
  const purgedPaths = [];
20778
21001
  for (const filePath of previousFiles) {
20779
- const fullPath = join7(workspacePath, filePath);
20780
- if (!existsSync6(fullPath)) {
21002
+ const fullPath = join8(workspacePath, filePath);
21003
+ if (!existsSync7(fullPath)) {
20781
21004
  continue;
20782
21005
  }
20783
21006
  try {
@@ -20799,8 +21022,8 @@ async function selectivePurgeWorkspace(workspacePath, state, clients) {
20799
21022
  async function cleanupEmptyParents(workspacePath, filePath) {
20800
21023
  let parentPath = dirname4(filePath);
20801
21024
  while (parentPath && parentPath !== "." && parentPath !== "/") {
20802
- const fullParentPath = join7(workspacePath, parentPath);
20803
- if (!existsSync6(fullParentPath)) {
21025
+ const fullParentPath = join8(workspacePath, parentPath);
21026
+ if (!existsSync7(fullParentPath)) {
20804
21027
  parentPath = dirname4(parentPath);
20805
21028
  continue;
20806
21029
  }
@@ -20885,8 +21108,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
20885
21108
  errors2.push(`Cannot resolve file '${file}' - no workspace.source configured`);
20886
21109
  continue;
20887
21110
  }
20888
- const fullPath = join7(defaultSourcePath, file);
20889
- if (!existsSync6(fullPath)) {
21111
+ const fullPath = join8(defaultSourcePath, file);
21112
+ if (!existsSync7(fullPath)) {
20890
21113
  errors2.push(`File source not found: ${fullPath}`);
20891
21114
  }
20892
21115
  continue;
@@ -20904,8 +21127,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
20904
21127
  errors2.push(`GitHub cache not found for ${cacheKey}`);
20905
21128
  continue;
20906
21129
  }
20907
- const fullPath = join7(cachePath, parsed.filePath);
20908
- if (!existsSync6(fullPath)) {
21130
+ const fullPath = join8(cachePath, parsed.filePath);
21131
+ if (!existsSync7(fullPath)) {
20909
21132
  errors2.push(`Path not found in repository: ${cacheKey}/${parsed.filePath}`);
20910
21133
  }
20911
21134
  } else {
@@ -20913,13 +21136,13 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
20913
21136
  if (file.source.startsWith("/")) {
20914
21137
  fullPath = file.source;
20915
21138
  } else if (file.source.startsWith("../")) {
20916
- fullPath = resolve4(file.source);
21139
+ fullPath = resolve5(file.source);
20917
21140
  } else if (defaultSourcePath) {
20918
- fullPath = join7(defaultSourcePath, file.source);
21141
+ fullPath = join8(defaultSourcePath, file.source);
20919
21142
  } else {
20920
- fullPath = resolve4(file.source);
21143
+ fullPath = resolve5(file.source);
20921
21144
  }
20922
- if (!existsSync6(fullPath)) {
21145
+ if (!existsSync7(fullPath)) {
20923
21146
  errors2.push(`File source not found: ${fullPath}`);
20924
21147
  }
20925
21148
  }
@@ -20928,8 +21151,8 @@ function validateFileSources(files, defaultSourcePath, githubCache) {
20928
21151
  errors2.push(`Cannot resolve file '${file.dest}' - no workspace.source configured and no explicit source provided`);
20929
21152
  continue;
20930
21153
  }
20931
- const fullPath = join7(defaultSourcePath, file.dest ?? "");
20932
- if (!existsSync6(fullPath)) {
21154
+ const fullPath = join8(defaultSourcePath, file.dest ?? "");
21155
+ if (!existsSync7(fullPath)) {
20933
21156
  errors2.push(`File source not found: ${fullPath}`);
20934
21157
  }
20935
21158
  }
@@ -20965,7 +21188,7 @@ function collectSyncedPaths(copyResults, workspacePath, clients) {
20965
21188
  }
20966
21189
  async function validatePlugin(pluginSource, workspacePath, offline) {
20967
21190
  if (isPluginSpec(pluginSource)) {
20968
- const resolved = await resolvePluginSpecWithAutoRegister(pluginSource, offline);
21191
+ const resolved = await resolvePluginSpecWithAutoRegister(pluginSource);
20969
21192
  if (!resolved.success) {
20970
21193
  return {
20971
21194
  plugin: pluginSource,
@@ -20977,7 +21200,8 @@ async function validatePlugin(pluginSource, workspacePath, offline) {
20977
21200
  return {
20978
21201
  plugin: pluginSource,
20979
21202
  resolved: resolved.path ?? "",
20980
- success: true
21203
+ success: true,
21204
+ ...resolved.pluginName && { pluginName: resolved.pluginName }
20981
21205
  };
20982
21206
  }
20983
21207
  if (isGitHubUrl(pluginSource)) {
@@ -20994,15 +21218,15 @@ async function validatePlugin(pluginSource, workspacePath, offline) {
20994
21218
  ...fetchResult.error && { error: fetchResult.error }
20995
21219
  };
20996
21220
  }
20997
- const resolvedPath2 = parsed?.subpath ? join7(fetchResult.cachePath, parsed.subpath) : fetchResult.cachePath;
21221
+ const resolvedPath2 = parsed?.subpath ? join8(fetchResult.cachePath, parsed.subpath) : fetchResult.cachePath;
20998
21222
  return {
20999
21223
  plugin: pluginSource,
21000
21224
  resolved: resolvedPath2,
21001
21225
  success: true
21002
21226
  };
21003
21227
  }
21004
- const resolvedPath = resolve4(workspacePath, pluginSource);
21005
- if (!existsSync6(resolvedPath)) {
21228
+ const resolvedPath = resolve5(workspacePath, pluginSource);
21229
+ if (!existsSync7(resolvedPath)) {
21006
21230
  return {
21007
21231
  plugin: pluginSource,
21008
21232
  resolved: resolvedPath,
@@ -21036,7 +21260,7 @@ async function copyValidatedPlugin(validatedPlugin, workspacePath, clients, dryR
21036
21260
  async function collectAllSkills(validatedPlugins) {
21037
21261
  const allSkills = [];
21038
21262
  for (const plugin of validatedPlugins) {
21039
- const pluginName = await getPluginName(plugin.resolved);
21263
+ const pluginName = plugin.pluginName ?? await getPluginName(plugin.resolved);
21040
21264
  const skills = await collectPluginSkills(plugin.resolved, plugin.plugin);
21041
21265
  for (const skill of skills) {
21042
21266
  allSkills.push({
@@ -21076,9 +21300,9 @@ function buildPluginSkillNameMaps(allSkills) {
21076
21300
  }
21077
21301
  async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
21078
21302
  const { offline = false, dryRun = false, workspaceSourceBase } = options2;
21079
- const configDir = join7(workspacePath, CONFIG_DIR);
21080
- const configPath = join7(configDir, WORKSPACE_CONFIG_FILE);
21081
- if (!existsSync6(configPath)) {
21303
+ const configDir = join8(workspacePath, CONFIG_DIR);
21304
+ const configPath = join8(configDir, WORKSPACE_CONFIG_FILE);
21305
+ if (!existsSync7(configPath)) {
21082
21306
  return {
21083
21307
  success: false,
21084
21308
  pluginResults: [],
@@ -21104,6 +21328,22 @@ async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
21104
21328
  error: error instanceof Error ? error.message : `Failed to parse ${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE}`
21105
21329
  };
21106
21330
  }
21331
+ const clients = options2.clients ? config.clients.filter((c) => options2.clients?.includes(c)) : config.clients;
21332
+ if (options2.clients) {
21333
+ const invalidClients = options2.clients.filter((c) => !config.clients.includes(c));
21334
+ if (invalidClients.length > 0) {
21335
+ return {
21336
+ success: false,
21337
+ pluginResults: [],
21338
+ totalCopied: 0,
21339
+ totalFailed: 0,
21340
+ totalSkipped: 0,
21341
+ totalGenerated: 0,
21342
+ error: `Client(s) not configured in workspace.yaml: ${invalidClients.join(", ")}
21343
+ Configured clients: ${config.clients.join(", ")}`
21344
+ };
21345
+ }
21346
+ }
21107
21347
  const validatedPlugins = await validateAllPlugins(config.plugins, workspacePath, offline);
21108
21348
  let validatedWorkspaceSource = null;
21109
21349
  if (config.workspace?.source) {
@@ -21143,18 +21383,20 @@ ${errors2}`
21143
21383
  };
21144
21384
  }
21145
21385
  const previousState = await loadSyncState(workspacePath);
21146
- const purgedPaths = previousState ? config.clients.map((client) => ({
21386
+ const purgedPaths = previousState ? clients.map((client) => ({
21147
21387
  client,
21148
21388
  paths: getPreviouslySyncedFiles(previousState, client)
21149
21389
  })).filter((p) => p.paths.length > 0) : [];
21150
21390
  if (!dryRun) {
21151
- await selectivePurgeWorkspace(workspacePath, previousState, config.clients);
21391
+ await selectivePurgeWorkspace(workspacePath, previousState, clients, {
21392
+ partialSync: !!options2.clients
21393
+ });
21152
21394
  }
21153
21395
  const allSkills = await collectAllSkills(validatedPlugins);
21154
21396
  const pluginSkillMaps = buildPluginSkillNameMaps(allSkills);
21155
21397
  const pluginResults = await Promise.all(validatedPlugins.map((validatedPlugin) => {
21156
21398
  const skillNameMap = pluginSkillMaps.get(validatedPlugin.resolved);
21157
- return copyValidatedPlugin(validatedPlugin, workspacePath, config.clients, dryRun, skillNameMap);
21399
+ return copyValidatedPlugin(validatedPlugin, workspacePath, clients, dryRun, skillNameMap);
21158
21400
  }));
21159
21401
  let workspaceFileResults = [];
21160
21402
  if (config.workspace) {
@@ -21162,8 +21404,8 @@ ${errors2}`
21162
21404
  const filesToCopy = [...config.workspace.files];
21163
21405
  if (sourcePath) {
21164
21406
  for (const agentFile of AGENT_FILES) {
21165
- const agentPath = join7(sourcePath, agentFile);
21166
- if (existsSync6(agentPath) && !filesToCopy.includes(agentFile)) {
21407
+ const agentPath = join8(sourcePath, agentFile);
21408
+ if (existsSync7(agentPath) && !filesToCopy.includes(agentFile)) {
21167
21409
  filesToCopy.push(agentFile);
21168
21410
  }
21169
21411
  }
@@ -21202,11 +21444,11 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
21202
21444
  };
21203
21445
  }
21204
21446
  workspaceFileResults = await copyWorkspaceFiles(sourcePath, workspacePath, filesToCopy, { dryRun, githubCache });
21205
- if (!dryRun && config.clients.includes("claude") && sourcePath) {
21206
- const claudePath = join7(workspacePath, "CLAUDE.md");
21207
- const agentsPath = join7(workspacePath, "AGENTS.md");
21208
- const claudeExistsInSource = existsSync6(join7(sourcePath, "CLAUDE.md"));
21209
- if (!claudeExistsInSource && existsSync6(agentsPath) && !existsSync6(claudePath)) {
21447
+ if (!dryRun && clients.includes("claude") && sourcePath) {
21448
+ const claudePath = join8(workspacePath, "CLAUDE.md");
21449
+ const agentsPath = join8(workspacePath, "AGENTS.md");
21450
+ const claudeExistsInSource = existsSync7(join8(sourcePath, "CLAUDE.md"));
21451
+ if (!claudeExistsInSource && existsSync7(agentsPath) && !existsSync7(claudePath)) {
21210
21452
  await copyFile(agentsPath, claudePath);
21211
21453
  }
21212
21454
  }
@@ -21252,7 +21494,14 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
21252
21494
  ...pluginResults.flatMap((r) => r.copyResults),
21253
21495
  ...workspaceFileResults
21254
21496
  ];
21255
- const syncedFiles = collectSyncedPaths(allCopyResults, workspacePath, config.clients);
21497
+ const syncedFiles = collectSyncedPaths(allCopyResults, workspacePath, clients);
21498
+ if (options2.clients && previousState) {
21499
+ for (const [client, files] of Object.entries(previousState.files)) {
21500
+ if (!clients.includes(client)) {
21501
+ syncedFiles[client] = files;
21502
+ }
21503
+ }
21504
+ }
21256
21505
  await saveSyncState(workspacePath, syncedFiles);
21257
21506
  }
21258
21507
  return {
@@ -21265,79 +21514,6 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
21265
21514
  purgedPaths
21266
21515
  };
21267
21516
  }
21268
- async function resolvePluginSpecWithAutoRegister(spec, _offline = false) {
21269
- const parsed = parsePluginSpec(spec);
21270
- if (!parsed) {
21271
- return {
21272
- success: false,
21273
- error: `Invalid plugin spec format: ${spec}
21274
- Expected: plugin@marketplace or plugin@owner/repo[/subpath]`
21275
- };
21276
- }
21277
- const { plugin: pluginName, marketplaceName, owner, repo, subpath } = parsed;
21278
- let marketplace = await getMarketplace(marketplaceName);
21279
- if (!marketplace) {
21280
- const sourceToRegister = owner && repo ? `${owner}/${repo}` : marketplaceName;
21281
- const autoRegResult = await autoRegisterMarketplace(sourceToRegister);
21282
- if (!autoRegResult.success) {
21283
- return {
21284
- success: false,
21285
- error: autoRegResult.error || "Unknown error"
21286
- };
21287
- }
21288
- marketplace = await getMarketplace(autoRegResult.name ?? marketplaceName);
21289
- }
21290
- if (!marketplace) {
21291
- return {
21292
- success: false,
21293
- error: `Marketplace '${marketplaceName}' not found`
21294
- };
21295
- }
21296
- const expectedSubpath = subpath ?? "plugins";
21297
- const resolved = await resolvePluginSpec(spec, subpath ? { subpath } : {});
21298
- if (!resolved) {
21299
- return {
21300
- success: false,
21301
- error: `Plugin '${pluginName}' not found in marketplace '${marketplaceName}'
21302
- Expected at: ${marketplace.path}/${expectedSubpath}/${pluginName}/`
21303
- };
21304
- }
21305
- return {
21306
- success: true,
21307
- path: resolved.path
21308
- };
21309
- }
21310
- async function autoRegisterMarketplace(name) {
21311
- const wellKnown = getWellKnownMarketplaces();
21312
- if (wellKnown[name]) {
21313
- console.log(`Auto-registering well-known marketplace: ${name}`);
21314
- const result = await addMarketplace(name);
21315
- if (!result.success) {
21316
- return { success: false, error: result.error || "Unknown error" };
21317
- }
21318
- return { success: true, name };
21319
- }
21320
- if (name.includes("/") && !name.includes("://")) {
21321
- const parts = name.split("/");
21322
- if (parts.length === 2 && parts[0] && parts[1]) {
21323
- console.log(`Auto-registering GitHub marketplace: ${name}`);
21324
- const repoName = parts[1];
21325
- const result = await addMarketplace(name, repoName);
21326
- if (!result.success) {
21327
- return { success: false, error: result.error || "Unknown error" };
21328
- }
21329
- return { success: true, name: repoName };
21330
- }
21331
- }
21332
- return {
21333
- success: false,
21334
- error: `Marketplace '${name}' not found.
21335
- Options:
21336
- 1. Use fully qualified name: plugin@owner/repo
21337
- 2. Register first: allagents plugin marketplace add <source>
21338
- 3. Well-known marketplaces: ${Object.keys(wellKnown).join(", ")}`
21339
- };
21340
- }
21341
21517
 
21342
21518
  // src/core/github-fetch.ts
21343
21519
  async function resolveBranchAndSubpath(owner, repo, pathAfterTree) {
@@ -21465,17 +21641,17 @@ async function fetchWorkspaceFromGitHub(url) {
21465
21641
 
21466
21642
  // src/core/workspace.ts
21467
21643
  async function initWorkspace(targetPath = ".", options2 = {}) {
21468
- const absoluteTarget = resolve5(targetPath);
21469
- const configDir = join8(absoluteTarget, CONFIG_DIR);
21470
- const configPath = join8(configDir, WORKSPACE_CONFIG_FILE);
21471
- if (existsSync7(configPath)) {
21644
+ const absoluteTarget = resolve6(targetPath);
21645
+ const configDir = join9(absoluteTarget, CONFIG_DIR);
21646
+ const configPath = join9(configDir, WORKSPACE_CONFIG_FILE);
21647
+ if (existsSync8(configPath)) {
21472
21648
  throw new Error(`Workspace already exists: ${absoluteTarget}
21473
21649
  Found existing ${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE}`);
21474
21650
  }
21475
21651
  const currentFilePath = fileURLToPath2(import.meta.url);
21476
21652
  const currentFileDir = dirname5(currentFilePath);
21477
21653
  const isProduction = currentFilePath.includes(`${sep}dist${sep}`);
21478
- const defaultTemplatePath = isProduction ? join8(currentFileDir, "templates", "default") : join8(currentFileDir, "..", "templates", "default");
21654
+ const defaultTemplatePath = isProduction ? join9(currentFileDir, "templates", "default") : join9(currentFileDir, "..", "templates", "default");
21479
21655
  try {
21480
21656
  await mkdir5(absoluteTarget, { recursive: true });
21481
21657
  await mkdir5(configDir, { recursive: true });
@@ -21506,20 +21682,20 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
21506
21682
  }
21507
21683
  console.log(`✓ Using workspace.yaml from: ${options2.from}`);
21508
21684
  } else {
21509
- const fromPath = resolve5(options2.from);
21510
- if (!existsSync7(fromPath)) {
21685
+ const fromPath = resolve6(options2.from);
21686
+ if (!existsSync8(fromPath)) {
21511
21687
  throw new Error(`Template not found: ${fromPath}`);
21512
21688
  }
21513
21689
  const { stat: stat2 } = await import("node:fs/promises");
21514
21690
  const fromStat = await stat2(fromPath);
21515
21691
  let sourceYamlPath;
21516
21692
  if (fromStat.isDirectory()) {
21517
- const nestedPath = join8(fromPath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21518
- const rootPath = join8(fromPath, WORKSPACE_CONFIG_FILE);
21519
- if (existsSync7(nestedPath)) {
21693
+ const nestedPath = join9(fromPath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21694
+ const rootPath = join9(fromPath, WORKSPACE_CONFIG_FILE);
21695
+ if (existsSync8(nestedPath)) {
21520
21696
  sourceYamlPath = nestedPath;
21521
21697
  sourceDir = fromPath;
21522
- } else if (existsSync7(rootPath)) {
21698
+ } else if (existsSync8(rootPath)) {
21523
21699
  sourceYamlPath = rootPath;
21524
21700
  sourceDir = fromPath;
21525
21701
  } else {
@@ -21535,14 +21711,14 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
21535
21711
  sourceDir = parentDir;
21536
21712
  }
21537
21713
  }
21538
- workspaceYamlContent = await readFile7(sourceYamlPath, "utf-8");
21714
+ workspaceYamlContent = await readFile8(sourceYamlPath, "utf-8");
21539
21715
  if (sourceDir) {
21540
21716
  const parsed2 = load(workspaceYamlContent);
21541
21717
  const workspace = parsed2?.workspace;
21542
21718
  if (workspace?.source) {
21543
21719
  const source = workspace.source;
21544
21720
  if (!isGitHubUrl(source) && !isAbsolute2(source)) {
21545
- workspace.source = resolve5(sourceDir, source);
21721
+ workspace.source = resolve6(sourceDir, source);
21546
21722
  workspaceYamlContent = dump(parsed2, { lineWidth: -1 });
21547
21723
  }
21548
21724
  }
@@ -21550,11 +21726,11 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
21550
21726
  console.log(`✓ Using workspace.yaml from: ${sourceYamlPath}`);
21551
21727
  }
21552
21728
  } else {
21553
- const defaultYamlPath = join8(defaultTemplatePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21554
- if (!existsSync7(defaultYamlPath)) {
21729
+ const defaultYamlPath = join9(defaultTemplatePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21730
+ if (!existsSync8(defaultYamlPath)) {
21555
21731
  throw new Error(`Default template not found at: ${defaultTemplatePath}`);
21556
21732
  }
21557
- workspaceYamlContent = await readFile7(defaultYamlPath, "utf-8");
21733
+ workspaceYamlContent = await readFile8(defaultYamlPath, "utf-8");
21558
21734
  }
21559
21735
  await writeFile4(configPath, workspaceYamlContent, "utf-8");
21560
21736
  const copiedAgentFiles = [];
@@ -21566,7 +21742,7 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
21566
21742
  const filePath = basePath ? `${basePath}/${agentFile}` : agentFile;
21567
21743
  const content = await fetchFileFromGitHub(parsedUrl.owner, parsedUrl.repo, filePath, parsedUrl.branch);
21568
21744
  if (content) {
21569
- await writeFile4(join8(absoluteTarget, agentFile), content, "utf-8");
21745
+ await writeFile4(join9(absoluteTarget, agentFile), content, "utf-8");
21570
21746
  copiedAgentFiles.push(agentFile);
21571
21747
  }
21572
21748
  }
@@ -21574,27 +21750,27 @@ async function initWorkspace(targetPath = ".", options2 = {}) {
21574
21750
  } else {
21575
21751
  const effectiveSourceDir = sourceDir ?? defaultTemplatePath;
21576
21752
  for (const agentFile of AGENT_FILES) {
21577
- const sourcePath = join8(effectiveSourceDir, agentFile);
21578
- if (existsSync7(sourcePath)) {
21579
- const content = await readFile7(sourcePath, "utf-8");
21580
- await writeFile4(join8(absoluteTarget, agentFile), content, "utf-8");
21753
+ const sourcePath = join9(effectiveSourceDir, agentFile);
21754
+ if (existsSync8(sourcePath)) {
21755
+ const content = await readFile8(sourcePath, "utf-8");
21756
+ await writeFile4(join9(absoluteTarget, agentFile), content, "utf-8");
21581
21757
  copiedAgentFiles.push(agentFile);
21582
21758
  }
21583
21759
  }
21584
21760
  }
21585
21761
  if (copiedAgentFiles.length === 0) {
21586
- await ensureWorkspaceRules(join8(absoluteTarget, "AGENTS.md"));
21762
+ await ensureWorkspaceRules(join9(absoluteTarget, "AGENTS.md"));
21587
21763
  copiedAgentFiles.push("AGENTS.md");
21588
21764
  } else {
21589
21765
  for (const agentFile of copiedAgentFiles) {
21590
- await ensureWorkspaceRules(join8(absoluteTarget, agentFile));
21766
+ await ensureWorkspaceRules(join9(absoluteTarget, agentFile));
21591
21767
  }
21592
21768
  }
21593
21769
  const parsed = load(workspaceYamlContent);
21594
21770
  const clients = parsed?.clients ?? [];
21595
21771
  if (clients.includes("claude") && !copiedAgentFiles.includes("CLAUDE.md") && copiedAgentFiles.includes("AGENTS.md")) {
21596
- const agentsPath = join8(absoluteTarget, "AGENTS.md");
21597
- const claudePath = join8(absoluteTarget, "CLAUDE.md");
21772
+ const agentsPath = join9(absoluteTarget, "AGENTS.md");
21773
+ const claudePath = join9(absoluteTarget, "CLAUDE.md");
21598
21774
  await copyFile2(agentsPath, claudePath);
21599
21775
  }
21600
21776
  console.log(`✓ Workspace created at: ${absoluteTarget}`);
@@ -21628,11 +21804,11 @@ Next steps:`);
21628
21804
  }
21629
21805
 
21630
21806
  // src/core/status.ts
21631
- import { existsSync as existsSync8 } from "node:fs";
21632
- import { join as join9 } from "node:path";
21807
+ import { existsSync as existsSync9 } from "node:fs";
21808
+ import { join as join10 } from "node:path";
21633
21809
  async function getWorkspaceStatus(workspacePath = process.cwd()) {
21634
- const configPath = join9(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21635
- if (!existsSync8(configPath)) {
21810
+ const configPath = join10(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21811
+ if (!existsSync9(configPath)) {
21636
21812
  return {
21637
21813
  success: false,
21638
21814
  error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
@@ -21645,9 +21821,14 @@ async function getWorkspaceStatus(workspacePath = process.cwd()) {
21645
21821
  const config = await parseWorkspaceConfig(configPath);
21646
21822
  const plugins = [];
21647
21823
  for (const pluginSource of config.plugins) {
21648
- const parsed = parsePluginSource(pluginSource, workspacePath);
21649
- const status = getPluginStatus(parsed);
21650
- plugins.push(status);
21824
+ if (isPluginSpec(pluginSource)) {
21825
+ const status = await getMarketplacePluginStatus(pluginSource);
21826
+ plugins.push(status);
21827
+ } else {
21828
+ const parsed = parsePluginSource(pluginSource, workspacePath);
21829
+ const status = getPluginStatus(parsed);
21830
+ plugins.push(status);
21831
+ }
21651
21832
  }
21652
21833
  return {
21653
21834
  success: true,
@@ -21666,7 +21847,7 @@ async function getWorkspaceStatus(workspacePath = process.cwd()) {
21666
21847
  function getPluginStatus(parsed) {
21667
21848
  if (parsed.type === "github") {
21668
21849
  const cachePath = parsed.owner && parsed.repo ? getPluginCachePath(parsed.owner, parsed.repo) : "";
21669
- const available2 = cachePath ? existsSync8(cachePath) : false;
21850
+ const available2 = cachePath ? existsSync9(cachePath) : false;
21670
21851
  return {
21671
21852
  source: parsed.original,
21672
21853
  type: "github",
@@ -21676,7 +21857,7 @@ function getPluginStatus(parsed) {
21676
21857
  ...parsed.repo && { repo: parsed.repo }
21677
21858
  };
21678
21859
  }
21679
- const available = existsSync8(parsed.normalized);
21860
+ const available = existsSync9(parsed.normalized);
21680
21861
  return {
21681
21862
  source: parsed.original,
21682
21863
  type: "local",
@@ -21684,14 +21865,23 @@ function getPluginStatus(parsed) {
21684
21865
  path: parsed.normalized
21685
21866
  };
21686
21867
  }
21868
+ async function getMarketplacePluginStatus(spec) {
21869
+ const resolved = await resolvePluginSpecWithAutoRegister(spec);
21870
+ return {
21871
+ source: spec,
21872
+ type: "marketplace",
21873
+ available: resolved.success,
21874
+ path: resolved.path ?? ""
21875
+ };
21876
+ }
21687
21877
 
21688
21878
  // src/core/workspace-modify.ts
21689
- import { readFile as readFile8, writeFile as writeFile5 } from "node:fs/promises";
21690
- import { existsSync as existsSync9 } from "node:fs";
21691
- import { join as join10 } from "node:path";
21879
+ import { readFile as readFile9, writeFile as writeFile5 } from "node:fs/promises";
21880
+ import { existsSync as existsSync10 } from "node:fs";
21881
+ import { join as join11 } from "node:path";
21692
21882
  async function addPlugin(plugin, workspacePath = process.cwd()) {
21693
- const configPath = join10(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21694
- if (!existsSync9(configPath)) {
21883
+ const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21884
+ if (!existsSync10(configPath)) {
21695
21885
  return {
21696
21886
  success: false,
21697
21887
  error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
@@ -21699,31 +21889,14 @@ async function addPlugin(plugin, workspacePath = process.cwd()) {
21699
21889
  };
21700
21890
  }
21701
21891
  if (isPluginSpec(plugin)) {
21702
- const parsed = parsePluginSpec(plugin);
21703
- if (!parsed) {
21704
- return {
21705
- success: false,
21706
- error: `Invalid plugin spec: ${plugin}`
21707
- };
21708
- }
21709
- const autoRegResult = await ensureMarketplaceRegistered(plugin);
21710
- if (!autoRegResult.success) {
21711
- return {
21712
- success: false,
21713
- error: autoRegResult.error || "Unknown error"
21714
- };
21715
- }
21716
- const resolved = await resolvePluginSpec(plugin, parsed.subpath ? { subpath: parsed.subpath } : {});
21717
- if (!resolved) {
21718
- const marketplace = await getMarketplace(autoRegResult.registeredAs || parsed.marketplaceName);
21719
- const expectedSubpath = parsed.subpath ?? "plugins";
21892
+ const resolved = await resolvePluginSpecWithAutoRegister(plugin);
21893
+ if (!resolved.success) {
21720
21894
  return {
21721
21895
  success: false,
21722
- error: `Plugin '${parsed.plugin}' not found in marketplace '${parsed.marketplaceName}'
21723
- Expected at: ${marketplace?.path ?? `~/.allagents/marketplaces/${parsed.marketplaceName}`}/${expectedSubpath}/${parsed.plugin}/`
21896
+ error: resolved.error || "Unknown error"
21724
21897
  };
21725
21898
  }
21726
- return await addPluginToConfig(autoRegResult.registeredAs ? plugin.replace(/@[^@]+$/, `@${autoRegResult.registeredAs}`) : plugin, configPath, autoRegResult.registeredAs);
21899
+ return await addPluginToConfig(resolved.registeredAs ? plugin.replace(/@[^@]+$/, `@${resolved.registeredAs}`) : plugin, configPath, resolved.registeredAs);
21727
21900
  }
21728
21901
  if (isGitHubUrl(plugin)) {
21729
21902
  const validation = validatePluginSource(plugin);
@@ -21741,8 +21914,8 @@ async function addPlugin(plugin, workspacePath = process.cwd()) {
21741
21914
  };
21742
21915
  }
21743
21916
  } else {
21744
- const fullPath = join10(workspacePath, plugin);
21745
- if (!existsSync9(fullPath) && !existsSync9(plugin)) {
21917
+ const fullPath = join11(workspacePath, plugin);
21918
+ if (!existsSync10(fullPath) && !existsSync10(plugin)) {
21746
21919
  return {
21747
21920
  success: false,
21748
21921
  error: `Plugin not found at ${plugin}`
@@ -21751,58 +21924,9 @@ async function addPlugin(plugin, workspacePath = process.cwd()) {
21751
21924
  }
21752
21925
  return await addPluginToConfig(plugin, configPath);
21753
21926
  }
21754
- async function ensureMarketplaceRegistered(spec) {
21755
- const atIndex = spec.lastIndexOf("@");
21756
- const marketplaceName = spec.slice(atIndex + 1);
21757
- if (!marketplaceName) {
21758
- return {
21759
- success: false,
21760
- error: `Invalid plugin spec: ${spec}`
21761
- };
21762
- }
21763
- const existing = await getMarketplace(marketplaceName);
21764
- if (existing) {
21765
- return { success: true };
21766
- }
21767
- const wellKnown = getWellKnownMarketplaces();
21768
- if (wellKnown[marketplaceName]) {
21769
- console.log(`Auto-registering well-known marketplace: ${marketplaceName}`);
21770
- const result = await addMarketplace(marketplaceName);
21771
- if (!result.success) {
21772
- return { success: false, error: result.error || "Unknown error" };
21773
- }
21774
- return { success: true, registeredAs: marketplaceName };
21775
- }
21776
- if (marketplaceName.includes("/") && !marketplaceName.includes("://")) {
21777
- const parts = marketplaceName.split("/");
21778
- if (parts.length >= 2 && parts[0] && parts[1]) {
21779
- const owner = parts[0];
21780
- const repo = parts[1];
21781
- const ownerRepo = `${owner}/${repo}`;
21782
- const existingByRepo = await getMarketplace(repo);
21783
- if (existingByRepo) {
21784
- return { success: true, registeredAs: repo };
21785
- }
21786
- console.log(`Auto-registering GitHub marketplace: ${ownerRepo}`);
21787
- const result = await addMarketplace(ownerRepo, repo);
21788
- if (!result.success) {
21789
- return { success: false, error: result.error || "Unknown error" };
21790
- }
21791
- return { success: true, registeredAs: repo };
21792
- }
21793
- }
21794
- return {
21795
- success: false,
21796
- error: `Marketplace '${marketplaceName}' not found.
21797
- Options:
21798
- 1. Use fully qualified name: plugin@owner/repo
21799
- 2. Register first: allagents plugin marketplace add <source>
21800
- 3. Well-known marketplaces: ${Object.keys(wellKnown).join(", ")}`
21801
- };
21802
- }
21803
21927
  async function addPluginToConfig(plugin, configPath, autoRegistered) {
21804
21928
  try {
21805
- const content = await readFile8(configPath, "utf-8");
21929
+ const content = await readFile9(configPath, "utf-8");
21806
21930
  const config = load(content);
21807
21931
  if (config.plugins.includes(plugin)) {
21808
21932
  return {
@@ -21825,8 +21949,8 @@ async function addPluginToConfig(plugin, configPath, autoRegistered) {
21825
21949
  }
21826
21950
  }
21827
21951
  async function removePlugin(plugin, workspacePath = process.cwd()) {
21828
- const configPath = join10(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21829
- if (!existsSync9(configPath)) {
21952
+ const configPath = join11(workspacePath, CONFIG_DIR, WORKSPACE_CONFIG_FILE);
21953
+ if (!existsSync10(configPath)) {
21830
21954
  return {
21831
21955
  success: false,
21832
21956
  error: `${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE} not found in ${workspacePath}
@@ -21834,7 +21958,7 @@ async function removePlugin(plugin, workspacePath = process.cwd()) {
21834
21958
  };
21835
21959
  }
21836
21960
  try {
21837
- const content = await readFile8(configPath, "utf-8");
21961
+ const content = await readFile9(configPath, "utf-8");
21838
21962
  const config = load(content);
21839
21963
  let index = config.plugins.indexOf(plugin);
21840
21964
  if (index === -1 && isPluginSpec(plugin) === false) {
@@ -21860,6 +21984,49 @@ async function removePlugin(plugin, workspacePath = process.cwd()) {
21860
21984
 
21861
21985
  // src/cli/commands/workspace.ts
21862
21986
  var workspaceCommand = new Command("workspace").description("Manage AI agent workspaces - initialize, sync, and configure plugins");
21987
+ async function runSyncAndPrint() {
21988
+ console.log(`
21989
+ Syncing workspace...
21990
+ `);
21991
+ const result = await syncWorkspace();
21992
+ if (!result.success && result.error) {
21993
+ console.error(`Sync error: ${result.error}`);
21994
+ return false;
21995
+ }
21996
+ for (const pluginResult of result.pluginResults) {
21997
+ const status = pluginResult.success ? "✓" : "✗";
21998
+ console.log(`${status} Plugin: ${pluginResult.plugin}`);
21999
+ if (pluginResult.error) {
22000
+ console.log(` Error: ${pluginResult.error}`);
22001
+ }
22002
+ const copied = pluginResult.copyResults.filter((r) => r.action === "copied").length;
22003
+ const generated = pluginResult.copyResults.filter((r) => r.action === "generated").length;
22004
+ const failed = pluginResult.copyResults.filter((r) => r.action === "failed").length;
22005
+ if (copied > 0)
22006
+ console.log(` Copied: ${copied} files`);
22007
+ if (generated > 0)
22008
+ console.log(` Generated: ${generated} files`);
22009
+ if (failed > 0) {
22010
+ console.log(` Failed: ${failed} files`);
22011
+ for (const failedResult of pluginResult.copyResults.filter((r) => r.action === "failed")) {
22012
+ console.log(` - ${failedResult.destination}: ${failedResult.error}`);
22013
+ }
22014
+ }
22015
+ }
22016
+ console.log(`
22017
+ Sync complete:`);
22018
+ console.log(` Total copied: ${result.totalCopied}`);
22019
+ if (result.totalGenerated > 0) {
22020
+ console.log(` Total generated: ${result.totalGenerated}`);
22021
+ }
22022
+ if (result.totalFailed > 0) {
22023
+ console.log(` Total failed: ${result.totalFailed}`);
22024
+ }
22025
+ if (result.totalSkipped > 0) {
22026
+ console.log(` Total skipped: ${result.totalSkipped}`);
22027
+ }
22028
+ return result.success && result.totalFailed === 0;
22029
+ }
21863
22030
  workspaceCommand.command("init [path]").description("Create new workspace and sync plugins").option("--from <template>", "Copy workspace.yaml from existing template/workspace").action(async (path3, options2) => {
21864
22031
  try {
21865
22032
  const targetPath = path3 ?? ".";
@@ -21891,17 +22058,25 @@ Sync complete: ${syncResult.totalCopied} files copied`);
21891
22058
  throw error;
21892
22059
  }
21893
22060
  });
21894
- workspaceCommand.command("sync").description("Sync plugins to workspace").option("--offline", "Use cached plugins without fetching latest from remote").option("-n, --dry-run", "Simulate sync without making changes").action(async (options2) => {
22061
+ workspaceCommand.command("sync").description("Sync plugins to workspace").option("--offline", "Use cached plugins without fetching latest from remote").option("-n, --dry-run", "Simulate sync without making changes").option("-c, --client <client>", "Sync only the specified client (e.g., opencode, claude)").action(async (options2) => {
21895
22062
  try {
21896
22063
  const offline = options2.offline ?? false;
21897
22064
  const dryRun = options2.dryRun ?? false;
21898
22065
  if (dryRun) {
21899
22066
  console.log(`Dry run mode - no changes will be made
22067
+ `);
22068
+ }
22069
+ if (options2.client) {
22070
+ console.log(`Syncing client: ${options2.client}
21900
22071
  `);
21901
22072
  }
21902
22073
  console.log(`Syncing workspace...
21903
22074
  `);
21904
- const result = await syncWorkspace(process.cwd(), { offline, dryRun });
22075
+ const result = await syncWorkspace(process.cwd(), {
22076
+ offline,
22077
+ dryRun,
22078
+ ...options2.client && { clients: [options2.client] }
22079
+ });
21905
22080
  if (!result.success && result.error) {
21906
22081
  console.error(`Error: ${result.error}`);
21907
22082
  process.exit(1);
@@ -21974,7 +22149,14 @@ workspaceCommand.command("status").description("Show sync status of plugins").ac
21974
22149
  } else {
21975
22150
  for (const plugin of result.plugins) {
21976
22151
  const status = plugin.available ? "✓" : "✗";
21977
- const typeLabel = plugin.type === "github" ? plugin.available ? "cached" : "not cached" : "local";
22152
+ let typeLabel;
22153
+ if (plugin.type === "marketplace") {
22154
+ typeLabel = plugin.available ? "marketplace" : "not synced";
22155
+ } else if (plugin.type === "github") {
22156
+ typeLabel = plugin.available ? "cached" : "not cached";
22157
+ } else {
22158
+ typeLabel = "local";
22159
+ }
21978
22160
  console.log(` ${status} ${plugin.source} (${typeLabel})`);
21979
22161
  }
21980
22162
  }
@@ -21994,7 +22176,7 @@ Clients (${result.clients.length}):`);
21994
22176
  }
21995
22177
  });
21996
22178
  var pluginSubcommand = new Command("plugin").description("Manage plugins in .allagents/workspace.yaml");
21997
- pluginSubcommand.command("add <plugin>").description("Add plugin to .allagents/workspace.yaml (supports plugin@marketplace, GitHub URL, or local path)").action(async (plugin) => {
22179
+ pluginSubcommand.command("install <plugin>").description("Install plugin to .allagents/workspace.yaml (supports plugin@marketplace, GitHub URL, or local path)").action(async (plugin) => {
21998
22180
  try {
21999
22181
  const result = await addPlugin(plugin);
22000
22182
  if (!result.success) {
@@ -22004,9 +22186,11 @@ pluginSubcommand.command("add <plugin>").description("Add plugin to .allagents/w
22004
22186
  if (result.autoRegistered) {
22005
22187
  console.log(`✓ Auto-registered marketplace: ${result.autoRegistered}`);
22006
22188
  }
22007
- console.log(`✓ Added plugin: ${plugin}`);
22008
- console.log(`
22009
- Run "allagents workspace sync" to fetch and sync the plugin.`);
22189
+ console.log(`✓ Installed plugin: ${plugin}`);
22190
+ const syncOk = await runSyncAndPrint();
22191
+ if (!syncOk) {
22192
+ process.exit(1);
22193
+ }
22010
22194
  } catch (error) {
22011
22195
  if (error instanceof Error) {
22012
22196
  console.error(`Error: ${error.message}`);
@@ -22015,14 +22199,18 @@ Run "allagents workspace sync" to fetch and sync the plugin.`);
22015
22199
  throw error;
22016
22200
  }
22017
22201
  });
22018
- pluginSubcommand.command("remove <plugin>").description("Remove plugin from .allagents/workspace.yaml").action(async (plugin) => {
22202
+ pluginSubcommand.command("uninstall <plugin>").alias("remove").description("Uninstall plugin from .allagents/workspace.yaml").action(async (plugin) => {
22019
22203
  try {
22020
22204
  const result = await removePlugin(plugin);
22021
22205
  if (!result.success) {
22022
22206
  console.error(`Error: ${result.error}`);
22023
22207
  process.exit(1);
22024
22208
  }
22025
- console.log(`✓ Removed plugin: ${plugin}`);
22209
+ console.log(`✓ Uninstalled plugin: ${plugin}`);
22210
+ const syncOk = await runSyncAndPrint();
22211
+ if (!syncOk) {
22212
+ process.exit(1);
22213
+ }
22026
22214
  } catch (error) {
22027
22215
  if (error instanceof Error) {
22028
22216
  console.error(`Error: ${error.message}`);
@@ -22189,9 +22377,9 @@ pluginCommand.command("validate <path>").description("Validate plugin structure
22189
22377
  console.log("(validation not yet implemented)");
22190
22378
  });
22191
22379
 
22192
- // src/cli/commands/update.ts
22380
+ // src/cli/commands/self.ts
22193
22381
  import { readFileSync as readFileSync2 } from "node:fs";
22194
- import { dirname as dirname6, join as join11 } from "node:path";
22382
+ import { dirname as dirname6, join as join12 } from "node:path";
22195
22383
  import { fileURLToPath as fileURLToPath3 } from "node:url";
22196
22384
  function detectPackageManagerFromPath(scriptPath) {
22197
22385
  if (scriptPath.includes(".bun")) {
@@ -22205,14 +22393,15 @@ function detectPackageManager() {
22205
22393
  function getCurrentVersion() {
22206
22394
  try {
22207
22395
  const __dirname2 = dirname6(fileURLToPath3(import.meta.url));
22208
- const packageJsonPath = join11(__dirname2, "..", "package.json");
22396
+ const packageJsonPath = join12(__dirname2, "..", "package.json");
22209
22397
  const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
22210
22398
  return packageJson.version;
22211
22399
  } catch {
22212
22400
  return "unknown";
22213
22401
  }
22214
22402
  }
22215
- var updateCommand = new Command("update").description("Update allagents to the latest version").option("--npm", "Force update using npm").option("--bun", "Force update using bun").action(async (options2) => {
22403
+ var selfCommand = new Command("self").description("Manage the allagents installation");
22404
+ selfCommand.command("update").description("Update allagents to the latest version").option("--npm", "Force update using npm").option("--bun", "Force update using bun").action(async (options2) => {
22216
22405
  try {
22217
22406
  let packageManager;
22218
22407
  if (options2.npm && options2.bun) {
@@ -22262,11 +22451,11 @@ Update complete.`);
22262
22451
 
22263
22452
  // src/cli/index.ts
22264
22453
  var __dirname2 = dirname7(fileURLToPath4(import.meta.url));
22265
- var packageJsonPath = join12(__dirname2, "..", "package.json");
22454
+ var packageJsonPath = join13(__dirname2, "..", "package.json");
22266
22455
  var packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
22267
22456
  var program2 = new Command;
22268
22457
  program2.name("allagents").description("CLI tool for managing multi-repo AI agent workspaces with plugin synchronization").version(packageJson.version);
22269
22458
  program2.addCommand(workspaceCommand);
22270
22459
  program2.addCommand(pluginCommand);
22271
- program2.addCommand(updateCommand);
22460
+ program2.addCommand(selfCommand);
22272
22461
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "allagents",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
5
5
  "type": "module",
6
6
  "bin": {