@ericrisco/rsc 2.0.13 → 2.0.14

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/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.0.13",
2
+ "version": "2.0.14",
3
3
  "counts": {
4
4
  "skills": 273,
5
5
  "agents": 33,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericrisco/rsc",
3
- "version": "2.0.13",
3
+ "version": "2.0.14",
4
4
  "description": "A guided harness builder that turns your goal into a minimal, verified AI-agent setup using strict best practices.",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/rsc.js CHANGED
@@ -501,7 +501,18 @@ function printContextBudget(b) {
501
501
  say('════════════════════════════════════════════════\n');
502
502
  }
503
503
 
504
- async function wizard(flagTargets) {
504
+ async function syncDeclared(targets, dry = false) {
505
+ for (const t of targets) {
506
+ const result = await syncInstalled({ target: t, dryRun: dry });
507
+ const verb = dry ? 'Would sync' : 'Synced';
508
+ say(`${verb} ${t}: ${result.synced.length ? result.synced.join(', ') : '(nothing to sync)'}`);
509
+ if (dry && result.paths?.length) {
510
+ for (const p of result.paths) say(` ${p}`);
511
+ }
512
+ }
513
+ }
514
+
515
+ async function wizard(flagTargets, declaredTargets = []) {
505
516
  const m = loadManifest();
506
517
  await banner(m.counts.skills);
507
518
  say(' the skill catalog for your assistant (Claude Code · Codex · Cursor · Gemini · Antigravity)\n');
@@ -511,12 +522,16 @@ async function wizard(flagTargets) {
511
522
  const baseIds = skillsForProfile(m, 'minimal');
512
523
  // Navigable loop: esc / "← Back" / "no" all return here instead of quitting.
513
524
  for (;;) {
525
+ const declared = readManifest();
514
526
  const choice = await select('What do you want to do?', [
527
+ // First, because it is what someone who already has a harness came here for nine times in ten.
528
+ ...(declared ? [{ key: 'update', label: `Update this project — keeps its ${declared.skills.length} skills, refreshes hooks and content` }] : []),
515
529
  { key: 'base', label: `Base install — the essentials (${baseIds.length} skills)` },
516
530
  { key: 'sdd', label: 'Base + Spec-Driven Development — the specify → plan → implement → ship flow' },
517
531
  { key: 'manual', label: 'Pick skills by hand, by area' },
518
532
  ]);
519
533
  if (choice === null) { say('\nOK — nothing installed. Anytime: npx @ericrisco/rsc'); return; }
534
+ if (choice === 'update') return syncDeclared(declaredTargets);
520
535
 
521
536
  let ids;
522
537
  if (choice === 'base') ids = baseIds;
@@ -621,7 +636,14 @@ async function main() {
621
636
  const target = targets[0];
622
637
  switch (cmd) {
623
638
  case undefined:
624
- return hasDeclaredHarness() ? wizard(f ? targets : null) : runOnboarding(f ? targets : []);
639
+ if (!hasDeclaredHarness()) return runOnboarding(f ? targets : []);
640
+ // A project that already has a harness: the bare command means "bring it up to date". It is
641
+ // what every update notice, the README and our own replies to users tell people to run, with
642
+ // the promise "that reinstalls and refreshes". It used to open the install wizard instead, and
643
+ // with no terminal — which is how an agent runs it when it obeys the notice — it did nothing.
644
+ // Restoring what the project already declared is not a decision, so there is nothing to ask.
645
+ if (!isInteractive()) return syncDeclared(targets);
646
+ return wizard(f ? targets : null, targets);
625
647
  case 'onboard':
626
648
  return runOnboarding(f ? targets : []);
627
649
  case 'reassess':
@@ -773,18 +795,8 @@ async function main() {
773
795
  process.exitCode = 1;
774
796
  return;
775
797
  }
776
- case 'sync': {
777
- const dry = argv.includes('--dry-run');
778
- for (const t of targets) {
779
- const result = await syncInstalled({ target: t, dryRun: dry });
780
- const verb = dry ? 'Would sync' : 'Synced';
781
- say(`${verb} ${t}: ${result.synced.length ? result.synced.join(', ') : '(nothing to sync)'}`);
782
- if (dry && result.paths?.length) {
783
- for (const p of result.paths) say(` ${p}`);
784
- }
785
- }
786
- return;
787
- }
798
+ case 'sync':
799
+ return syncDeclared(targets, argv.includes('--dry-run'));
788
800
  case 'backups': {
789
801
  const backups = listBackups();
790
802
  if (!backups.length) return void say('(no backups)');