skills-atlas-cli 0.8.8 → 0.8.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/skills.js CHANGED
@@ -73,6 +73,9 @@ async function main() {
73
73
  process.exitCode = 1;
74
74
  return;
75
75
  }
76
+ // Opportunistic, non-blocking background catalog refresh so new skills appear over
77
+ // time without a manual `update`. Skipped for `update` itself; fully fail-silent.
78
+ if (sub !== 'update') { try { require('../src/data').maybeBackgroundRefresh(); } catch { /* ignore */ } }
76
79
  await cmd(rest);
77
80
  }
78
81
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skills-atlas-cli",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "description": "Search, install and learn AI agent skills from the terminal — powered by the Skills Atlas catalog.",
5
5
  "bin": {
6
6
  "skills-atlas": "bin/skills.js",
@@ -6,7 +6,10 @@ const { refreshData, refreshSources } = require('../data');
6
6
  module.exports = async function update(argv) {
7
7
  const { values } = parse(argv, ['json']);
8
8
  if (values.help) {
9
- console.log('usage: skills-atlas update\n\nRefresh the local catalog cache from the public data feed.');
9
+ console.log('usage: skills-atlas update\n\n' +
10
+ 'Refresh the local catalog cache from the public data feed.\n' +
11
+ 'The catalog also auto-refreshes in the background (~daily) so new skills appear\n' +
12
+ 'on their own; set SKILLS_ATLAS_NO_REFRESH=1 (or SKILLS_ATLAS_OFFLINE=1) to disable.');
10
13
  return;
11
14
  }
12
15
 
package/src/data.js CHANGED
@@ -93,6 +93,37 @@ function maybeStaleNudge(fromCache) {
93
93
  process.stderr.write("tip: run 'skills-atlas update' to refresh the catalog\n");
94
94
  }
95
95
 
96
+ const REFRESH_AFTER_MS = 24 * 3600000; // auto-refresh once the cache is a day old
97
+ const refreshStampFile = () => path.join(cacheDir(), 'autorefresh.json');
98
+
99
+ // Pure decision: should we kick off a background refresh now? (cache stale/absent,
100
+ // not throttled, not opted out). Injectable for tests.
101
+ function shouldAutoRefresh({ meta, stamp, env, now }) {
102
+ if (env.SKILLS_ATLAS_NO_REFRESH || env.SKILLS_ATLAS_OFFLINE || env.SKILLS_ATLAS_BG_REFRESH) return false;
103
+ const t = meta && meta.fetchedAt ? Date.parse(meta.fetchedAt) : NaN;
104
+ const ageMs = Number.isFinite(t) ? now - t : Infinity; // absent/bad meta → treat as stale
105
+ if (ageMs < REFRESH_AFTER_MS) return false;
106
+ const st = stamp && stamp.at ? Date.parse(stamp.at) : NaN;
107
+ if (Number.isFinite(st) && (now - st) < REFRESH_AFTER_MS) return false; // already tried today
108
+ return true;
109
+ }
110
+
111
+ // Fire-and-forget: if the catalog is old (or absent) and we haven't tried today,
112
+ // spawn a DETACHED background `update` so new skills appear next time. Never blocks,
113
+ // never errors out. Opt out with SKILLS_ATLAS_NO_REFRESH / SKILLS_ATLAS_OFFLINE.
114
+ function maybeBackgroundRefresh() {
115
+ try {
116
+ if (!shouldAutoRefresh({ meta: tryReadJSON(metaFile()), stamp: tryReadJSON(refreshStampFile()), env: process.env, now: Date.now() })) return;
117
+ ensureDir(cacheDir());
118
+ fs.writeFileSync(refreshStampFile(), JSON.stringify({ at: new Date().toISOString() })); // stamp first → no double-spawn
119
+ const { spawn } = require('child_process');
120
+ const child = spawn(process.execPath, [path.join(__dirname, '..', 'bin', 'skills.js'), 'update'], {
121
+ detached: true, stdio: 'ignore', env: { ...process.env, SKILLS_ATLAS_BG_REFRESH: '1' },
122
+ });
123
+ child.unref();
124
+ } catch { /* fully fail-silent — never affect the foreground command */ }
125
+ }
126
+
96
127
  function loadData({ quiet = false } = {}) {
97
128
  const cached = tryReadJSON(cacheFile());
98
129
  let base, info;
@@ -205,4 +236,4 @@ async function refreshSources() {
205
236
  return out;
206
237
  }
207
238
 
208
- module.exports = { loadData, refreshData, fetchSource, refreshSources, counts, isValid, cacheDir, PUBLIC_URL };
239
+ module.exports = { loadData, refreshData, fetchSource, refreshSources, counts, isValid, shouldAutoRefresh, maybeBackgroundRefresh, cacheDir, PUBLIC_URL };