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, +};