From 227bb3f876db312525c99af3effd83e551625a03 Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 29 Jun 2026 12:06:21 -0400 Subject: [PATCH] fix(ruvector-npm): accurate MCP tool count + reconcile tool lists (0.2.33) (#624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(ruvector-npm): accurate MCP tool count + reconcile tool lists (0.2.33) The package advertised 103 MCP tools but the server registers 97, and the README listed 12 tools (brain_agi_*, midstream_*) that don't exist. Three independent sources had also drifted: server TOOLS array (97), the cli 'mcp tools' display list (91, missing the 6 decompile_* tools), and the README count (103). - README: 103 → 97; drop the 12 phantom tool bullets; point to 'npx ruvector mcp tools' as the authoritative list - cli.js 'mcp tools': add the missing 'decompile' group (6 tools) → now lists 97, matching the server - mcp-server.js: report version from package.json instead of the hardcoded (and stale) '0.2.30' - bump 0.2.32 → 0.2.33 All three sources now reconcile to 97. Full test suite passes (73 + 7 + 2 + 8 + 2 across the publish-gate files). Co-Authored-By: claude-flow * fix(ci): commit optimizer module, ignore RUSTSEC-2026-0190, ship src/optimizer Root-causes three pre-existing CI failures on the ruvector npm package: 1. Optimizer test (Cannot find module src/optimizer/index.js): the module was never committed because .gitignore's bare 'index.js' rule (for napi artifacts) silently ignored this hand-written module. Add a negation exception (as already done for @ruvector/sona) and commit index.js — 8 profiles, detectTaskType, applyProfile. Test: 135/135 pass. 2. cargo deny (advisories FAILED): RUSTSEC-2026-0190 (unsoundness in anyhow's Error::downcast_mut, anyhow 1.0.102, single version in tree). Added to deny.toml ignore list with justification + re-review date, consistent with the existing unsoundness exceptions — we never downcast anyhow::Error to a mismatched mutable type. 3. src/optimizer/ now ships (added to package.json files), so the published package's 'optimize' command works instead of graceful-degrading. Pre-existing failures NOT addressed here (separate infra work; main is also red): Functional smoke / Recall quality / Learning check fail because the npm CI installs with --no-optional, so @ruvector/rvf + the native HNSW addon are absent and the engine runs degraded. dependency-review is already continue-on-error (awaiting a repo Dependency-Graph settings flip). Co-Authored-By: claude-flow --- .gitignore | 4 + deny.toml | 8 + npm/packages/ruvector/README.md | 11 +- npm/packages/ruvector/bin/cli.js | 10 +- npm/packages/ruvector/bin/mcp-server.js | 2 +- npm/packages/ruvector/package.json | 3 +- npm/packages/ruvector/src/optimizer/index.js | 177 +++++++++++++++++++ 7 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 npm/packages/ruvector/src/optimizer/index.js diff --git a/.gitignore b/.gitignore index f958547ce..fffb1bb0c 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,10 @@ index.d.ts # (v3 emits no v2-style loader), which published an empty 0.1.6 (#516) !npm/packages/sona/index.js !npm/packages/sona/index.d.ts +# ruvector optimizer is a hand-written module (not a napi artifact); the bare +# `index.js` rule above wrongly ignored it, so it was never committed and CI's +# optimizer test could not resolve it. +!npm/packages/ruvector/src/optimizer/index.js # Environment variables and secrets .env diff --git a/deny.toml b/deny.toml index 64cc17fb8..f6fbf3ff7 100644 --- a/deny.toml +++ b/deny.toml @@ -130,6 +130,14 @@ ignore = [ # mmap loading and other crates; no fixed release on the 0.9 line yet. # We don't pass attacker-controlled offsets to memmap2. Re-review 2026-08-01. "RUSTSEC-2026-0186", + + # anyhow 1.0.102 — unsoundness in `Error::downcast_mut()` (RUSTSEC-2026-0190, + # "unsound", not an exploitable vuln). Triggering it requires downcasting an + # `anyhow::Error` to a mismatched mutable type — a pattern we never use; our + # usage is construct/propagate/`Display`/read-only `downcast_ref`. Single + # version (1.0.102) in the tree. Bump to the patched anyhow once it ships and + # remove this entry. Re-review 2026-08-01. + "RUSTSEC-2026-0190", ] # ───────────────────────────────────────────────────────────────────── diff --git a/npm/packages/ruvector/README.md b/npm/packages/ruvector/README.md index d0d963f71..6ce8f138f 100644 --- a/npm/packages/ruvector/README.md +++ b/npm/packages/ruvector/README.md @@ -69,7 +69,7 @@ npx ruvector hooks init --pretrain --build-agents quality ### MCP Server Integration -RuVector includes an MCP server for Claude Code with 103 tools: +RuVector includes an MCP server for Claude Code with 97 tools: ```bash # Add to Claude Code @@ -86,10 +86,11 @@ claude mcp add ruvector -- npx ruvector mcp start - `hooks_rag_context` — Semantic context retrieval - `hooks_attention_info`, `hooks_gnn_info` — Neural capabilities - `brain_search`, `brain_share`, `brain_status` — Shared brain knowledge -- `brain_agi_status`, `brain_sona_stats`, `brain_temporal`, `brain_explore` — AGI diagnostics -- `brain_midstream`, `brain_flags` — Midstream platform + feature flags -- `midstream_status`, `midstream_attractor`, `midstream_scheduler` — Streaming analysis -- `midstream_benchmark`, `midstream_search`, `midstream_health` — Latency benchmarks + health +- `decompile_package`, `decompile_file`, `decompile_url` — Package/file decompilation + witness +- `edge_status`, `edge_join`, `edge_balance` — Edge cluster coordination +- `identity_generate`, `identity_show` — Agent identity + +See `npx ruvector mcp tools` for the full, authoritative tool list. **MCP tool-access policy (default-deny, ADR-256):** restrict the exposed/callable tool surface with environment variables — useful for least-privilege deployments. diff --git a/npm/packages/ruvector/bin/cli.js b/npm/packages/ruvector/bin/cli.js index 2bb2a42f2..a1647cb2f 100755 --- a/npm/packages/ruvector/bin/cli.js +++ b/npm/packages/ruvector/bin/cli.js @@ -8200,7 +8200,7 @@ mcpCmd.command('info') mcpCmd.command('tools') .description('List all MCP tools with descriptions (JSON output)') - .option('--group ', 'Filter by group (hooks, workers, rvf, rvlite, brain, edge, identity)') + .option('--group ', 'Filter by group (hooks, workers, rvf, rvlite, brain, edge, identity, decompile)') .option('--json', 'JSON output') .action((opts) => { const tools = { @@ -8323,6 +8323,14 @@ mcpCmd.command('tools') { name: 'identity_generate', desc: 'Generate new pi key' }, { name: 'identity_show', desc: 'Show current identity' }, ], + 'decompile': [ + { name: 'decompile_package', desc: 'Decompile an npm package + witness' }, + { name: 'decompile_file', desc: 'Decompile a single file' }, + { name: 'decompile_url', desc: 'Decompile from a URL' }, + { name: 'decompile_diff', desc: 'Diff two decompiled artifacts' }, + { name: 'decompile_search', desc: 'Search decompiled artifacts' }, + { name: 'decompile_witness', desc: 'Verify decompile witness' }, + ], }; // Filter by group if specified diff --git a/npm/packages/ruvector/bin/mcp-server.js b/npm/packages/ruvector/bin/mcp-server.js index bf568f3d2..d2f0e428e 100644 --- a/npm/packages/ruvector/bin/mcp-server.js +++ b/npm/packages/ruvector/bin/mcp-server.js @@ -514,7 +514,7 @@ class Intelligence { const server = new Server( { name: 'ruvector', - version: '0.2.30', + version: require('../package.json').version, }, { capabilities: { diff --git a/npm/packages/ruvector/package.json b/npm/packages/ruvector/package.json index 72994a05f..2505ac4d4 100644 --- a/npm/packages/ruvector/package.json +++ b/npm/packages/ruvector/package.json @@ -1,6 +1,6 @@ { "name": "ruvector", - "version": "0.2.32", + "version": "0.2.33", "description": "Self-learning vector database for Node.js \u2014 hybrid search, Graph RAG, FlashAttention-3, HNSW, 50+ attention mechanisms", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -93,6 +93,7 @@ "bin/", "dist/", "src/decompiler/", + "src/optimizer/", "wasm/", "README.md", "LICENSE" diff --git a/npm/packages/ruvector/src/optimizer/index.js b/npm/packages/ruvector/src/optimizer/index.js new file mode 100644 index 000000000..6ee783b96 --- /dev/null +++ b/npm/packages/ruvector/src/optimizer/index.js @@ -0,0 +1,177 @@ +/** + * RVAgent Optimizer — Claude Code configuration profiles (ADR-139). + * + * Maps a task type (coding, research, quickfix, …) to an optimal set of + * `CLAUDE_CODE_*` environment variables and a permission mode. Profiles are + * derived from the decompiled Claude Code intelligence (see ADR-139) and are + * consumed by `npx ruvector optimize` and the settings generator. + * + * Public API: + * - PERMISSION_MODES — valid permission-mode strings + * - listProfiles() — array of profile names + * - getProfile(name) — { description, permissionMode, env } | null + * - applyProfile(name) — sets process.env, returns { applied, permissionMode } | null + * - detectTaskType(prompt) — infer a profile name from a free-text prompt + */ + +'use strict'; + +/** Permission modes understood by Claude Code's harness. */ +const PERMISSION_MODES = [ + 'default', + 'acceptEdits', + 'bypassPermissions', + 'plan', + 'dontAsk', + 'auto', +]; + +/** + * Task profiles. Every env key MUST start with `CLAUDE_CODE_` and every + * `permissionMode` MUST be a member of {@link PERMISSION_MODES}. + */ +const PROFILES = { + coding: { + description: 'Implementation work — edits with checkpointing and thinking enabled', + permissionMode: 'acceptEdits', + env: { + CLAUDE_CODE_EFFORT_LEVEL: 'high', + CLAUDE_CODE_THINKING: '1', + CLAUDE_CODE_AUTO_COMPACT: '1', + CLAUDE_CODE_FILE_CHECKPOINTING: '1', + }, + }, + research: { + description: 'Investigation and analysis — read-heavy, no edits, deep reasoning', + permissionMode: 'default', + env: { + CLAUDE_CODE_EFFORT_LEVEL: 'high', + CLAUDE_CODE_THINKING: '1', + CLAUDE_CODE_AUTO_COMPACT: '1', + }, + }, + quickfix: { + description: 'Small, fast fixes — brief output, low effort, quick turnaround', + permissionMode: 'acceptEdits', + env: { + CLAUDE_CODE_BRIEF: '1', + CLAUDE_CODE_EFFORT_LEVEL: 'low', + CLAUDE_CODE_THINKING: '0', + }, + }, + planning: { + description: 'Architecture and design — plan mode, no edits until approved', + permissionMode: 'plan', + env: { + CLAUDE_CODE_EFFORT_LEVEL: 'high', + CLAUDE_CODE_THINKING: '1', + }, + }, + background: { + description: 'Long-running background/daemon work — autonomous, minimal prompts', + permissionMode: 'bypassPermissions', + env: { + CLAUDE_CODE_EFFORT_LEVEL: 'medium', + CLAUDE_CODE_AUTO_COMPACT: '1', + CLAUDE_CODE_PROMPT_SUGGESTION: '0', + }, + }, + swarm: { + description: 'Multi-agent swarm coordination — autonomous, hooks-driven', + permissionMode: 'bypassPermissions', + env: { + CLAUDE_CODE_EFFORT_LEVEL: 'medium', + CLAUDE_CODE_AUTO_COMPACT: '1', + CLAUDE_CODE_THINKING: '1', + }, + }, + review: { + description: 'Code review — read-only, no edits, thorough reasoning', + permissionMode: 'default', + env: { + CLAUDE_CODE_EFFORT_LEVEL: 'high', + CLAUDE_CODE_THINKING: '1', + CLAUDE_CODE_BRIEF: '0', + }, + }, + ci: { + description: 'CI / automation pipelines — fast, non-interactive, no suggestions', + permissionMode: 'dontAsk', + env: { + CLAUDE_CODE_FAST_MODE: '1', + CLAUDE_CODE_EFFORT_LEVEL: 'low', + CLAUDE_CODE_PROMPT_SUGGESTION: '0', + }, + }, +}; + +/** + * Keyword → profile rules, evaluated in order. The first rule whose regex + * matches the prompt wins, so more specific task types are listed first. + */ +const DETECTION_RULES = [ + { type: 'quickfix', re: /\b(typo|quick\s?-?fix|one-?liner|small fix|rename)\b/i }, + { type: 'ci', re: /\b(ci|cd|pipeline|github actions|workflow|deploy)\b/i }, + { type: 'swarm', re: /\b(swarm|multi-?agent|coordinate|orchestrat\w*|hive)\b/i }, + { type: 'background', re: /\b(background|daemon|monitor\w*|watch\b|long-?running)\b/i }, + { type: 'review', re: /\b(review|pull request|\bpr\b|audit|critique)\b/i }, + { type: 'planning', re: /\b(plan\w*|architect\w*|design|roadmap|strategy)\b/i }, + { type: 'research', re: /\b(research|investigat\w*|explore|compare|survey)\b/i }, + { type: 'coding', re: /\b(implement|code|build|add|write|refactor|create|fix|function|feature)\b/i }, +]; + +/** List all available profile names. */ +function listProfiles() { + return Object.keys(PROFILES); +} + +/** Return a profile by name, or `null` if it does not exist. */ +function getProfile(name) { + if (!name || !Object.prototype.hasOwnProperty.call(PROFILES, name)) { + return null; + } + return PROFILES[name]; +} + +/** + * Apply a profile's env vars to `process.env`. Returns `{ applied, permissionMode }` + * where `applied` is the map of env vars that were set, or `null` if the profile + * is unknown. + */ +function applyProfile(name) { + const profile = getProfile(name); + if (!profile) { + return null; + } + const applied = {}; + for (const [key, val] of Object.entries(profile.env)) { + process.env[key] = val; + applied[key] = val; + } + return { applied, permissionMode: profile.permissionMode }; +} + +/** + * Infer the most likely task type from a free-text prompt. Defaults to + * `'coding'` for empty/unrecognised input. + */ +function detectTaskType(prompt) { + if (!prompt || typeof prompt !== 'string') { + return 'coding'; + } + for (const rule of DETECTION_RULES) { + if (rule.re.test(prompt)) { + return rule.type; + } + } + return 'coding'; +} + +module.exports = { + PERMISSION_MODES, + PROFILES, + listProfiles, + getProfile, + applyProfile, + detectTaskType, +};