ruvnet-brain 2.0.0 β 2.1.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.
- package/README.md +7 -5
- package/bin/install.mjs +103 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# π§ RuvNet Brain
|
|
6
6
|
|
|
7
|
-
### π§ RuvNet Brain β [](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
|
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
| **Retrieval eval** | 12 frozen questions | **120 frozen, hash-pinned questions** across 5 strata; promotion gated on Wilson lower bounds, fail-closed β it blocked a real release this morning, which is the feature working |
|
|
49
49
|
| **Token cost** | 6,183 bytes injected per hook turn Β· zero self-measurement | **684 bytes (~90% cut)** with an eval-PASS proving zero quality loss Β· a live token meter measuring real bytes/tokens per prompt class Β· ~27% faster repeat queries via the KB cache |
|
|
50
50
|
| **rUv's gists** | not indexed | **437 gists** indexed with per-chunk freshness/provenance banners, refreshed nightly with cost-disciplined skip |
|
|
51
|
-
| **Reliability** | claims were prose | **claims ledger** (
|
|
51
|
+
| **Reliability** | claims were prose | **claims ledger** (6 marketing claims mechanically re-verified in CI) Β· integration tests in CI incl. Linux Β· a Windows CI job Β· an honest coverage denominator (all source files) |
|
|
52
52
|
| **Autonomy** | hooks asked questions to an empty room β the #1 real-user complaint | **`/loop` contract** β checkpoint / resume / done-criteria; hooks detect autonomous mode and stop asking β 10 mutation-verified tests |
|
|
53
53
|
| **Publishing** | manual npm token Β· a nightly release PATH bug | **self-renewing npm token** (launchd daemon, proven end-to-end) Β· PATH bug root-caused and cured |
|
|
54
54
|
| **Memory layer** | silent SQLite corruption Β· searches returning 0 Β· invisible flywheel patterns | **3 root-caused fixes** (an ABI-mismatched binary falling back to WAL-blind whole-file overwrites; keys that were never scored; a split-store display bug) β plus **6 exact patches queued upstream to ruflo** |
|
|
@@ -126,6 +126,8 @@ That single command runs the whole setup, narrating _what it's doing and why_ at
|
|
|
126
126
|
|
|
127
127
|
> Want the bleeding-edge installer, even ahead of the last npm publish? `npx github:stuinfla/ruvnet-brain` always runs straight off the latest GitHub commit.
|
|
128
128
|
|
|
129
|
+
> **Anonymous usage counts β opt-in, counts only.** At the end of the install you're asked once: _"Share anonymous usage counts (installs/searches β never your queries or code)?"_ If you say yes, the only things ever transmitted are event counts (`install` / `search` / `session`) plus the bundle version β batched to at most one ping per machine per day. **Never** your queries, code, repo names, or paths; the entire client is one readable file (`kb/telemetry-ping.mjs`). Your answer is a plain-text file you can read or flip any time (`~/.cache/ruvnet-brain/.telemetry-consent`), and `npx ruvnet-brain --no-telemetry` declines without being asked. Found it useful? [Star the repo](https://github.com/stuinfla/ruvnet-brain) or [leave feedback in Discussions](https://github.com/stuinfla/ruvnet-brain/discussions).
|
|
130
|
+
|
|
129
131
|
<details><summary>Manual install (what the one-liner automates)</summary>
|
|
130
132
|
|
|
131
133
|
```bash
|
|
@@ -149,7 +151,7 @@ You install once. After that, three mechanisms keep you on the current brain wit
|
|
|
149
151
|
|
|
150
152
|
- **Consent-gated auto-update heartbeat** (the `SessionStart` hook, `plugin/scripts/session-start.sh`). The **first** time the plugin runs on a machine it asks you **once** whether it may keep itself updated in the background β a security-conscious opt-in, because self-update can change the model's own instructions. Your answer is remembered (`~/.cache/ruvnet-brain/.auto-update-pref`) and never asked again. On each session start it does a rate-limited (~15 min) 3s-capped check of the live GitHub `plugin.json`. If a newer plugin version exists **and** you opted in, it downloads it in the background through Claude Code's own trusted marketplace path β but the new version is **staged, not active**: Claude Code only loads plugins at process start, so **this session keeps running the version it started with** until you restart (`claude --continue` brings your conversation right back on the new version). If you declined, it just tells you the command to run. The 512 MB knowledge bundle is handled more conservatively β **detect + notify only**, never auto-applied, because the bundle isn't cryptographically signed yet and applying it would overwrite executable tool files (SEC-0010 #6).
|
|
151
153
|
|
|
152
|
-
- **Stack watchdog status footer** (the `UserPromptSubmit` hook's always-on Gate 0, `plugin/scripts/ground-ruvnet.sh`). Every response ends with one dim status line β e.g. `π§ RuvNet Brain v2.0.
|
|
154
|
+
- **Stack watchdog status footer** (the `UserPromptSubmit` hook's always-on Gate 0, `plugin/scripts/ground-ruvnet.sh`). Every response ends with one dim status line β e.g. `π§ RuvNet Brain v2.0.1 Β· Ruflo: yes Β· AgentDB memory: on` β read from **filesystem ground truth**, not impressions. The version shown is always the one **actually loaded in memory** for this session; if a newer version is staged awaiting a restart, the line says so plainly (`β¦ vX staged, restart to load`). So you never have to wonder whether the brain is on, which version is acting, or whether project memory is wired.
|
|
153
155
|
|
|
154
156
|
- **Nightly publish β `releases/latest` chain** (`scripts/self-update.mjs --publish`, run by the `deploy/com.ruvnet.brain-nightly.plist` LaunchAgent at 03:15). The nightly rebuilds only the repos whose upstream changed, and **if anything was rebuilt** it bumps the product version, cuts a GitHub Release, and advances [`releases/latest`](https://github.com/stuinfla/ruvnet-brain/releases/latest). Plugin and knowledge bundle move under **one** version number, so the heartbeat above picks up both automatically. (The LaunchAgent is not auto-installed β enabling a system scheduler needs explicit owner approval.)
|
|
155
157
|
|
|
@@ -173,7 +175,7 @@ Plus: the **βtake the wheelβ behavioral pipeline** (below), a **4-level beha
|
|
|
173
175
|
|
|
174
176
|
## How it works
|
|
175
177
|
|
|
176
|
-
The expensive work happens **once, at build time**: every covered repo is deep-walked (whole files, full function bodies, plus a symbol index), embedded into **two** vector variants (MiniLM-384 for edge/portability, bge-768 for depth) stored on-disk in **RVF / HNSW**, and distilled into a concepts + capability layer of per-repo primers and cards. That's **
|
|
178
|
+
The expensive work happens **once, at build time**: every covered repo is deep-walked (whole files, full function bodies, plus a symbol index), embedded into **two** vector variants (MiniLM-384 for edge/portability, bge-768 for depth) stored on-disk in **RVF / HNSW**, and distilled into a concepts + capability layer of per-repo primers and cards. That's **129,012 source chunks**. At **query time**, `search_ruvnet` searches every repo's store at once, pools the hits, and runs them through **one cross-encoder rerank** on a common scale β so the truly relevant file wins regardless of which repo it lives in β then returns whole source files, each labeled by repo and path.
|
|
177
179
|
|
|
178
180
|

|
|
179
181
|
|
|
@@ -283,7 +285,7 @@ node forge-ask-all.mjs --dir . --q "How does RuVector implement HNSW vector sear
|
|
|
283
285
|
|
|
284
286
|
This project versions in the open (see the live badge up top for the exact plugin version; the downloadable knowledge bundle is a separate track) β we don't claim βdone,β βcomplete,β or βzero hallucinations.β Where it stands:
|
|
285
287
|
|
|
286
|
-
- β
**The grounding brain is real and proven** β 32 repos,
|
|
288
|
+
- β
**The grounding brain is real and proven** β 32 repos, 129,012 chunks, dual embeddings, cross-encoder rerank, plugin (MCP tool + enforcement hook + skill), all re-runnable.
|
|
287
289
|
- β
**Code-level depth** β the code-rich repos are indexed to full function bodies; βhow is it implemented?β returns the implementation. Verified in the shipped bundle (clean-room 3/3).
|
|
288
290
|
- β
**Routing holds** β named 47/48, described 26/28, scenario 7/8; behavioral L1βL4 all pass; private stores fenced out of the public bundle (zero-leak verified).
|
|
289
291
|
- β οΈ **Two routing residuals** (above) β surfaced, not hidden.
|
package/bin/install.mjs
CHANGED
|
@@ -70,6 +70,7 @@ const FLAG_UPDATE = argv.includes('--update'); // one-shot: pull the latest Rele
|
|
|
70
70
|
const FLAG_ENABLE_NIGHTLY = argv.includes('--enable-nightly'); // schedule that update nightly (macOS LaunchAgent)
|
|
71
71
|
const FLAG_DISABLE_NIGHTLY = argv.includes('--disable-nightly'); // remove the nightly schedule
|
|
72
72
|
const FLAG_NO_NIGHTLY_PROMPT = argv.includes('--no-nightly-prompt'); // don't offer nightly auto-updates at the end of an install
|
|
73
|
+
const FLAG_NO_TELEMETRY = argv.includes('--no-telemetry'); // decline anonymous usage counts without being asked
|
|
73
74
|
// ββ onboarding-experience flags (all optional; every offer is safe to decline) ββ
|
|
74
75
|
const FLAG_YES = argv.includes('--yes') || argv.includes('-y'); // accept every optional offer non-interactively
|
|
75
76
|
const FLAG_WITH_STACK = argv.includes('--with-stack'); // add missing Ruflo/RuVector without prompting
|
|
@@ -992,6 +993,96 @@ export async function offerNightly() {
|
|
|
992
993
|
return 'enabled';
|
|
993
994
|
}
|
|
994
995
|
|
|
996
|
+
// ββ step: offer OPT-IN anonymous usage counts (asked once ever; explicit yes required) ββββββββββββ
|
|
997
|
+
// The whole contract, honestly: counts ONLY (installs / searches / sessions + version) β NEVER the
|
|
998
|
+
// user's queries, code, repo names, or paths. Consent is a plain yes/no file the user can read and
|
|
999
|
+
// flip (~/.cache/ruvnet-brain/.telemetry-consent); nothing is ever sent without the literal "yes".
|
|
1000
|
+
// Fail-private everywhere: no TTY β not asked β not enabled; TEST mode β suppressed entirely.
|
|
1001
|
+
const telemetryStateDir = () => path.join(os.homedir(), '.cache', 'ruvnet-brain');
|
|
1002
|
+
const telemetryConsentPath = () => path.join(telemetryStateDir(), '.telemetry-consent');
|
|
1003
|
+
|
|
1004
|
+
// Same default-yes contract as parseNightlyAnswer, exported under its own name so the telemetry
|
|
1005
|
+
// tests read as telemetry tests: ENTER/y/yes accept; ONLY an explicit n/no declines.
|
|
1006
|
+
export const parseTelemetryAnswer = parseNightlyAnswer;
|
|
1007
|
+
|
|
1008
|
+
// Fire-and-forget install ping β 3s cap, all failures swallowed, payload is { event, v } and
|
|
1009
|
+
// nothing else. Exported (with injectable fetch) so tests can assert the payload without a network.
|
|
1010
|
+
export async function sendInstallPing({
|
|
1011
|
+
version = 'unknown',
|
|
1012
|
+
fetchFn = globalThis.fetch,
|
|
1013
|
+
pingUrl = process.env.RUVNET_BRAIN_PING_URL || 'https://ruvnet-brain.vercel.app/api/ping',
|
|
1014
|
+
} = {}) {
|
|
1015
|
+
try {
|
|
1016
|
+
const ctl = new AbortController();
|
|
1017
|
+
const timer = setTimeout(() => ctl.abort(), 3000);
|
|
1018
|
+
await fetchFn(pingUrl, {
|
|
1019
|
+
method: 'POST',
|
|
1020
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1021
|
+
body: JSON.stringify({ event: 'install', v: version }),
|
|
1022
|
+
signal: ctl.signal,
|
|
1023
|
+
}).catch(() => {});
|
|
1024
|
+
clearTimeout(timer);
|
|
1025
|
+
} catch { /* a lost count is nothing; a broken install step would be everything */ }
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Exported decision matrix (testable under RUVNET_BRAIN_IMPORT_ONLY=1, like offerNightly).
|
|
1029
|
+
// Returns a status string; never throws β a finished install must never be broken by an offer.
|
|
1030
|
+
export async function offerTelemetry(cacheDir) {
|
|
1031
|
+
if (TEST_MODE) return 'suppressed'; // tests: never prompt, never write, NEVER send
|
|
1032
|
+
const consentPath = telemetryConsentPath();
|
|
1033
|
+
if (fs.existsSync(consentPath)) return 'already-set'; // asked once ever β respect the answer
|
|
1034
|
+
if (FLAG_NO_TELEMETRY) {
|
|
1035
|
+
try { fs.mkdirSync(telemetryStateDir(), { recursive: true }); fs.writeFileSync(consentPath, 'no\n'); } catch { /* best-effort */ }
|
|
1036
|
+
return 'declined-flag';
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
step(
|
|
1040
|
+
'Optional: anonymous usage counts',
|
|
1041
|
+
'a simple count of installs/searches tells Stuart the brain is actually helping people β nothing about WHAT you ask',
|
|
1042
|
+
);
|
|
1043
|
+
info(`Counts only β installs, searches, sessions, version. ${c.bold('Never your queries, code, repo names, or paths.')}`);
|
|
1044
|
+
info(c.dim(`Your answer is a plain-text file you can read or flip any time: ${consentPath}`));
|
|
1045
|
+
|
|
1046
|
+
if (!process.stdin.isTTY && !FLAG_YES) {
|
|
1047
|
+
// No terminal to ask on β fail PRIVATE: no consent recorded, so nothing will ever be sent.
|
|
1048
|
+
info(`No interactive terminal here, so I won't assume β usage counts stay ${c.bold('OFF')}.`);
|
|
1049
|
+
info(`Opt in any time: echo yes > ${consentPath}`);
|
|
1050
|
+
return 'not-asked';
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
let yes = true; // --yes accepts every optional offer, this one included
|
|
1054
|
+
if (!FLAG_YES) {
|
|
1055
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1056
|
+
const answer = await new Promise((resolve) =>
|
|
1057
|
+
rl.question(` ${c.cyan('?')} Share anonymous usage counts (installs/searches β never your queries or code)? ${c.dim('[Y/n]')} `, resolve),
|
|
1058
|
+
);
|
|
1059
|
+
rl.close();
|
|
1060
|
+
yes = parseTelemetryAnswer(answer);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
try {
|
|
1064
|
+
fs.mkdirSync(telemetryStateDir(), { recursive: true });
|
|
1065
|
+
fs.writeFileSync(consentPath, yes ? 'yes\n' : 'no\n');
|
|
1066
|
+
} catch (e) {
|
|
1067
|
+
warn(`couldn't record the answer (${e.message}) β defaulting to OFF (nothing will be sent)`);
|
|
1068
|
+
return 'error';
|
|
1069
|
+
}
|
|
1070
|
+
if (!yes) {
|
|
1071
|
+
ok('usage counts are OFF β nothing will ever be sent');
|
|
1072
|
+
return 'declined';
|
|
1073
|
+
}
|
|
1074
|
+
ok('thanks β anonymous counters only, batched to at most one ping a day');
|
|
1075
|
+
// Count this install (the one event the brain itself can't see). Version = the bundle we
|
|
1076
|
+
// just put on disk, read live from its own SOURCE.json β never guessed.
|
|
1077
|
+
let v = 'unknown';
|
|
1078
|
+
try {
|
|
1079
|
+
const j = JSON.parse(fs.readFileSync(path.join(cacheDir, 'SOURCE.json'), 'utf8'));
|
|
1080
|
+
if (typeof j.releaseTag === 'string' && /^[A-Za-z0-9._-]{1,32}$/.test(j.releaseTag)) v = j.releaseTag;
|
|
1081
|
+
} catch { /* unknown is honest */ }
|
|
1082
|
+
await sendInstallPing({ version: v });
|
|
1083
|
+
return 'enabled';
|
|
1084
|
+
}
|
|
1085
|
+
|
|
995
1086
|
// ββ tiny interactive yes/no β SAFE in non-TTY (returns the default; never blocks a piped install) ββ
|
|
996
1087
|
function ask(question, def = false) {
|
|
997
1088
|
if (FLAG_YES) return Promise.resolve(true);
|
|
@@ -1232,6 +1323,11 @@ function success({ cacheDir, isCustom, plugin, env, nightly }) {
|
|
|
1232
1323
|
console.log(` ${c.dim('Either way, your copy only advances when a new Release is actually published.')}`);
|
|
1233
1324
|
}
|
|
1234
1325
|
|
|
1326
|
+
// One tasteful ask, at the moment the value was just delivered β never repeated by the plugin
|
|
1327
|
+
// more than once ever (see session-start.sh's stamped one-liner).
|
|
1328
|
+
console.log(`\n ${c.bold('If it earns it:')} a GitHub star helps other people find the brain β`);
|
|
1329
|
+
console.log(` ${c.bold('https://github.com/stuinfla/ruvnet-brain')} ${c.dim('Β· feedback/ideas: https://github.com/stuinfla/ruvnet-brain/discussions')}`);
|
|
1330
|
+
|
|
1235
1331
|
console.log(`\n ${c.dim('You can\'t break anything β the plugin is disable-able and only acts on RuvNet-shaped work.')}`);
|
|
1236
1332
|
console.log('');
|
|
1237
1333
|
}
|
|
@@ -1255,6 +1351,10 @@ Usage:
|
|
|
1255
1351
|
npx ruvnet-brain --disable-nightly Remove the nightly schedule (safe to run any time)
|
|
1256
1352
|
(a default install RECOMMENDS nightly and asks, defaulting to yes)
|
|
1257
1353
|
node bin/install.mjs --no-nightly-prompt Don't offer nightly auto-updates at the end of the install
|
|
1354
|
+
node bin/install.mjs --no-telemetry Decline anonymous usage counts without being asked
|
|
1355
|
+
(counts of installs/searches ONLY β never queries, code, or paths;
|
|
1356
|
+
opt-in prompt appears once at install; answer lives in a plain file:
|
|
1357
|
+
~/.cache/ruvnet-brain/.telemetry-consent)
|
|
1258
1358
|
node bin/install.mjs --version <tag> Install a specific Release tag (e.g. --version v0.5.0-dev)
|
|
1259
1359
|
node bin/install.mjs --pin Skip the latest-check; use the bundled known-good version
|
|
1260
1360
|
node bin/install.mjs --local Install from a repo clone's dist/ruvnet-brain.zip
|
|
@@ -1362,6 +1462,9 @@ It is safe to re-run at any time. After installing, restart Claude Code so the g
|
|
|
1362
1462
|
// and asking, DEFAULTING TO YES (TTY + macOS + not already on). Non-fatal like every other offer.
|
|
1363
1463
|
let nightly = 'skipped';
|
|
1364
1464
|
try { nightly = await offerNightly(); } catch { /* never let the offer break a finished install */ }
|
|
1465
|
+
// Anonymous usage counts β OPT-IN, asked once ever, right after the nightly offer. Same rule:
|
|
1466
|
+
// an optional offer can never break a finished install.
|
|
1467
|
+
try { await offerTelemetry(cacheDir); } catch { /* fail-private: unanswered = OFF */ }
|
|
1365
1468
|
|
|
1366
1469
|
success({ cacheDir, isCustom, plugin, env, nightly });
|
|
1367
1470
|
})().catch((e) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruvnet-brain",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
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": {
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"test:unit": "vitest run tests/unit",
|
|
15
15
|
"test:cov": "vitest run tests/unit --coverage",
|
|
16
16
|
"test:all": "npm run test:unit && npm run test:integration && npm test",
|
|
17
|
+
"metaharness:receipts": "node scripts/metaharness-receipts.mjs",
|
|
18
|
+
"route:cheap": "node scripts/route-cheap.mjs",
|
|
17
19
|
"metaharness:fix": "node scripts/fix-metaharness-memretrieve.mjs --apply",
|
|
18
20
|
"metaharness:check": "node scripts/fix-metaharness-memretrieve.mjs --check",
|
|
19
21
|
"eval": "node scripts/eval-brain.mjs",
|