ruvnet-brain 1.14.0-dev โ†’ 1.16.0-dev

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # ๐Ÿง  RuvNet Brain
6
6
 
7
- ### ๐Ÿง  RuvNet Brain โ€” [![RuvNet Brain version 1.14.0-dev โ€” updated 2026-07-08 07:21 EDT](https://img.shields.io/badge/version_1.14.0--dev-updated_2026--07--08_07:21_EDT-1E90FF?style=for-the-badge&labelColor=0757BA)](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
7
+ ### ๐Ÿง  RuvNet Brain โ€” [![RuvNet Brain version 1.16.0-dev โ€” updated 2026-07-09 04:53 EDT](https://img.shields.io/badge/version_1.16.0--dev-updated_2026--07--09_04:53_EDT-1E90FF?style=for-the-badge&labelColor=0757BA)](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
8
8
 
9
9
  **A portable, source-grounded brain over Reuven Cohen's (rUv's) RuvNet stack โ€” delivered as a Claude Code plugin that makes Claude _use_ the stack instead of fighting it.**
10
10
 
@@ -14,12 +14,15 @@
14
14
  [![explainer](https://img.shields.io/badge/โ–ถ%20see%20it%20live-isovision.ai%2Fruvnet--brain-e8a13a?style=flat-square)](https://isovision.ai/ruvnet-brain/)
15
15
  [![license](https://img.shields.io/badge/license-MIT-8ecae6?style=flat-square)](LICENSE)
16
16
  [![grounded](https://img.shields.io/badge/answers-cited%20rUv%20source-333?style=flat-square)](#testing--proof)
17
- [![coverage](https://img.shields.io/badge/coverage-75%25%20lines%20ยท%2072%25%20stmts-2e7d32?style=flat-square)](#testing--proof)
17
+ [![coverage](https://img.shields.io/badge/coverage-10%25%20of%20ALL%20source%20ยท%20honest-b58900?style=flat-square)](#testing--proof)
18
18
 
19
19
  > **Three independent things version separately here โ€” by design, not drift. Every number below is live (read straight from its real source, never hand-typed), so none of them can go stale:**
20
20
  > - **`plugin`** (badge above) โ€” the Claude Code plugin itself: SKILL.md, the grounding hooks, the MCP server. Read live from [`plugin/.claude-plugin/plugin.json`](plugin/.claude-plugin/plugin.json). Updates often โ€” this is where behavior fixes land.
21
21
  > - **`installer (npm)`** (badge above) โ€” the `npx ruvnet-brain` setup script. Read live from the [npm registry](https://www.npmjs.com/package/ruvnet-brain). Only moves when the installer script itself changes โ€” rare.
22
22
  > - **Brain Release** (the downloadable 512MB knowledge bundle, linked from the "download" badge above) โ€” always resolves to [`releases/latest`](https://github.com/stuinfla/ruvnet-brain/releases/latest), currently `v0.5.0-dev`. Only moves when the underlying knowledge base is rebuilt โ€” separate again from the two above.
23
+ > - **Update an installed brain once:** `npx ruvnet-brain --update` โ€” runs the bundle's own self-updater (backs up first, re-verifies, fails loud instead of half-applying).
24
+ > - **Nightly auto-update is OFF by default:** `npx ruvnet-brain --enable-nightly` schedules it (macOS LaunchAgent, 03:47); `npx ruvnet-brain --disable-nightly` removes it; Linux/Windows get the cron line documented in the bundle's `forge-update.mjs`.
25
+ > - **Either way, your copy only advances when a new Release is published** โ€” the updater pulls `releases/latest`, so running it between releases is a safe no-op.
23
26
 
24
27
  <sub>Built by **[Stuart Kerr](https://isovision.ai)** at [Isovision.ai](https://isovision.ai) ยท free & fair use, to help everyone leverage the high end of agentic coding.</sub>
25
28
 
@@ -204,7 +207,7 @@ node plugin/test/run-tests.mjs # full plugin QA over real JSO
204
207
  | **L1โ€“L4 behavioral harness** | **all pass** | route ยท deep-recall (returns _code_) ยท implement (cites the API) ยท orchestrate (the hook drives the full pipeline) |
205
208
  | **Plugin QA** | **26 / 26** | manifests, hook firing, MCP `initialize`/`tools/list`, capability battery |
206
209
  | **Clean-room install** | **3 / 3** | download the published 512 MB bundle fresh โ†’ unzip โ†’ query โ†’ grounded, cited answers |
207
- | **Unit tests** | **154 passing** ยท 75% lines | `npm run test:cov` โ€” a CI floor fails the build if coverage slips |
210
+ | **Unit tests** | **208 passing** ยท 10% of ALL source covered | `npm run test:cov` โ€” the floor fails CI if it slips. 10% is the honest number over every shipped file; the previous "75%" measured a hand-picked 8-file subset |
208
211
  | **Grounding proof** | `npx ruvnet-brain --doctor` | asks a real question, then checks the cited path really exists in the on-disk store; a citation that doesn't resolve is reported as **NOT grounded** |
209
212
  | **Held-out eval** | **grounded 12/12** ยท routed 10/12 | `npm run eval` โ€” 12 frozen questions never used for tuning, graded on ground truth, never by a model |
210
213
 
package/bin/install.mjs CHANGED
@@ -65,6 +65,10 @@ const FLAG_DOCTOR = argv.includes('--doctor');
65
65
  const FLAG_NO_VERIFY = argv.includes('--no-verify');
66
66
  const FLAG_PIN = argv.includes('--pin'); // skip the latest-check, use the bundled default
67
67
  const FLAG_DEMO = argv.includes('--demo'); // guided, real (non-fabricated) walkthrough of the brain in action
68
+ // โ”€โ”€ freshness flags โ€” invoke/schedule the SELF-UPDATER the bundle already ships (kb/forge-update.mjs) โ”€โ”€
69
+ const FLAG_UPDATE = argv.includes('--update'); // one-shot: pull the latest Release bundle into the installed brain now
70
+ const FLAG_ENABLE_NIGHTLY = argv.includes('--enable-nightly'); // schedule that update nightly (macOS LaunchAgent)
71
+ const FLAG_DISABLE_NIGHTLY = argv.includes('--disable-nightly'); // remove the nightly schedule
68
72
  // โ”€โ”€ onboarding-experience flags (all optional; every offer is safe to decline) โ”€โ”€
69
73
  const FLAG_YES = argv.includes('--yes') || argv.includes('-y'); // accept every optional offer non-interactively
70
74
  const FLAG_WITH_STACK = argv.includes('--with-stack'); // add missing Ruflo/RuVector without prompting
@@ -725,6 +729,163 @@ async function doctor() {
725
729
  );
726
730
  }
727
731
 
732
+ // โ”€โ”€ `--update` / `--enable-nightly` / `--disable-nightly`: end-user freshness controls โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
733
+ // The brain bundle SHIPS its own self-updater (forge-update.mjs, right in the KB dir): it pulls the
734
+ // canonical Release bundle, backs the current copy up, extracts, and re-verifies with forge-guard โ€”
735
+ // failing loud with no partial clobber. These flags never reimplement any of that; they only INVOKE
736
+ // it once (--update) or SCHEDULE it per-user (--enable-nightly). Nothing here ever publishes.
737
+ const NIGHTLY_LABEL = 'com.ruvnet.brain-update';
738
+ const resolvedKbDir = () =>
739
+ process.env.RUVNET_BRAIN_KB || path.join(os.homedir(), '.cache', 'ruvnet-brain', 'kb');
740
+ const nightlyPlistPath = () =>
741
+ path.join(os.homedir(), 'Library', 'LaunchAgents', `${NIGHTLY_LABEL}.plist`);
742
+ // RUVNET_BRAIN_TEST=1 โ†’ write/remove the plist but NEVER call launchctl. Tests point HOME at a temp
743
+ // dir; bootstrapping a temp-dir plist into the user's real gui domain would mutate exactly the
744
+ // system state the tests promise not to touch.
745
+ const TEST_MODE = process.env.RUVNET_BRAIN_TEST === '1';
746
+ const xmlEscape = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
747
+
748
+ // The same nightly command, cron-flavored โ€” the pattern forge-update.mjs documents in its header.
749
+ // 03:47 on purpose: an off-hour minute, so it never piles onto the :00 cron rush.
750
+ const cronExample = (kbDir) =>
751
+ `47 3 * * * cd ${kbDir} && ${process.execPath} forge-update.mjs --apply >> ${kbDir}/update.log 2>&1`;
752
+
753
+ function missingUpdaterHelp(kbDir) {
754
+ console.error(`\n${c.red('โœ— can\'t update:')} ${c.bold('forge-update.mjs')} is missing from ${kbDir}.`);
755
+ console.error(` Either no brain is installed there, or the bundle predates the self-updater.`);
756
+ console.error(` Fix: re-run the installer โ€” ${c.bold('npx ruvnet-brain')} ${c.dim('(add --force if a brain is already present)')}`);
757
+ console.error(` โ€” the current bundle ships forge-update.mjs; then this command will work.`);
758
+ }
759
+
760
+ function runUpdate() {
761
+ printBanner('update');
762
+ const kbDir = resolvedKbDir();
763
+ info(`brain dir: ${c.bold(kbDir)}`);
764
+ if (!fs.existsSync(path.join(kbDir, 'forge-update.mjs'))) {
765
+ missingUpdaterHelp(kbDir);
766
+ process.exit(1);
767
+ }
768
+ info(c.dim("running the bundle's own self-updater (backs up first, re-verifies, never half-applies)โ€ฆ\n"));
769
+ // Relative filename + matching cwd โ€” same launch convention as smokeQuery(); stdio:'inherit'
770
+ // streams the updater's narration live and unedited.
771
+ const r = spawnSync(process.execPath, ['forge-update.mjs', '--apply'], { cwd: kbDir, stdio: 'inherit' });
772
+ if (r.error) {
773
+ console.error(`\n${c.red('โœ— update failed to launch:')} ${r.error.message}`);
774
+ process.exit(1);
775
+ }
776
+ process.exit(r.status === null ? 1 : r.status); // exit with the updater's own verdict
777
+ }
778
+
779
+ function enableNightly() {
780
+ printBanner('enable nightly updates');
781
+ const kbDir = resolvedKbDir();
782
+
783
+ if (process.platform !== 'darwin') {
784
+ info('The LaunchAgent scheduler is macOS-only (for now).');
785
+ info("On this platform, schedule the bundle's self-updater with cron โ€” the pattern");
786
+ info('forge-update.mjs itself documents:');
787
+ console.log(`\n ${c.bold(cronExample(kbDir))}\n`);
788
+ info(`(${c.bold('crontab -e')}, paste the line, save. Remove the line to disable.)`);
789
+ return; // exit 0 โ€” the user got the working recipe
790
+ }
791
+
792
+ info(`brain dir: ${c.bold(kbDir)}`);
793
+ if (!fs.existsSync(path.join(kbDir, 'forge-update.mjs'))) {
794
+ // Refuse to schedule a job that is guaranteed to fail every night โ€” fail loud NOW instead.
795
+ missingUpdaterHelp(kbDir);
796
+ process.exit(1);
797
+ }
798
+
799
+ // Template the plist to THIS user's kb dir + node binary. Quotes guard paths with spaces;
800
+ // xmlEscape guards the XML (>> and && must survive as shell operators after plist parsing).
801
+ const logPath = path.join(kbDir, 'update.log');
802
+ const shellCmd = `cd "${kbDir}" && "${process.execPath}" forge-update.mjs --apply >> "${logPath}" 2>&1`;
803
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
804
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
805
+ <plist version="1.0">
806
+ <dict>
807
+ <key>Label</key>
808
+ <string>${NIGHTLY_LABEL}</string>
809
+ <key>ProgramArguments</key>
810
+ <array>
811
+ <string>/bin/sh</string>
812
+ <string>-c</string>
813
+ <string>${xmlEscape(shellCmd)}</string>
814
+ </array>
815
+ <key>StartCalendarInterval</key>
816
+ <dict>
817
+ <key>Hour</key>
818
+ <integer>3</integer>
819
+ <key>Minute</key>
820
+ <integer>47</integer>
821
+ </dict>
822
+ <key>RunAtLoad</key>
823
+ <false/>
824
+ </dict>
825
+ </plist>
826
+ `;
827
+ const plistPath = nightlyPlistPath();
828
+ try {
829
+ fs.mkdirSync(path.dirname(plistPath), { recursive: true });
830
+ fs.writeFileSync(plistPath, plist);
831
+ } catch (e) {
832
+ console.error(`\n${c.red('โœ— couldn\'t write the LaunchAgent:')} ${e.message}`);
833
+ process.exit(1);
834
+ }
835
+ ok(`wrote ${c.bold(plistPath)}`);
836
+ info(c.dim('runs nightly at 03:47 โ€” an off-hour minute, so it never lands on the :00 rush'));
837
+
838
+ if (TEST_MODE) {
839
+ warn('RUVNET_BRAIN_TEST=1 โ€” skipping launchctl bootout/bootstrap (plist written only)');
840
+ } else {
841
+ const uid = process.getuid();
842
+ // bootout first so re-running replaces the loaded job cleanly; failure just means "wasn't loaded".
843
+ spawnSync('launchctl', ['bootout', `gui/${uid}/${NIGHTLY_LABEL}`], { stdio: 'ignore' });
844
+ const boot = spawnSync('launchctl', ['bootstrap', `gui/${uid}`, plistPath], { encoding: 'utf8' });
845
+ if (boot.status === 0) ok('LaunchAgent loaded โ€” your brain now updates while you sleep');
846
+ else {
847
+ warn(`launchctl bootstrap failed (${(boot.stderr || '').trim() || `exit ${boot.status}`}) โ€” the plist is in place;`);
848
+ info(`load it yourself: ${c.bold(`launchctl bootstrap gui/${uid} ${plistPath}`)}`);
849
+ }
850
+ }
851
+
852
+ console.log(`\n ${c.bold('Verify it:')} launchctl list | grep ${NIGHTLY_LABEL}`);
853
+ console.log(` ${c.bold('Watch it:')} tail ${logPath} ${c.dim('(appears after the first nightly run)')}`);
854
+ console.log(` ${c.bold('Disable it:')} npx ruvnet-brain --disable-nightly`);
855
+ console.log(`\n ${c.dim('It only ever PULLS the published Release bundle (backup + re-verify built in) โ€” it never publishes,')}`);
856
+ console.log(` ${c.dim('and a night with no new Release is a clean no-op.')}\n`);
857
+ }
858
+
859
+ function disableNightly() {
860
+ printBanner('disable nightly updates');
861
+ if (process.platform !== 'darwin') {
862
+ info('The LaunchAgent nightly is macOS-only, so nothing was scheduled here by this tool.');
863
+ info(`If you added the cron line yourself, remove it with: ${c.bold('crontab -e')}`);
864
+ return;
865
+ }
866
+ const plistPath = nightlyPlistPath();
867
+ const existed = fs.existsSync(plistPath);
868
+ if (TEST_MODE) {
869
+ warn('RUVNET_BRAIN_TEST=1 โ€” skipping launchctl bootout (plist removal only)');
870
+ } else {
871
+ // Ignore failure: "not loaded" is exactly the state we want anyway.
872
+ spawnSync('launchctl', ['bootout', `gui/${process.getuid()}/${NIGHTLY_LABEL}`], { stdio: 'ignore' });
873
+ }
874
+ if (existed) {
875
+ try {
876
+ fs.rmSync(plistPath);
877
+ } catch (e) {
878
+ console.error(`\n${c.red('โœ— couldn\'t remove the LaunchAgent:')} ${e.message}`);
879
+ console.error(` Remove it yourself: rm ${plistPath}`);
880
+ process.exit(1);
881
+ }
882
+ ok(`nightly updates disabled โ€” removed ${plistPath}`);
883
+ } else {
884
+ ok('nightly updates were already off โ€” nothing to remove (safe to run any time)');
885
+ }
886
+ info(`re-enable any time: ${c.bold('npx ruvnet-brain --enable-nightly')}`);
887
+ }
888
+
728
889
  // โ”€โ”€ tiny interactive yes/no โ€” SAFE in non-TTY (returns the default; never blocks a piped install) โ”€โ”€
729
890
  function ask(question, def = false) {
730
891
  if (FLAG_YES) return Promise.resolve(true);
@@ -949,9 +1110,14 @@ function success({ cacheDir, isCustom, plugin, env }) {
949
1110
  โ€ข Want to see it answer, live, right now? ${c.bold('npx ruvnet-brain --demo')} โ€” 2 real questions, real cited answers.`);
950
1111
 
951
1112
  console.log(`\n ${c.bold('Set it up your way:')} this default is ${c.bold('global')} โ€” live in every VS Code project automatically,`);
952
- console.log(` which is what most people want. Want it different (project-only, moved, with the build stack added,`);
953
- console.log(` auto-updating nightly)? ${c.bold('Just tell Claude')} once it's on โ€” the brain is smart enough to reconfigure`);
954
- console.log(` itself. You never have to learn its internals.`);
1113
+ console.log(` which is what most people want. Want it different (project-only, moved, with the build stack`);
1114
+ console.log(` added)? ${c.bold('Just tell Claude')} once it's on. You never have to learn its internals.`);
1115
+
1116
+ console.log(`\n ${c.bold('Staying current:')} nightly updates are ${c.bold('OFF by default')} โ€” nothing runs on your machine unasked.`);
1117
+ console.log(` โ€ข one-shot check + update now: ${c.bold('npx ruvnet-brain --update')}`);
1118
+ console.log(` โ€ข nightly at 03:47 (macOS): ${c.bold('npx ruvnet-brain --enable-nightly')} ${c.dim('ยท off again: --disable-nightly')}`);
1119
+ console.log(` โ€ข Linux/Windows: the cron line documented in the brain's own ${c.bold('forge-update.mjs')}`);
1120
+ console.log(` ${c.dim('Either way, your copy only advances when a new Release is actually published.')}`);
955
1121
 
956
1122
  console.log(`\n ${c.dim('You can\'t break anything โ€” the plugin is disable-able and only acts on RuvNet-shaped work.')}`);
957
1123
  console.log('');
@@ -969,6 +1135,11 @@ Usage:
969
1135
  npx github:stuinfla/ruvnet-brain Same, but from the bleeding-edge GitHub commit
970
1136
  npx ruvnet-brain --doctor Health-check an existing install (green/red per part)
971
1137
  npx ruvnet-brain --demo Guided walkthrough โ€” 2 real questions, real cited answers
1138
+ npx ruvnet-brain --update One-shot: pull the latest Release bundle into your installed brain
1139
+ (runs the bundle's own forge-update.mjs --apply: backup + re-verify)
1140
+ npx ruvnet-brain --enable-nightly Schedule that update nightly at 03:47 โ€” macOS LaunchAgent;
1141
+ other platforms get the documented cron line. OFF by default.
1142
+ npx ruvnet-brain --disable-nightly Remove the nightly schedule (safe to run any time)
972
1143
  node bin/install.mjs --version <tag> Install a specific Release tag (e.g. --version v0.4.0-dev)
973
1144
  node bin/install.mjs --pin Skip the latest-check; use the bundled known-good version
974
1145
  node bin/install.mjs --local Install from a repo clone's dist/ruvnet-brain.zip
@@ -992,6 +1163,9 @@ It is safe to re-run at any time. After installing, restart Claude Code so the g
992
1163
  if (FLAG_HELP) return showHelp();
993
1164
  if (FLAG_DOCTOR) return await doctor();
994
1165
  if (FLAG_DEMO) return runDemo();
1166
+ if (FLAG_UPDATE) return runUpdate();
1167
+ if (FLAG_ENABLE_NIGHTLY) return enableNightly();
1168
+ if (FLAG_DISABLE_NIGHTLY) return disableNightly();
995
1169
 
996
1170
  printBanner('installer');
997
1171
  console.log(c.dim("I'll set up the brain and the Claude Code plugin, explaining each step as I go.\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvnet-brain",
3
- "version": "1.14.0-dev",
3
+ "version": "1.16.0-dev",
4
4
  "description": "One-command installer for RuvNet Brain โ€” a portable, source-grounded brain over rUv's RuvNet building blocks, delivered as a Claude Code plugin so Claude uses the stack instead of fighting it.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,16 +9,21 @@
9
9
  "scripts": {
10
10
  "test": "node plugin/test/run-tests.mjs",
11
11
  "version:check": "node scripts/sync-version.mjs --check",
12
+ "claims:verify": "node scripts/claims-verify.mjs",
12
13
  "version:sync": "node scripts/sync-version.mjs",
13
- "test:unit": "vitest run",
14
- "test:cov": "vitest run --coverage",
14
+ "test:unit": "vitest run tests/unit",
15
+ "test:cov": "vitest run tests/unit --coverage",
16
+ "test:all": "npm run test:unit && npm run test:integration && npm test",
15
17
  "metaharness:fix": "node scripts/fix-metaharness-memretrieve.mjs --apply",
16
18
  "metaharness:check": "node scripts/fix-metaharness-memretrieve.mjs --check",
17
19
  "eval": "node scripts/eval-brain.mjs",
18
20
  "eval:gate": "node scripts/eval-brain.mjs --gate",
19
21
  "eval:record": "node scripts/eval-brain.mjs --record",
20
22
  "embed:verifier": "node scripts/embed-verifier.mjs",
21
- "embed:check": "node scripts/embed-verifier.mjs --check"
23
+ "embed:check": "node scripts/embed-verifier.mjs --check",
24
+ "gists:index": "node scripts/ingest-gists.mjs --index-only",
25
+ "gists:sync": "node scripts/ingest-gists.mjs && node kb/forge-big.mjs both --dir kb --name ruv-gists",
26
+ "test:integration": "vitest run tests/integration"
22
27
  },
23
28
  "files": [
24
29
  "bin/install.mjs",
@@ -50,6 +55,7 @@
50
55
  "bugs": "https://github.com/stuinfla/ruvnet-brain/issues",
51
56
  "author": "Stuart Kerr",
52
57
  "devDependencies": {
58
+ "@metaharness/darwin": "~0.8.0",
53
59
  "@vitest/coverage-v8": "^4.1.10",
54
60
  "vitest": "^4.1.10"
55
61
  }