From 95772be3d5a479c2504aa214c10b0e9587ebbe65 Mon Sep 17 00:00:00 2001 From: Chris Phillipson Date: Thu, 30 Jul 2026 19:39:38 -0700 Subject: [PATCH 1/2] fix(natives): resolve @claude-flow/security & aidefence when npm nests instead of hoisting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit securityPresent()/aidefencePresent() only checked the top level of ruflo/node_modules, so a false WARN/fail could appear on a status panel when a version conflict left npm nesting the package under @claude-flow/cli/node_modules instead of hoisting it — the package was present, just not where the shallow check looked. Also refreshes package.json's description to reflect the multi-host (Claude/Codex/OpenCode) execution routing shipped through alpha.30. --- package.json | 2 +- src/lib/natives.mjs | 19 +++++++++--- tests/kit/natives.test.mjs | 62 +++++++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index efe063d..8774eac 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@pacphi/agentic-kit", "version": "4.0.0-alpha.30", - "description": "Machine-level setup, healing, and verification kit for your AI agent stack (ruflo/claude-flow, agentic-qe, Claude Code glue) — cross-platform, zero-dependency", + "description": "Setup, healing, verification, and multi-host execution routing (Claude, Codex, OpenCode) for your AI agent stack (ruflo/claude-flow, agentic-qe) — cross-platform, zero-dependency", "type": "module", "bin": { "agentic-kit": "bin/agentic-kit.mjs", diff --git a/src/lib/natives.mjs b/src/lib/natives.mjs index 06fbda0..97f28f6 100644 --- a/src/lib/natives.mjs +++ b/src/lib/natives.mjs @@ -177,8 +177,19 @@ export function dbPathPinStatus({ settingsLocalFile, projectRoot }) { return { warn: false, pinned }; } -export const aidefencePresent = () => - fs.existsSync(path.join(rufloNodeModules(), '@claude-flow', 'aidefence', 'package.json')); +// npm only hoists a @claude-flow/* package to the top of ruflo's node_modules when +// every dependent can share one version; a conflicting sibling (e.g. @claude-flow/cli +// pinning a different range) leaves it nested under that dependent instead. A +// top-level-only check then false-negatives on a package that IS installed — seen +// with @claude-flow/security landing under cli/node_modules instead of hoisting. +// Check the top level plus the known @claude-flow/* dependents (mirrors +// rufloMemoryContexts' consumer list) rather than a full recursive search. +function claudeFlowPackagePresent(name) { + const nm = rufloNodeModules(); + const roots = [nm, path.join(nm, '@claude-flow', 'cli', 'node_modules')]; + return roots.some((root) => fs.existsSync(path.join(root, '@claude-flow', name, 'package.json'))); +} + +export const aidefencePresent = () => claudeFlowPackagePresent('aidefence'); -export const securityPresent = () => - fs.existsSync(path.join(rufloNodeModules(), '@claude-flow', 'security', 'package.json')); +export const securityPresent = () => claudeFlowPackagePresent('security'); diff --git a/tests/kit/natives.test.mjs b/tests/kit/natives.test.mjs index 6b0440c..2d26fc3 100644 --- a/tests/kit/natives.test.mjs +++ b/tests/kit/natives.test.mjs @@ -6,7 +6,8 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; -import { bsq3Root, bsq3IsNative, selfSpecConflicts } from '../../src/lib/natives.mjs'; +import { bsq3Root, bsq3IsNative, selfSpecConflicts, aidefencePresent, securityPresent } from '../../src/lib/natives.mjs'; +import { _setGlobalRootForTest } from '../../src/lib/paths.mjs'; function makeFixture({ withBinding }) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ak-natives-')); @@ -111,3 +112,62 @@ test('selfSpecConflicts ignores non-plain-semver declarations (workspace:/link:/ assert.deepEqual(selfSpecConflicts(dir, '^12.10.0'), []); fs.rmSync(dir, { recursive: true, force: true }); }); + +function makeGlobalRootFixture() { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ak-natives-cfpkg-')); + fs.mkdirSync(path.join(root, 'ruflo', 'node_modules'), { recursive: true }); + return root; +} + +test('securityPresent is true when the package is hoisted to the top of ruflo/node_modules', () => { + const root = makeGlobalRootFixture(); + writePkgJson(path.join(root, 'ruflo', 'node_modules', '@claude-flow', 'security'), { name: '@claude-flow/security' }); + _setGlobalRootForTest(root); + try { + assert.equal(securityPresent(), true); + } finally { + _setGlobalRootForTest(null); + fs.rmSync(root, { recursive: true, force: true }); + } +}); + +test('securityPresent is true when npm nests the package under @claude-flow/cli instead of hoisting it (regression: false WARN when a version conflict prevents hoisting)', () => { + const root = makeGlobalRootFixture(); + writePkgJson( + path.join(root, 'ruflo', 'node_modules', '@claude-flow', 'cli', 'node_modules', '@claude-flow', 'security'), + { name: '@claude-flow/security' }, + ); + _setGlobalRootForTest(root); + try { + assert.equal(securityPresent(), true); + } finally { + _setGlobalRootForTest(null); + fs.rmSync(root, { recursive: true, force: true }); + } +}); + +test('securityPresent is false when the package is genuinely absent from both locations', () => { + const root = makeGlobalRootFixture(); + _setGlobalRootForTest(root); + try { + assert.equal(securityPresent(), false); + } finally { + _setGlobalRootForTest(null); + fs.rmSync(root, { recursive: true, force: true }); + } +}); + +test('aidefencePresent follows the same top-level-or-nested-under-cli resolution', () => { + const root = makeGlobalRootFixture(); + writePkgJson( + path.join(root, 'ruflo', 'node_modules', '@claude-flow', 'cli', 'node_modules', '@claude-flow', 'aidefence'), + { name: '@claude-flow/aidefence' }, + ); + _setGlobalRootForTest(root); + try { + assert.equal(aidefencePresent(), true); + } finally { + _setGlobalRootForTest(null); + fs.rmSync(root, { recursive: true, force: true }); + } +}); From 88c5aba77c676aef93cc7e751ce6aef97e39d65a Mon Sep 17 00:00:00 2001 From: Chris Phillipson Date: Thu, 30 Jul 2026 19:42:37 -0700 Subject: [PATCH 2/2] docs: credit RuvNet Brain's author, mention it in package/repo descriptions Stuart Kerr's RuvNet Brain (github.com/stuinfla/ruvnet-brain) is a managed subsystem ak setup installs and ak sync keeps current, but it was missing from the prior-art credits and from the npm/GitHub-facing descriptions alongside ruflo and agentic-qe. --- README.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d30054e..85958b6 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,7 @@ codebase. - [rUv](https://github.com/ruvnet) — [ruflo/claude-flow](https://github.com/ruvnet/ruflo); upstream issues filed from this kit's verification work: [#2219](https://github.com/ruvnet/ruflo/issues/2219), [#2222](https://github.com/ruvnet/ruflo/issues/2222), [#2239](https://github.com/ruvnet/ruflo/issues/2239), [#2360](https://github.com/ruvnet/ruflo/issues/2360), [#2549](https://github.com/ruvnet/ruflo/issues/2549), [#2670](https://github.com/ruvnet/ruflo/issues/2670) - [Dragan Spiridonov](https://github.com/proffesor-for-testing) — [agentic-qe](https://github.com/proffesor-for-testing/agentic-qe) +- [Stuart Kerr](https://github.com/stuinfla) — [RuvNet Brain](https://github.com/stuinfla/ruvnet-brain), the source-grounded knowledge base over the rUv stack that `ak setup` installs and `ak sync` keeps current - [Ciprian Melian](https://github.com/ciprianmelian) — prior art: [repair gist](https://gist.github.com/ciprianmelian/eb7e8ff7d24018141ca34bb8a7e216a6) ### Contributors diff --git a/package.json b/package.json index 8774eac..44d538f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@pacphi/agentic-kit", "version": "4.0.0-alpha.30", - "description": "Setup, healing, verification, and multi-host execution routing (Claude, Codex, OpenCode) for your AI agent stack (ruflo/claude-flow, agentic-qe) — cross-platform, zero-dependency", + "description": "Setup, healing, verification, and multi-host execution routing (Claude, Codex, OpenCode) for your AI agent stack (ruflo/claude-flow, agentic-qe, RuvNet Brain) — cross-platform, zero-dependency", "type": "module", "bin": { "agentic-kit": "bin/agentic-kit.mjs",