sra-skills 0.14.13 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.mjs +32 -19
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -300,6 +300,7 @@ if (cmd === 'add') {
300
300
  const targets = target ? [target] : Object.keys(m.repos);
301
301
  const depsEntries = [];
302
302
  const updatedRepos = new Set();
303
+ const processedRepos = new Set();
303
304
  for (const name of targets) {
304
305
  const repo = m.repos[name];
305
306
  if (!repo) { console.error(`Repo "${name}" not found`); continue; }
@@ -307,30 +308,44 @@ if (cmd === 'add') {
307
308
  const isCloned = !!repo.url; // --local installs have no url
308
309
  if (isCloned) {
309
310
  const prevRev = execSync('git rev-parse HEAD', { cwd: repo.path, encoding: 'utf8' }).trim();
310
- const ref = repo.ref || 'release';
311
+ let ref = repo.ref || 'release';
311
312
  if (!repo.ref) repo.ref = ref; // backfill for pre-v2.0.1 installs
312
313
  const current = execSync('git rev-parse --abbrev-ref HEAD', { cwd: repo.path, encoding: 'utf8' }).trim();
313
314
  let activeBranch = ref;
314
315
  // Try to fetch as tag first; if that fails, treat as branch
315
316
  let isTag = false;
317
+ let updateOk = false;
318
+ let fetchOk = false;
316
319
  try {
317
320
  execSync(`git fetch --depth 1 origin "refs/tags/${ref}:refs/tags/${ref}"`, { cwd: repo.path, stdio: 'pipe' });
318
321
  isTag = true;
322
+ fetchOk = true;
319
323
  } catch {
320
324
  try {
321
325
  execSync(`git fetch --depth 1 origin "${ref}"`, { cwd: repo.path, stdio: 'pipe' });
326
+ fetchOk = true;
322
327
  } catch {
323
328
  console.log(` ⚠ Ref "${ref}" not found on remote, staying on ${current}`);
324
329
  activeBranch = current;
330
+ if (current !== 'HEAD') {
331
+ console.log(` → Auto-correcting manifest ref: "${ref}" → "${current}"`);
332
+ repo.ref = current;
333
+ ref = current;
334
+ try {
335
+ execSync(`git fetch --depth 1 origin "${current}"`, { cwd: repo.path, stdio: 'pipe' });
336
+ fetchOk = true;
337
+ } catch {}
338
+ }
325
339
  }
326
340
  }
327
- if (activeBranch === ref) {
341
+ if (fetchOk && activeBranch === ref) {
328
342
  try {
329
343
  if (isTag) {
330
344
  execSync(`git checkout "tags/${ref}" --detach`, { cwd: repo.path, stdio: 'pipe' });
331
345
  } else {
332
346
  execSync(`git checkout -B "${ref}" FETCH_HEAD`, { cwd: repo.path, stdio: 'pipe' });
333
347
  }
348
+ updateOk = true;
334
349
  if (current !== ref && !(current === 'HEAD' && isTag)) console.log(` Switched to ${ref}`);
335
350
  } catch (e) {
336
351
  console.error(` ⚠ git update failed: ${e.stderr?.toString().trim() || e.message}`);
@@ -339,8 +354,11 @@ if (cmd === 'add') {
339
354
  try { execSync('git fetch --tags', { cwd: repo.path, stdio: 'ignore' }); } catch {}
340
355
  const newRev = execSync('git rev-parse HEAD', { cwd: repo.path, encoding: 'utf8' }).trim();
341
356
  if (prevRev === newRev && !forceUpdate) {
342
- console.log(` Already up to date (${newRev.slice(0, 7)})`);
343
- repo.updated_at = new Date().toISOString();
357
+ if (updateOk) {
358
+ console.log(` Already up to date (${newRev.slice(0, 7)})`);
359
+ repo.updated_at = new Date().toISOString();
360
+ processedRepos.add(name);
361
+ }
344
362
  continue;
345
363
  }
346
364
  console.log(` Updated ${prevRev.slice(0, 7)} → ${newRev.slice(0, 7)}`);
@@ -355,7 +373,6 @@ if (cmd === 'add') {
355
373
  console.log(` "url": "${remoteUrl || '<git-remote-url>'}",`);
356
374
  console.log(` Or: git -C ${repo.path} pull, then re-run ./scripts/install.sh`);
357
375
  repo.updated_at = new Date().toISOString();
358
- continue;
359
376
  }
360
377
  const allSkills = discoverSkills(repo.path);
361
378
  const saved = repo.skills;
@@ -426,22 +443,18 @@ if (cmd === 'add') {
426
443
  const repo = Object.values(m.repos).find(r => r.path === repoPath);
427
444
  warnMissingCredentials(skills, repo?.credential_path || DEFAULT_CRED_PATH, repoPath);
428
445
  }
429
- // Clean up pending files: remove updated skills, then purge any entries
430
- // no longer present in the manifest (renamed/removed skills).
431
- const allKnownSkills = new Set(Object.values(m.repos).flatMap(r => r.skills || []));
432
- const updatedSkills = new Set(
433
- [...updatedRepos].flatMap(name => m.repos[name]?.skills || [])
434
- );
435
- const cacheDir = join(SRA_HOME, 'update-cache');
436
- for (const fname of ['bulk-pending', 'hook-pending']) {
437
- const f = join(cacheDir, fname);
438
- if (!existsSync(f)) continue;
439
- const remaining = readFileSync(f, 'utf8').split('\n')
440
- .filter(l => l && !updatedSkills.has(l) && allKnownSkills.has(l));
446
+ const availableFile = join(SRA_HOME, 'updates', 'available');
447
+ if (existsSync(availableFile)) {
448
+ const remaining = readFileSync(availableFile, 'utf8').split('\n')
449
+ .filter(l => {
450
+ if (!l.trim()) return false;
451
+ try { const n = JSON.parse(l).name; return !updatedRepos.has(n) && !processedRepos.has(n); }
452
+ catch { return false; }
453
+ });
441
454
  if (remaining.length > 0) {
442
- writeFileSync(f, remaining.join('\n') + '\n');
455
+ writeFileSync(availableFile, remaining.join('\n') + '\n');
443
456
  } else {
444
- unlinkSync(f);
457
+ unlinkSync(availableFile);
445
458
  }
446
459
  }
447
460
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sra-skills",
3
- "version": "0.14.13",
3
+ "version": "0.15.0",
4
4
  "description": "SRA agent skills installer — manage AI skill repos",
5
5
  "bin": {
6
6
  "sra": "index.mjs",