allagents 1.4.9 → 1.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +261 -36
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15959,9 +15959,37 @@ var init_plugin_path = __esm(() => {
15959
15959
  });
15960
15960
 
15961
15961
  // src/core/plugin.ts
15962
+ var exports_plugin = {};
15963
+ __export(exports_plugin, {
15964
+ updatePlugin: () => updatePlugin,
15965
+ updateCachedPlugins: () => updateCachedPlugins,
15966
+ seedFetchCache: () => seedFetchCache,
15967
+ resetFetchCache: () => resetFetchCache,
15968
+ listCachedPlugins: () => listCachedPlugins,
15969
+ getPluginName: () => getPluginName,
15970
+ getPluginCacheDir: () => getPluginCacheDir,
15971
+ fetchPlugin: () => fetchPlugin
15972
+ });
15962
15973
  import { mkdir, readdir, stat } from "node:fs/promises";
15963
15974
  import { existsSync as existsSync2 } from "node:fs";
15964
15975
  import { basename, dirname, join as join3, resolve as resolve3 } from "node:path";
15976
+ function resetFetchCache() {
15977
+ fetchCache.clear();
15978
+ }
15979
+ function seedFetchCache(url, path, branch) {
15980
+ const parsed = parseGitHubUrl(url);
15981
+ if (!parsed)
15982
+ return;
15983
+ const { owner, repo } = parsed;
15984
+ const cachePath = getPluginCachePath(owner, repo, branch ?? parsed.branch);
15985
+ if (fetchCache.has(cachePath))
15986
+ return;
15987
+ fetchCache.set(cachePath, Promise.resolve({
15988
+ success: true,
15989
+ action: "skipped",
15990
+ cachePath: path
15991
+ }));
15992
+ }
15965
15993
  async function fetchPlugin(url, options2 = {}, deps = {}) {
15966
15994
  const { offline = false, branch } = options2;
15967
15995
  const validation = validatePluginSource(url);
@@ -15984,17 +16012,13 @@ async function fetchPlugin(url, options2 = {}, deps = {}) {
15984
16012
  }
15985
16013
  const { owner, repo } = parsed;
15986
16014
  const cachePath = getPluginCachePath(owner, repo, branch);
15987
- const existing = inflight.get(cachePath);
15988
- if (existing) {
15989
- return existing;
16015
+ const cached = fetchCache.get(cachePath);
16016
+ if (cached) {
16017
+ return cached;
15990
16018
  }
15991
16019
  const promise = doFetchPlugin(cachePath, owner, repo, offline, branch, deps);
15992
- inflight.set(cachePath, promise);
15993
- try {
15994
- return await promise;
15995
- } finally {
15996
- inflight.delete(cachePath);
15997
- }
16020
+ fetchCache.set(cachePath, promise);
16021
+ return promise;
15998
16022
  }
15999
16023
  async function doFetchPlugin(cachePath, owner, repo, offline, branch, deps) {
16000
16024
  const {
@@ -16014,8 +16038,10 @@ async function doFetchPlugin(cachePath, owner, repo, offline, branch, deps) {
16014
16038
  const repoUrl = gitHubUrl(owner, repo);
16015
16039
  if (isCached) {
16016
16040
  try {
16041
+ const pullStart = performance.now();
16017
16042
  await pullFn(cachePath);
16018
- return { success: true, action: "updated", cachePath };
16043
+ const pullMs = Math.round(performance.now() - pullStart);
16044
+ return { success: true, action: "updated", cachePath, durationMs: pullMs };
16019
16045
  } catch {
16020
16046
  return { success: true, action: "skipped", cachePath };
16021
16047
  }
@@ -16023,11 +16049,14 @@ async function doFetchPlugin(cachePath, owner, repo, offline, branch, deps) {
16023
16049
  try {
16024
16050
  const parentDir = dirname(cachePath);
16025
16051
  await mkdirFn(parentDir, { recursive: true });
16052
+ const cloneStart = performance.now();
16026
16053
  await cloneToFn(repoUrl, cachePath, branch);
16054
+ const cloneMs = Math.round(performance.now() - cloneStart);
16027
16055
  return {
16028
16056
  success: true,
16029
16057
  action: "fetched",
16030
- cachePath
16058
+ cachePath,
16059
+ durationMs: cloneMs
16031
16060
  };
16032
16061
  } catch (error) {
16033
16062
  if (error instanceof GitCloneError) {
@@ -16058,6 +16087,57 @@ async function doFetchPlugin(cachePath, owner, repo, offline, branch, deps) {
16058
16087
  };
16059
16088
  }
16060
16089
  }
16090
+ function getPluginCacheDir() {
16091
+ return resolve3(getHomeDir(), ".allagents", "plugins", "marketplaces");
16092
+ }
16093
+ async function listCachedPlugins() {
16094
+ const cacheDir = getPluginCacheDir();
16095
+ if (!existsSync2(cacheDir)) {
16096
+ return [];
16097
+ }
16098
+ const entries = await readdir(cacheDir, { withFileTypes: true });
16099
+ const plugins = [];
16100
+ for (const entry of entries) {
16101
+ if (entry.isDirectory()) {
16102
+ const pluginPath = join3(cacheDir, entry.name);
16103
+ const stats = await stat(pluginPath);
16104
+ plugins.push({
16105
+ name: entry.name,
16106
+ path: pluginPath,
16107
+ lastModified: stats.mtime
16108
+ });
16109
+ }
16110
+ }
16111
+ plugins.sort((a, b) => a.name.localeCompare(b.name));
16112
+ return plugins;
16113
+ }
16114
+ async function updateCachedPlugins(name) {
16115
+ const plugins = await listCachedPlugins();
16116
+ const results = [];
16117
+ const toUpdate = name ? plugins.filter((p) => p.name === name) : plugins;
16118
+ if (name && toUpdate.length === 0) {
16119
+ return [
16120
+ {
16121
+ name,
16122
+ success: false,
16123
+ error: `Plugin not found in cache: ${name}`
16124
+ }
16125
+ ];
16126
+ }
16127
+ for (const plugin of toUpdate) {
16128
+ try {
16129
+ await pull(plugin.path);
16130
+ results.push({ name: plugin.name, success: true });
16131
+ } catch (error) {
16132
+ results.push({
16133
+ name: plugin.name,
16134
+ success: false,
16135
+ error: error instanceof Error ? error.message : "Unknown error"
16136
+ });
16137
+ }
16138
+ }
16139
+ return results;
16140
+ }
16061
16141
  function getPluginName(pluginPath) {
16062
16142
  return basename(pluginPath);
16063
16143
  }
@@ -16135,12 +16215,12 @@ async function updatePlugin(pluginSpec, deps) {
16135
16215
  ...fetchResult.error && { error: fetchResult.error }
16136
16216
  };
16137
16217
  }
16138
- var inflight;
16218
+ var fetchCache;
16139
16219
  var init_plugin = __esm(() => {
16140
16220
  init_plugin_path();
16141
16221
  init_constants();
16142
16222
  init_git();
16143
- inflight = new Map;
16223
+ fetchCache = new Map;
16144
16224
  });
16145
16225
 
16146
16226
  // node_modules/braces/lib/utils.js
@@ -29736,6 +29816,80 @@ var init_native = __esm(() => {
29736
29816
  init_registry();
29737
29817
  });
29738
29818
 
29819
+ // src/utils/stopwatch.ts
29820
+ class Stopwatch {
29821
+ entries = [];
29822
+ active = new Map;
29823
+ globalStart;
29824
+ constructor() {
29825
+ this.globalStart = performance.now();
29826
+ }
29827
+ start(label) {
29828
+ this.active.set(label, performance.now());
29829
+ }
29830
+ stop(label, detail) {
29831
+ const startTime = this.active.get(label);
29832
+ if (startTime === undefined) {
29833
+ return 0;
29834
+ }
29835
+ const durationMs = performance.now() - startTime;
29836
+ this.active.delete(label);
29837
+ this.entries.push({ label, durationMs, ...detail !== undefined && { detail } });
29838
+ return durationMs;
29839
+ }
29840
+ async measure(label, fn, detail) {
29841
+ this.start(label);
29842
+ try {
29843
+ const result = await fn();
29844
+ this.stop(label, detail);
29845
+ return result;
29846
+ } catch (error) {
29847
+ this.stop(label, detail ? `${detail} (failed)` : "(failed)");
29848
+ throw error;
29849
+ }
29850
+ }
29851
+ getEntries() {
29852
+ return [...this.entries];
29853
+ }
29854
+ getTotalMs() {
29855
+ return performance.now() - this.globalStart;
29856
+ }
29857
+ formatReport() {
29858
+ const lines = [];
29859
+ const totalMs = this.getTotalMs();
29860
+ lines.push(`Sync timing report (total: ${formatMs(totalMs)})`);
29861
+ lines.push("─".repeat(60));
29862
+ for (const entry of this.entries) {
29863
+ const pct = totalMs > 0 ? (entry.durationMs / totalMs * 100).toFixed(1) : "0.0";
29864
+ const detail = entry.detail ? ` [${entry.detail}]` : "";
29865
+ lines.push(` ${padEnd(entry.label, 40)} ${padStart2(formatMs(entry.durationMs), 8)} ${padStart2(pct, 5)}%${detail}`);
29866
+ }
29867
+ lines.push("─".repeat(60));
29868
+ return lines;
29869
+ }
29870
+ toJSON() {
29871
+ return {
29872
+ totalMs: Math.round(this.getTotalMs()),
29873
+ steps: this.entries.map((e) => ({
29874
+ label: e.label,
29875
+ durationMs: Math.round(e.durationMs),
29876
+ ...e.detail && { detail: e.detail }
29877
+ }))
29878
+ };
29879
+ }
29880
+ }
29881
+ function formatMs(ms) {
29882
+ if (ms < 1000)
29883
+ return `${Math.round(ms)}ms`;
29884
+ return `${(ms / 1000).toFixed(2)}s`;
29885
+ }
29886
+ function padEnd(str3, len) {
29887
+ return str3.length >= len ? str3 : str3 + " ".repeat(len - str3.length);
29888
+ }
29889
+ function padStart2(str3, len) {
29890
+ return str3.length >= len ? str3 : " ".repeat(len - str3.length) + str3;
29891
+ }
29892
+
29739
29893
  // src/core/sync.ts
29740
29894
  import { existsSync as existsSync14, readFileSync as readFileSync4, writeFileSync as writeFileSync4, lstatSync } from "node:fs";
29741
29895
  import { rm as rm4, unlink as unlink2, rmdir, copyFile } from "node:fs/promises";
@@ -29784,7 +29938,20 @@ function mergeSyncResults(a, b) {
29784
29938
  ...purgedPaths.length > 0 && { purgedPaths },
29785
29939
  ...deletedArtifacts.length > 0 && { deletedArtifacts },
29786
29940
  ...mcpResults && { mcpResults },
29787
- ...nativeResult && { nativeResult }
29941
+ ...nativeResult && { nativeResult },
29942
+ ...mergeTiming(a.timing, b.timing)
29943
+ };
29944
+ }
29945
+ function mergeTiming(a, b) {
29946
+ if (!a && !b)
29947
+ return {};
29948
+ const aSteps = (a?.steps ?? []).map((s) => ({ ...s, label: `user:${s.label}` }));
29949
+ const bSteps = (b?.steps ?? []).map((s) => ({ ...s, label: `project:${s.label}` }));
29950
+ return {
29951
+ timing: {
29952
+ totalMs: (a?.totalMs ?? 0) + (b?.totalMs ?? 0),
29953
+ steps: [...aSteps, ...bSteps]
29954
+ }
29788
29955
  };
29789
29956
  }
29790
29957
  function resolveNativePluginSource(vp) {
@@ -30523,6 +30690,7 @@ async function persistSyncState(workspacePath, pluginResults, workspaceFileResul
30523
30690
  async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
30524
30691
  await migrateWorkspaceSkillsV1toV2(workspacePath);
30525
30692
  const { offline = false, dryRun = false, workspaceSourceBase, skipAgentFiles = false } = options2;
30693
+ const sw = new Stopwatch;
30526
30694
  const configDir = join16(workspacePath, CONFIG_DIR);
30527
30695
  const configPath = join16(configDir, WORKSPACE_CONFIG_FILE);
30528
30696
  if (!existsSync14(configPath)) {
@@ -30557,11 +30725,13 @@ async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
30557
30725
  ]
30558
30726
  };
30559
30727
  }
30560
- await ensureMarketplacesRegistered(filteredPlans.map((plan) => plan.source));
30561
- const validatedPlugins = await validateAllPlugins(filteredPlans, workspacePath, offline);
30728
+ const marketplaceResults = await sw.measure("marketplace-registration", () => ensureMarketplacesRegistered(filteredPlans.map((plan) => plan.source)));
30729
+ await seedFetchCacheFromMarketplaces(marketplaceResults);
30730
+ const validatedPlugins = await sw.measure("plugin-validation", () => validateAllPlugins(filteredPlans, workspacePath, offline), `${filteredPlans.length} plugin(s)`);
30562
30731
  let validatedWorkspaceSource = null;
30563
30732
  const workspaceSourceWarnings = [];
30564
30733
  if (config.workspace?.source) {
30734
+ sw.start("workspace-source-validation");
30565
30735
  const sourceBasePath = workspaceSourceBase ?? workspacePath;
30566
30736
  const wsSourceResult = await validatePlugin(config.workspace.source, sourceBasePath, offline);
30567
30737
  if (wsSourceResult.success) {
@@ -30569,6 +30739,7 @@ async function syncWorkspace(workspacePath = process.cwd(), options2 = {}) {
30569
30739
  } else {
30570
30740
  workspaceSourceWarnings.push(`Workspace source: ${wsSourceResult.error}`);
30571
30741
  }
30742
+ sw.stop("workspace-source-validation");
30572
30743
  }
30573
30744
  const failedValidations = validatedPlugins.filter((v) => !v.success);
30574
30745
  const validPlugins = validatedPlugins.filter((v) => v.success);
@@ -30589,23 +30760,24 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
30589
30760
  paths: getPreviouslySyncedFiles(previousState, client)
30590
30761
  })).filter((p) => p.paths.length > 0) : [];
30591
30762
  if (!dryRun) {
30592
- await selectivePurgeWorkspace(workspacePath, previousState, syncClients);
30763
+ await sw.measure("selective-purge", () => selectivePurgeWorkspace(workspacePath, previousState, syncClients));
30593
30764
  }
30594
30765
  const isV1Fallback = config.version === undefined || config.version < 2;
30595
30766
  const disabledSkillsSet = isV1Fallback ? new Set(config.disabledSkills ?? []) : undefined;
30596
30767
  const enabledSkillsSet = isV1Fallback && config.enabledSkills ? new Set(config.enabledSkills) : undefined;
30597
- const allSkills = await collectAllSkills(validPlugins, disabledSkillsSet, enabledSkillsSet);
30768
+ const allSkills = await sw.measure("skill-collection", () => collectAllSkills(validPlugins, disabledSkillsSet, enabledSkillsSet));
30598
30769
  const pluginSkillMaps = buildPluginSkillNameMaps(allSkills);
30599
30770
  const syncMode = config.syncMode ?? "symlink";
30600
- const pluginResults = await Promise.all(validPlugins.map(async (validatedPlugin) => {
30771
+ const pluginResults = await sw.measure("plugin-copy", () => Promise.all(validPlugins.map(async (validatedPlugin) => {
30601
30772
  const skillNameMap = pluginSkillMaps.get(validatedPlugin.resolved);
30602
30773
  const result = await copyValidatedPlugin(validatedPlugin, workspacePath, validatedPlugin.clients, dryRun, skillNameMap, undefined, syncMode);
30603
30774
  return { ...result, scope: "project" };
30604
- }));
30605
- const nativeResult = await syncNativePlugins(validPlugins, previousState, "project", workspacePath, dryRun, warnings, messages);
30775
+ })), `${validPlugins.length} plugin(s)`);
30776
+ const nativeResult = await sw.measure("native-plugin-sync", () => syncNativePlugins(validPlugins, previousState, "project", workspacePath, dryRun, warnings, messages));
30606
30777
  let workspaceFileResults = [];
30607
30778
  const skipWorkspaceFiles = !!config.workspace?.source && !validatedWorkspaceSource;
30608
30779
  if (config.workspace && !skipWorkspaceFiles) {
30780
+ sw.start("workspace-files");
30609
30781
  const sourcePath = validatedWorkspaceSource?.resolved;
30610
30782
  const filesToCopy = [...config.workspace.files];
30611
30783
  if (hasRepositories && sourcePath) {
@@ -30642,18 +30814,20 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
30642
30814
  await copyFile(agentsPath, claudePath);
30643
30815
  }
30644
30816
  }
30817
+ sw.stop("workspace-files");
30645
30818
  }
30646
30819
  if (!config.workspace && !dryRun && !skipAgentFiles) {
30647
30820
  await updateAgentFiles(workspacePath);
30648
30821
  }
30649
30822
  let vscodeState;
30650
30823
  if (syncClients.includes("vscode") && !dryRun) {
30651
- const result = await syncVscodeWorkspaceFile(workspacePath, config, configPath, previousState, messages);
30824
+ const result = await sw.measure("vscode-workspace-file", () => syncVscodeWorkspaceFile(workspacePath, config, configPath, previousState, messages));
30652
30825
  config = result.config;
30653
30826
  if (result.hash && result.repos) {
30654
30827
  vscodeState = { hash: result.hash, repos: result.repos };
30655
30828
  }
30656
30829
  }
30830
+ sw.start("mcp-sync");
30657
30831
  const mcpResults = {};
30658
30832
  if (syncClients.includes("vscode")) {
30659
30833
  const trackedMcpServers = getPreviouslySyncedMcpServers(previousState, "vscode");
@@ -30711,6 +30885,7 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
30711
30885
  }
30712
30886
  mcpResults.copilot = copilotMcp;
30713
30887
  }
30888
+ sw.stop("mcp-sync");
30714
30889
  const PROJECT_MCP_CLIENTS = new Set(["claude", "codex", "vscode", "copilot", "universal"]);
30715
30890
  const { servers: allMcpServers } = collectMcpServers(validPlugins);
30716
30891
  if (allMcpServers.size > 0) {
@@ -30729,12 +30904,12 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
30729
30904
  const deletedArtifacts = computeDeletedArtifacts(previousState, newStatePaths, syncClients, resolvedMappings, availableSkillNames);
30730
30905
  const { pluginsByClient: nativePluginsByClient } = collectNativePluginSources(validPlugins);
30731
30906
  if (!dryRun) {
30732
- await persistSyncState(workspacePath, pluginResults, workspaceFileResults, syncClients, nativePluginsByClient, nativeResult, {
30907
+ await sw.measure("persist-state", () => persistSyncState(workspacePath, pluginResults, workspaceFileResults, syncClients, nativePluginsByClient, nativeResult, {
30733
30908
  ...vscodeState && { vscodeState },
30734
30909
  ...Object.keys(mcpResults).length > 0 && {
30735
30910
  mcpTrackedServers: Object.fromEntries(Object.entries(mcpResults).map(([scope, r]) => [scope, r.trackedServers]))
30736
30911
  }
30737
- });
30912
+ }));
30738
30913
  }
30739
30914
  return {
30740
30915
  success: !hasFailures,
@@ -30748,11 +30923,36 @@ ${fileValidationErrors.map((e) => ` - ${e}`).join(`
30748
30923
  ...warnings.length > 0 && { warnings },
30749
30924
  ...messages.length > 0 && { messages },
30750
30925
  ...Object.keys(mcpResults).length > 0 && { mcpResults },
30751
- ...nativeResult && { nativeResult }
30926
+ ...nativeResult && { nativeResult },
30927
+ timing: sw.toJSON()
30752
30928
  };
30753
30929
  }
30930
+ async function seedFetchCacheFromMarketplaces(results) {
30931
+ for (const result of results) {
30932
+ if (!result.success || !result.name)
30933
+ continue;
30934
+ const entry = await getMarketplace(result.name);
30935
+ if (!entry || entry.source.type !== "github")
30936
+ continue;
30937
+ seedFetchCache(entry.source.location, entry.path);
30938
+ const branch = readGitBranch(entry.path);
30939
+ if (branch) {
30940
+ seedFetchCache(entry.source.location, entry.path, branch);
30941
+ }
30942
+ }
30943
+ }
30944
+ function readGitBranch(repoPath) {
30945
+ try {
30946
+ const head = readFileSync4(join16(repoPath, ".git", "HEAD"), "utf-8").trim();
30947
+ const prefix = "ref: refs/heads/";
30948
+ return head.startsWith(prefix) ? head.slice(prefix.length) : null;
30949
+ } catch {
30950
+ return null;
30951
+ }
30952
+ }
30754
30953
  async function syncUserWorkspace(options2 = {}) {
30755
30954
  await migrateUserWorkspaceSkillsV1toV2();
30955
+ const sw = new Stopwatch;
30756
30956
  const homeDir = resolve9(getHomeDir());
30757
30957
  const config = await getUserWorkspaceConfig();
30758
30958
  if (!config) {
@@ -30770,8 +30970,9 @@ async function syncUserWorkspace(options2 = {}) {
30770
30970
  const { plans: allPluginPlans, warnings: planWarnings } = buildPluginSyncPlans(config.plugins, workspaceClients, "user");
30771
30971
  const pluginPlans = allPluginPlans.filter((plan) => plan.clients.length > 0 || plan.nativeClients.length > 0);
30772
30972
  const syncClients = collectSyncClients(workspaceClients, pluginPlans);
30773
- await ensureMarketplacesRegistered(pluginPlans.map((plan) => plan.source));
30774
- const validatedPlugins = await validateAllPlugins(pluginPlans, homeDir, offline);
30973
+ const marketplaceResults = await sw.measure("marketplace-registration", () => ensureMarketplacesRegistered(pluginPlans.map((plan) => plan.source)));
30974
+ await seedFetchCacheFromMarketplaces(marketplaceResults);
30975
+ const validatedPlugins = await sw.measure("plugin-validation", () => validateAllPlugins(pluginPlans, homeDir, offline), `${pluginPlans.length} plugin(s)`);
30775
30976
  const failedValidations = validatedPlugins.filter((v) => !v.success);
30776
30977
  const validPlugins = validatedPlugins.filter((v) => v.success);
30777
30978
  const warnings = [
@@ -30786,21 +30987,22 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
30786
30987
  }
30787
30988
  const previousState = await loadSyncState(homeDir);
30788
30989
  if (!dryRun) {
30789
- await selectivePurgeWorkspace(homeDir, previousState, syncClients);
30990
+ await sw.measure("selective-purge", () => selectivePurgeWorkspace(homeDir, previousState, syncClients));
30790
30991
  }
30791
30992
  const isV1FallbackUser = config.version === undefined || config.version < 2;
30792
30993
  const disabledSkillsSet = isV1FallbackUser ? new Set(config.disabledSkills ?? []) : undefined;
30793
30994
  const enabledSkillsSet = isV1FallbackUser && config.enabledSkills ? new Set(config.enabledSkills) : undefined;
30794
- const allSkills = await collectAllSkills(validPlugins, disabledSkillsSet, enabledSkillsSet);
30995
+ const allSkills = await sw.measure("skill-collection", () => collectAllSkills(validPlugins, disabledSkillsSet, enabledSkillsSet));
30795
30996
  const pluginSkillMaps = buildPluginSkillNameMaps(allSkills);
30796
30997
  const syncMode = config.syncMode ?? "symlink";
30797
- const pluginResults = await Promise.all(validPlugins.map(async (vp) => {
30998
+ const pluginResults = await sw.measure("plugin-copy", () => Promise.all(validPlugins.map(async (vp) => {
30798
30999
  const skillNameMap = pluginSkillMaps.get(vp.resolved);
30799
31000
  const resolvedUserMappings2 = resolveClientMappings(vp.clients, USER_CLIENT_MAPPINGS);
30800
31001
  const result = await copyValidatedPlugin(vp, homeDir, vp.clients, dryRun, skillNameMap, resolvedUserMappings2, syncMode);
30801
31002
  return { ...result, scope: "user" };
30802
- }));
31003
+ })), `${validPlugins.length} plugin(s)`);
30803
31004
  const { totalCopied, totalFailed, totalSkipped, totalGenerated } = countCopyResults(pluginResults, []);
31005
+ sw.start("mcp-sync");
30804
31006
  const mcpResults = {};
30805
31007
  if (syncClients.includes("vscode")) {
30806
31008
  const trackedMcpServers = getPreviouslySyncedMcpServers(previousState, "vscode");
@@ -30840,6 +31042,7 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
30840
31042
  }
30841
31043
  mcpResults.copilot = copilotMcp;
30842
31044
  }
31045
+ sw.stop("mcp-sync");
30843
31046
  const USER_MCP_CLIENTS = new Set(["claude", "codex", "vscode", "copilot", "universal"]);
30844
31047
  const { servers: allUserMcpServers } = collectMcpServers(validPlugins);
30845
31048
  if (allUserMcpServers.size > 0) {
@@ -30849,7 +31052,7 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
30849
31052
  }
30850
31053
  }
30851
31054
  }
30852
- const nativeResult = await syncNativePlugins(validPlugins, previousState, "user", homeDir, dryRun, warnings, messages);
31055
+ const nativeResult = await sw.measure("native-plugin-sync", () => syncNativePlugins(validPlugins, previousState, "user", homeDir, dryRun, warnings, messages));
30853
31056
  const availableUserSkillNames = await collectAvailableSkillNames(validPlugins);
30854
31057
  const allCopyResultsForState = pluginResults.flatMap((r) => r.copyResults);
30855
31058
  const resolvedUserMappings = resolveClientMappings(syncClients, USER_CLIENT_MAPPINGS);
@@ -30857,12 +31060,12 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
30857
31060
  const deletedArtifacts = computeDeletedArtifacts(previousState, newStatePaths, syncClients, resolvedUserMappings, availableUserSkillNames);
30858
31061
  if (!dryRun) {
30859
31062
  const { pluginsByClient: nativePluginsByClient } = collectNativePluginSources(validPlugins);
30860
- await persistSyncState(homeDir, pluginResults, [], syncClients, nativePluginsByClient, nativeResult, {
31063
+ await sw.measure("persist-state", () => persistSyncState(homeDir, pluginResults, [], syncClients, nativePluginsByClient, nativeResult, {
30861
31064
  clientMappings: USER_CLIENT_MAPPINGS,
30862
31065
  ...Object.keys(mcpResults).length > 0 && {
30863
31066
  mcpTrackedServers: Object.fromEntries(Object.entries(mcpResults).map(([scope, r]) => [scope, r.trackedServers]))
30864
31067
  }
30865
- });
31068
+ }));
30866
31069
  }
30867
31070
  return {
30868
31071
  success: totalFailed === 0,
@@ -30875,7 +31078,8 @@ ${failedValidations.map((v) => ` - ${v.plugin}: ${v.error}`).join(`
30875
31078
  ...warnings.length > 0 && { warnings },
30876
31079
  ...messages.length > 0 && { messages },
30877
31080
  ...Object.keys(mcpResults).length > 0 && { mcpResults },
30878
- ...nativeResult && { nativeResult }
31081
+ ...nativeResult && { nativeResult },
31082
+ timing: sw.toJSON()
30879
31083
  };
30880
31084
  }
30881
31085
  var import_json53, VSCODE_TEMPLATE_FILE = "template.code-workspace";
@@ -34103,7 +34307,7 @@ var package_default;
34103
34307
  var init_package = __esm(() => {
34104
34308
  package_default = {
34105
34309
  name: "allagents",
34106
- version: "1.4.9",
34310
+ version: "1.4.10",
34107
34311
  description: "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
34108
34312
  type: "module",
34109
34313
  bin: {
@@ -36080,6 +36284,8 @@ var syncCmd = import_cmd_ts2.command({
36080
36284
  return;
36081
36285
  }
36082
36286
  let combined = null;
36287
+ const { resetFetchCache: resetFetchCache2 } = await Promise.resolve().then(() => (init_plugin(), exports_plugin));
36288
+ resetFetchCache2();
36083
36289
  if (userConfigExists) {
36084
36290
  const userResult = await syncUserWorkspace({ offline, dryRun, force });
36085
36291
  combined = userResult;
@@ -36182,6 +36388,20 @@ native:`);
36182
36388
  console.log(line);
36183
36389
  }
36184
36390
  }
36391
+ if (process.env.ALLAGENTS_DEBUG?.includes("timing") && result.timing) {
36392
+ console.error("");
36393
+ const totalMs = result.timing.totalMs;
36394
+ console.error(`[debug] Sync timing (total: ${formatTimingMs(totalMs)})`);
36395
+ console.error(`[debug] ${"─".repeat(56)}`);
36396
+ for (const step of result.timing.steps) {
36397
+ const pct = totalMs > 0 ? (step.durationMs / totalMs * 100).toFixed(1) : "0.0";
36398
+ const detail = step.detail ? ` [${step.detail}]` : "";
36399
+ const label = step.label.padEnd(40);
36400
+ const duration = formatTimingMs(step.durationMs).padStart(8);
36401
+ console.error(`[debug] ${label} ${duration} ${pct.padStart(5)}%${detail}`);
36402
+ }
36403
+ console.error(`[debug] ${"─".repeat(56)}`);
36404
+ }
36185
36405
  if (!result.success || result.totalFailed > 0) {
36186
36406
  process.exit(1);
36187
36407
  }
@@ -36198,6 +36418,11 @@ native:`);
36198
36418
  }
36199
36419
  }
36200
36420
  });
36421
+ function formatTimingMs(ms) {
36422
+ if (ms < 1000)
36423
+ return `${Math.round(ms)}ms`;
36424
+ return `${(ms / 1000).toFixed(2)}s`;
36425
+ }
36201
36426
  var statusCmd = import_cmd_ts2.command({
36202
36427
  name: "status",
36203
36428
  description: buildDescription(statusMeta),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "allagents",
3
- "version": "1.4.9",
3
+ "version": "1.4.10",
4
4
  "description": "CLI tool for managing multi-repo AI agent workspaces with plugin synchronization",
5
5
  "type": "module",
6
6
  "bin": {