@shnitzel/plugscout 0.3.30 → 0.3.32

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.
@@ -55,7 +55,7 @@
55
55
  {
56
56
  "id": "google",
57
57
  "enabled": true,
58
- "officialOnly": true,
58
+ "officialOnly": false,
59
59
  "trustLevel": "high",
60
60
  "poll": {
61
61
  "mode": "daily",
@@ -2726,7 +2726,7 @@
2726
2726
  "timeoutMs": 10000,
2727
2727
  "fallbackToLocal": true,
2728
2728
  "provider": "cursor",
2729
- "official": false,
2729
+ "official": true,
2730
2730
  "licenseHint": "community"
2731
2731
  }
2732
2732
  },
@@ -3360,7 +3360,7 @@
3360
3360
  "timeoutMs": 10000,
3361
3361
  "fallbackToLocal": true,
3362
3362
  "provider": "google",
3363
- "official": false,
3363
+ "official": true,
3364
3364
  "licenseHint": "community"
3365
3365
  }
3366
3366
  }
@@ -4,13 +4,27 @@ import { CatalogItemSchema } from '../lib/validation/contracts.js';
4
4
  import { adaptRegistryEntries } from './adapter.js';
5
5
  import { resolveRegistryEntries } from './remote-registry.js';
6
6
  import { getStaleRegistries, getUpdatedSince, loadSyncState, saveSyncState, setSuccessfulSync, setUpdatedSince } from './sync-state.js';
7
- import { saveCatalogItems, saveLegacyCatalogViews } from './repository.js';
7
+ import { loadCatalogItems, saveCatalogItems, saveLegacyCatalogViews } from './repository.js';
8
+ // Skip remote fetch for registries synced within this window (unless --force)
9
+ const FRESH_REGISTRY_TTL_MS = 6 * 60 * 60 * 1000; // 6 hours
8
10
  export async function syncCatalogs(today = new Date().toISOString().slice(0, 10), options = {}) {
9
11
  const effectiveToday = process.env.SKILLS_MCPS_SYNC_TODAY || today;
10
- const [registries, providers] = await Promise.all([loadRegistries(), loadProviders()]);
12
+ const [registries, providers, existingItems] = await Promise.all([
13
+ loadRegistries(),
14
+ loadProviders(),
15
+ loadCatalogItems().catch(() => []),
16
+ ]);
11
17
  let syncState = await loadSyncState();
18
+ // Index existing items by source registry for reuse when skipping fresh registries
19
+ const existingBySource = new Map();
20
+ for (const item of existingItems) {
21
+ const list = existingBySource.get(item.source) ?? [];
22
+ list.push(item);
23
+ existingBySource.set(item.source, list);
24
+ }
12
25
  const selectedKinds = options.kinds?.length ? new Set(options.kinds) : null;
13
26
  const allItems = [];
27
+ const progress = options.onProgress ?? (() => undefined);
14
28
  for (const registry of registries) {
15
29
  if (selectedKinds && !selectedKinds.has(registry.kind)) {
16
30
  continue;
@@ -22,6 +36,19 @@ export async function syncCatalogs(today = new Date().toISOString().slice(0, 10)
22
36
  continue;
23
37
  }
24
38
  }
39
+ // Skip recently-synced remote registries unless --force — reuse cached items
40
+ if (!options.force && registry.remote) {
41
+ const lastSync = syncState.registries[registry.id]?.lastSuccessfulSyncAt;
42
+ if (lastSync && Date.now() - new Date(lastSync).getTime() < FRESH_REGISTRY_TTL_MS) {
43
+ const cached = existingBySource.get(registry.id) ?? [];
44
+ if (cached.length > 0) {
45
+ allItems.push(...cached);
46
+ progress(` ↩ ${registry.id} (${cached.length} cached)`);
47
+ continue;
48
+ }
49
+ }
50
+ }
51
+ progress(` ↓ ${registry.id} (${registry.kind})…`);
25
52
  const updatedSince = registry.remote?.supportsUpdatedSince ? getUpdatedSince(syncState, registry.id) : undefined;
26
53
  const resolved = await resolveRegistryEntries(registry, { updatedSince });
27
54
  const adaptedEntries = resolved.source === 'remote' ? adaptRegistryEntries(registry, resolved.entries) : resolved.entries;
@@ -695,6 +695,7 @@ async function handleTop(args) {
695
695
  async function handleSync(args) {
696
696
  const kinds = readKinds(args);
697
697
  const dryRun = hasFlag(args, '--dry-run');
698
+ const force = hasFlag(args, '--force');
698
699
  if (dryRun) {
699
700
  const registries = await loadRegistries();
700
701
  const selected = kinds?.length ? registries.filter((registry) => kinds.includes(registry.kind)) : registries;
@@ -705,7 +706,12 @@ async function handleSync(args) {
705
706
  printHint('Run without --dry-run to persist synced catalogs.');
706
707
  return;
707
708
  }
708
- const result = await syncCatalogs(undefined, { kinds });
709
+ console.log('Syncing catalogs…');
710
+ const result = await syncCatalogs(undefined, {
711
+ kinds,
712
+ force,
713
+ onProgress: (msg) => process.stdout.write(`${msg}\n`),
714
+ });
709
715
  if (result.staleRegistries.length > 0) {
710
716
  logger.warn(`Stale registries: ${result.staleRegistries.join(', ')}`);
711
717
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shnitzel/plugscout",
3
- "version": "0.3.30",
3
+ "version": "0.3.32",
4
4
  "description": "Claude plugins + Claude connectors + Copilot extensions + Skills + MCP security intelligence framework",
5
5
  "private": false,
6
6
  "type": "module",