Commit Graph
9865 Commits
Author SHA1 Message Date
rUvandr e00a82f2fa docs: ChatGPT plugin badge in the README and a plugin section in the user guide (#4438)
Co-authored-by: r <r@x>
2026-10-23 15:04:54 -04:00
rUvandr af6c74b3bc release: 3.60.1 (#4437)
* release: 3.60.1

Patch release: cleanup --keep-config fix (#4435) and ruflo-console 0.45.1 Windows writes (#4434).

Co-Authored-By: RuFlo <ruv@ruv.net>

* docs(changelog): 3.60.1 includes #4436

Co-Authored-By: RuFlo <ruv@ruv.net>

* docs(changelog): fix escaped backslashes

Co-Authored-By: RuFlo <ruv@ruv.net>

---------

Co-authored-by: r <r@x>
v3.60.1
2026-10-23 14:52:53 -04:00
Mario JauvinandRuFlo 43a3954350 fix(ruflo-console): accept Windows paths in the autopilot envelope (follow-up to #4434 R1) (#4436)
* fix(console): accept Windows paths in the autopilot envelope

#4434 added Windows path parity across the console, but the autopilot
envelope's own path validator still rejected any `\`, and the downstream
`isProtectedPath` and `pathAllowed` guards compared with POSIX-only string
ops. The code is unreachable today because autopilot start is still gated
off on Windows (ap-windows-gate.spec.ts), but it is the next thing a
Windows user will trip on when that gate is lifted — the natural
`C:\work\app` form, and every UNC path, is refused with the opaque "is
not an absolute folder below the root with no .." error.

Four narrow fixes in `hooks/data/ap-envelope.ts`, driven by the
separator-aware helpers already in `hooks/data/paths.ts`:

- `isProtectedPath`: fold `\` to `/` on a drive-lettered or UNC path
  before the protected-dir compare.
- `validateEnvelope` path check: drop the unconditional
  `path.includes('\')` reject (it contradicted `isAbsolutePath` which
  already accepts `C:\x` and `\srv\share`), and tighten the drive-root
  regex `/^[A-Za-z]:\/?$/` → `/^[A-Za-z]:[\/]?$/` so `C:\` is still
  refused explicitly (was previously caught by the backslash reject).
- `validateEnvelope` output canonicalisation: use `trimTrailing` instead
  of `/\/+$/` replace, so a trailing `\` on a Windows path is stripped
  the way `/` already is on POSIX.
- `pathAllowed`: use `hasParentSegment`, `samePath` and `below` from
  paths.ts. A file at `C:\work\app\file.ts` is now inside envelope path
  `C:\work\app` (and `c:\WORK\app\file.ts` is too), while
  `C:\work\application\file.ts` still is not.

Semantics on a POSIX envelope are unchanged — the new helpers degrade
exactly to their old string ops when neither side is a Windows path.

New `tests/ap-envelope-windows-paths.spec.ts` pins what should and should
not be accepted (13 cases: backslash drive, forward-slash drive, UNC,
trailing-separator strip, `..`, bare-drive-root, protected-folder,
pathAllowed under case + separator variation, prefix-match rejection).

Verified on Windows 11: new spec 13/13, ap-core.spec.ts 31/31 (no POSIX
regression), 5 envelope-adjacent specs 64/64 combined, full console suite
3444 total · 3399 pass · 13 fail — same pre-existing baseline as #4434's
own run, zero regressions.

Follow-up to #4434 R1 (https://github.com/ruvnet/ruflo/pull/4434#issuecomment-6371423874).

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix: keep the backslash reject for POSIX-rooted envelope paths

#4436 review (@ruvnet): dropping `path.includes('\')` outright also
dropped the refusal for a POSIX-rooted path with backslashes smuggled
in. `/work/app\..\..\other` passes `isAbsolutePath` (POSIX form), and
`hasParentSegment` splits POSIX-only when `isWindowsPath` says the path
is not Windows, so the `..` segments are invisible to it. On a Windows
host, that leading-`/` path then resolves to `\other`, outside the
folder the user named. Unreachable today because autopilot is still
gated off on Windows, but it is the guard that gate's removal depends
on.

Keep the backslash reject, but only for non-Windows paths (POSIX
should never have `\` in its path). The native Windows forms
`C:\work\app` and `\srv\share\app` continue to pass; the mixed
`/work/app\..\..\other` form is refused as before.

Added case to tests/ap-envelope-windows-paths.spec.ts.

Co-Authored-By: RuFlo <ruv@ruv.net>

---------

Co-authored-by: RuFlo <ruv@ruv.net>
2026-10-23 14:30:23 -04:00
nk-batman21 1d6930e583 fix(cleanup): honor --keep-config (#4435)
The parser stores `--keep-config` and `-k` as `keepConfig`, but cleanup
read `flags['keep-config']`, which is always undefined. So
`cleanup --force --keep-config` deleted claude-flow.config.json and
stripped the hooks and claudeFlow blocks from .claude/settings.json
anyway. Read `keepConfig`, keeping the kebab key as a fallback as
update, init and mods do.
2026-10-23 14:06:44 -04:00
ea25a05abe fix(ruflo-console): Windows writes go through the host filesystem (#4433) (#4434)
* test(console): fixture file system accepts Windows drive paths

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): Windows project folders are project folders (ADRs, export, catalog, mission cost)

Guards that accepted only a path starting with "/" treated a Windows session
folder (C:\Users\x\proj) as "no project folder is known". Adds hooks/data/paths.ts
(pure string helpers: isAbsolutePath, joinPath, samePath, plus hasParentSegment,
normalizePath, below, trimTrailing, isWindowsPath) and uses it at the absolute-path
guards in adr, event-export, wf-file, plugin-catalog, mission-cost (and the ledger
install path), wf-worktrees, skills-lab, wf-drill-io, facts, ap-envelope and
register. Each caller keeps its own ".." refusal; POSIX paths take the same code
path as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): protected autopilot folders match ignoring case on Windows paths

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): find the home folder on Windows (sessions, plugin health, What's new)

Implements homeOf function that checks HOME → USERPROFILE → HOMEDRIVE+HOMEPATH
with comprehensive test coverage. Fixes state.home initialization on Windows
where HOME is unset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): disk writes work on Windows through the host file API

Windows has none of uname, dd, sh, install, mkdir or cp in Claude Code's process
environment, so every console write (Events, Timeline, autopilot journal, ADRs,
exports, .gitignore) failed there. New write flavor host-fs, chosen on win32 (no
uname is run), carries the enumerated writes through $.fs.read/write/exists via
writeVia. Linux GNU and macOS/BSD argv are unchanged (pinned by test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): autopilot kill flag can be cleared on Windows; document host-fs create window

Windows has no delete, so clearKill rewrites the KILL flag with a fixed marker and
killSeen treats exactly that content as not set (host-fs only); stopNow rewrites
the flag empty so a second stop is seen. POSIX rm/stat behaviour is unchanged.
write-via and wf-file now say host-fs creates are check-then-write (no O_EXCL).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): no Linux-only process probe on Windows; clear probe timers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): a hive started from the console shows at once

When a start's CLI run exited 0 but its verify fails (the CLI can return
before its state file lands), wait 1 s via host.after, read fresh and verify
once more before reporting the "expected ..." text. No retry when the run
failed; exactly one retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(console): pin Windows containment, parent-segment and UNC rules in paths.ts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): workspace keys equal across drive-letter case and trailing separator

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): autopilot start is refused on Windows until atomic appends and Windows-aware path checks exist

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(console): 0.42.2 changelog and version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(console): read home variables by literal name so the module loads

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* post-merge cleanup: finish #4036 integration

The Option B merge commit (4313013cb) landed cleanly against upstream/main
but a batch of merge-resolution edits in the working tree were not persisted
into the commit itself. This commit finishes the integration:

Path-helper consolidation (abs-path.ts → paths.ts):
- hooks/data/wf-drill-io.ts: add missing paths.ts import for below,
  hasParentSegment, isAbsolutePath, samePath, trimTrailing (fixes 4
  wf-drill-read.spec.ts failures that threw `ReferenceError: isAbsolutePath
  is not defined` at runtime).
- hooks/adr.ts: migrate from ./data/abs-path to ./data/paths, pulling in
  joinPath which is needed at lines 89/94.
- hooks/register.ts: drop the rootedPath import from the deleted
  abs-path.ts; inline the equivalent isAbsolutePath+joinPath expression.
- hooks/data/event-export.ts, hooks/data/wf-file.ts: drop the duplicate
  isAbsolutePath import from the deleted ./abs-path (the main-side wider
  './paths' import already covers it).

VERIFY_RETRY_MS lifecycle (ADR-450 interaction with Marius' feature):
- hooks/runner.ts: extend the retry condition — `verified === 'n/a' &&
  spec.verify !== undefined` also retries, so a snapshot that hasn't
  loaded yet (null, not "no verifier") gets the one-second re-check.

Tests brought in line with the new timing / platform guards:
- tests/tui-actions.spec.ts: settle(600) → settle(1200) at the point the
  runner's VERIFY_RETRY_MS one-second re-check has to resolve before the
  verdict is drawn.
- tests/windows-drive-path-sites.spec.ts: fsWithDir made separator-agnostic
  via a normSeps helper, since joinPath now uses the base path's own
  separator (`\` on a Windows base).

Version bump:
- .claude-plugin/plugin.json, hooks/version.ts, scripts/smoke.sh: 0.44.7
  → 0.44.8.
- CHANGELOG.md: prepend 0.44.8 entry crediting #4036 and @socrates6969.

Verified: full console suite 3400 total · 3362 pass · 15 fail · 23 skipped.
Every one of the 15 remaining fails is pre-existing on main or a
platform/env limitation (Windows chmod, fake-timer timing, codex not
installed) — zero merge-caused failures remain.

Co-Authored-By: RuFlo <ruv@ruv.net>

---------

Co-authored-by: Marius Carlsson <109006081+socrates6969@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mario Jauvin <marioja@users.noreply.github.com>
Co-authored-by: r <r@x>
2026-10-23 14:04:03 -04:00
rUvandr 7402c321a5 release: 3.60.0 (#4431)
* chore(release): @claude-flow/hooks 3.0.2, @claude-flow/mcp 3.1.2

hooks: restart-timer cancellation on stop (#4413), dedup count (#4415), non-blocking fallback embeddings (#4422).
mcp: unguessable session ids (#4418).

Co-Authored-By: RuFlo <ruv@ruv.net>

* release: 3.60.0

@claude-flow/cli, claude-flow and ruflo to 3.60.0 (minor: new MCP tools — the six rure_* reverse-engineering tools).
@claude-flow/mcp pin to 3.1.2 (published standalone: unguessable session ids).

Co-Authored-By: RuFlo <ruv@ruv.net>

* docs(changelog): 3.60.0

Co-Authored-By: RuFlo <ruv@ruv.net>

---------

Co-authored-by: r <r@x>
v3.60.0
2026-10-23 13:20:20 -04:00
rUvandr 4efb421e33 fix(update): never treat an older registry version as an update (downgrade on 3.60.0) (#4432)
getUpdateType compared major, minor and patch independently. With 3.60.0
running and 3.59.17 as the registry latest, the minor check failed but
patch(17) > patch(0) returned 'patch', so the startup check auto-installed
3.59.17: a downgrade. It also made 'ruflo init' run npm install in the project
and time out the mods-cli-bin test on the 3.60.0 release PR.

An update is a strictly newer version: guard with semver.gt first.

Co-authored-by: r <r@x>
2026-10-23 12:48:58 -04:00
rUvandr e8c98eb916 feat(ruflo-console): ruRE page in TOOLS — static reverse engineering from the console (ADR-494) (#4430)
* feat(ruflo-console): ruRE page in TOOLS — static reverse engineering from the console (ADR-494)

A TOOLS page for the six rure_* tools of ruflo-rure, built the way the Sandbox
page is: six Dev Tools entries (dt-re-*) drawn on a page of their own, so they
share the confirm gate, palette ids and result panel. Five rows are $0 reads;
UNPACK .ASAR writes outside the project and asks first. A new 'sample' field
takes only a project-relative path. Results are the analyzed file's own text
and pass plain() so a hostile binary cannot put an escape on the terminal; a
number that is not a measurement reads n/a. A CLI without the tools says to
update. Enter in a ruRE field runs that page's row.

Tests: rure-page.spec.ts (catalog vs real schemas, readers vs real captured CLI
output, hostile answers, page, guide, palette, Enter routing) and
rure-e2e.spec.ts (every row's argv through the built CLI on real files,
including a hostile ELF; fails with plain() removed). Console 0.44.7 -> 0.45.0;
ruflo-rure 0.4.1 -> 0.4.2 (docs only).

Co-Authored-By: RuFlo <ruv@ruv.net>

* test(ruflo-console): ruRE e2e accepts either AIDefence outcome (CI has no AIDefence)

The .NET e2e asserted a string literal was 'hidden by AIDefence', true only
where the optional package is installed. In CI the tool reports screened:false
and the page says 'not screened: AIDefence was unavailable'. Both are honest;
the test now pins that the screening state is always on screen.

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix(ruflo-console): ruRE EVM result no longer shows a doubled ellipsis

plain() already marks a clipped value; the reader added a second one to the
event topic and the IPFS hash. Test pins it.

Co-Authored-By: RuFlo <ruv@ruv.net>

---------

Co-authored-by: r <r@x>
2026-10-23 12:02:36 -04:00
Aaditya 88349655eb fix(github): stop reporting merge/close/update success when nothing happened (#4425)
* fix(github): stop reporting merge/close/update success when nothing happened

github_pr_manage merge/close and github_issue_track update/close
returned success when nothing happened on GitHub:

- A failed `gh` call (merge conflict, failing checks, auth, unknown
  number) fell through to the local JSON store and returned
  { success: true, source: 'local-store' }.
- That fallback matched records with key.includes(String(n)) against
  `pr-<Date.now()>` ids. It returned success whether or not anything
  matched, and could mark an unrelated record (prNumber 12 vs
  pr-1786319612345).
- github_pr_manage validated owner/repo but never passed them to `gh`,
  so merge/close acted on the cwd's repository (fixed for issues in
  #2963).

Now:
- When `gh` is present, a failed command returns success: false.
- Offline, only a local record whose id is exactly pr-<n> / issue-<n>
  is updated; otherwise the call returns success: false.
- A missing number is an error.
- PR actions pass --repo owner/repo.

list/create and their offline local-store behaviour are unchanged.

Fixes #4424

* fix(github): include gh's error text when merge/close/update fails

Per the #4424 triage: a failed `gh` call now reports gh's stderr (or
the error message when there is none) along with the action, e.g.
"gh pr merge 12 failed: X Pull request #12 is not mergeable: ...".

runArgv keeps its string | null contract and is now a thin wrapper over
runArgvResult, which returns { out } or { error }. The four mutating
paths use runArgvResult directly.

Refs #4424
2026-10-23 09:59:23 -04:00
Aaditya f6c6a32432 fix(neural): refuse neural_compress prune instead of deleting every pattern (#4428)
neural_compress prune deleted every pattern whose usageCount was below
targetSize and saved the result. Nothing in the neural store records
usage: patterns are written with usageCount 0 and only max-merged by
distill and import. So any targetSize above 0, including the 0.5
default and a "10% reduction" of 0.1, deleted and persisted every
pattern while reporting success.

Prune now returns success: false with the reason, and never calls
saveNeuralStore(), until usage is actually tracked. This is the same
treatment quantize received in #4195. The targetSize schema description
no longer promises a 0-1 size reduction. neural_optimize's zero-norm
prune is a separate path and is unchanged.

Fixes #4423
2026-10-23 09:46:01 -04:00
Aaditya 55cb051880 fix(daa): record performanceScore 0 as a failure, not a 0.8 success (#4427)
daa_agent_adapt read the score as
`(input.performanceScore as number) || 0.8`:

- A failing score of 0 became 0.8, raised successRate, and was sent to
  AgentDB as { success: true, quality: 0.8 }.
- A missing score was recorded as the same fabricated 0.8 success.
- Out-of-range scores were accepted, although the schema says 0-1.

A given score must now be a finite number in [0, 1], and an invalid one
returns success: false without touching the agent. An omitted score
records no score: successRate and the AgentDB outcome are left alone,
while the adaptation itself (feedback and the adaptation count) is
still recorded.

Fixes #4426
2026-10-23 09:40:25 -04:00
ordinary being b45b035e49 fix(reasoningbank): stop blocking the event loop on fallback embeddings (#4422)
FallbackEmbeddingService.embed() spawned 'npx agentic-flow@alpha embeddings generate' with execFileSync, so whenever the primary embedder was unavailable each embedding could freeze the process for up to 10 seconds. Swap to async execFile and await the callback; keep the same 10s timeout, shell:false argument array, JSON parsing, cache and hash fallback (#4410 memory scan).\n\nExport the service for a focused regression. The test mocks both execFile and execFileSync: candidate lets a 10ms timer fire while the 60ms child is pending and returns the child's vector; baseline (export-only) blocks the timer and fails.
2026-10-23 08:57:37 -04:00
ordinary being aca151dfe1 fix(memory): rescue each store's embedder, not just the first (#4421)
rescueAgentdbEmbedder() used a process-wide boolean to avoid re-patching. AgentDB registries are keyed by dbPath and each owns a distinct embedder, so after the first store was rescued every later store kept its mock embedder and wrote/searchable vectors from AgentDB's random fallback (#4410 memory scan).\n\nThe per-embedder __ruvectorRescued sentinel already makes the patch idempotent. Remove the global flag, keep the sentinel check, and export the helper for a focused two-store regression. Baseline (export-only, no behavior change) rescues the first embedder and leaves the second false; candidate rescues both.
2026-10-23 08:33:50 -04:00
DexandDextheking1 47720bd684 fix(mcp): use crypto randomUUID for session ids (#4418)
Co-authored-by: Dextheking1 <Dextheking1@users.noreply.github.com>
2026-10-23 08:24:22 -04:00
rUvandr bcaf737032 test(agentbbs): regression test for roomId path traversal (follow-up to #4416) (#4420)
publish and watch must reject roomIds containing '..'. Fails without the
#4416 validator change, passes with it.

Co-authored-by: r <r@x>
2026-10-23 08:00:36 -04:00
Dragan SpiridonovandClaude Opus 5.5 7d18ee7a03 chore(marketplace): pin agentic-qe-fleet to v3.15.12 (#4419)
Move the external agentic-qe-fleet entry from v3.15.11 (221d6de1) to
v3.15.12 (90a91fe3), the current npm latest, and update the README links,
the external-plugins test fixture and ADR-484.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 07:47:08 -04:00
ordinary being 1baacb6856 fix(reasoningbank): count each duplicate removal once (#4415)
* fix(reasoningbank): count each duplicate removal once

consolidate() built its candidate array once, then kept comparing entries after deleting them. In a 3+-way similarity cluster it removed a tombstoned pattern again and incremented duplicatesRemoved a second time, issuing redundant storage/HNSW deletes and overstating the cleanup metric (#4410 deferred memory finding).\n\nSkip any pattern that is no longer live before starting its outer iteration or comparing it as a candidate. Add a 3-way cluster regression: baseline reports 3 removals for 2 duplicates; candidate reports 2 and leaves one survivor.

* fix(reasoningbank): stop comparing after the outer pattern is removed

Review repro: with lowest-quality-first insertion, the outer candidate could be removed inside its own inner loop, then keep comparing as a tombstone and remove a later live pattern. Break out of the inner loop when toRemove is patterns[i]. Parametrize the regression over high-first, low-first and mid-first insertion orders (3/3 candidate, low-first fails on baseline).
2026-10-23 07:44:16 -04:00
DexandDextheking1 279cb1fbfb fix(agentbbs): reject .. in roomId in agentbbs-tools validator (#4416)
Co-authored-by: Dextheking1 <Dextheking1@users.noreply.github.com>
2026-10-23 07:36:53 -04:00
Dragan Spiridonov d60e6cfc6b fix(security): refresh federation peer standalone TLS lock (#4414) 2026-10-23 06:16:02 -04:00
ordinary being aa26e0d8c8 fix(daemon): cancel pending auto-restart on stop (#4413)
DaemonManager's failure backoff used a bare setTimeout stored only in a local closure. stop() cleared the interval but not that timeout, so an explicit stop could be undone about a second later when the pending restart fired. Repeated failures could also accumulate multiple backoff timers (#4410 automation scan).\n\nTrack backoff timers per daemon, clear them in start() and stop(), supersede an older backoff before scheduling a new one, and re-check stopped/stopping/disabled state inside the timer callback. unref() the timer so it does not keep the process alive. Add a fake-timer regression; baseline restarts after stop (2 attempts), candidate stays stopped (1).
2026-10-23 05:09:31 -04:00
ordinary being e0bc9b3efc fix(daemon): keep one timer per worker type (#4412)
setWorkerEnabled(type, true) called scheduleWorker() without clearing an existing timeout. this.timers kept only the newest handle, so each redundant enable leaked the previous timer; the leaked timer survived stop() (which only clears current map entries) and could run the worker again untracked. Disabling also left the type in pendingWorkers, and an in-flight run rescheduled itself even after disable (#4410 automation scan).\n\nClear any existing timer at schedule time, avoid scheduling when a timer or in-flight run already owns the type, remove disabled types from the pending queue, and only reschedule after a run while the worker is still enabled. Add fake-timer regressions; baseline leaks three timers after three enables and keeps the disabled pending worker.
2026-10-23 04:17:14 -04:00
ordinary being 11e4c2beb2 fix(agntcy): report SDKs missing the command API as not configured (#4409)
detectAgntcyRuntime() treated any module that resolved as configured, even though the three AGNTCY commands call createSlimTransport/joinGroup/publishAgentRecord and the real @agntcy/slim-bindings exports a different class-based API (#4376). Users with the package installed and an endpoint set therefore got configured:true and only discovered the mismatch at the command call site.\n\nAdd an optional requiredExports probe to detectAgntcyRuntime, pass each command's actual function names, and report a resolved-but-incompatible SDK as configured:false with the missing exports in the reason. Commands now take the normal not-configured path with an accurate message.\n\nThis does not implement the missing SDK API; it stops the capability check from claiming an API exists when it does not.
2026-10-23 02:47:16 -04:00
rUvandr a413f47812 fix(doctor): read the version of a claude.cmd shim on Windows (#4397 part 3) (#4408)
Node rejects execFile of .cmd/.bat on Windows, so the npm-global shim read
as 'version unknown'. Probe shims via cmd.exe with a quoted path; skip paths
containing cmd metacharacters.

Co-authored-by: r <r@x>
2026-10-22 22:58:56 -04:00
rUvandr a2465d80ba fix(cost-tracker): run memory store in CLAUDE_FLOW_CWD (#4397 part 2) (#4407)
The Stop hook spawned the CLI with no cwd, so it created a stray
.swarm/memory.db in the session project instead of the MCP data root.
Pass cwd: CLAUDE_FLOW_CWD || process.cwd().

Co-authored-by: r <r@x>
2026-10-22 22:35:14 -04:00
rUvandr 17da11320a fix(memory): restore 10000-row bridge search window, configurable (#4397 part 1) (#4405)
The #2982 ORDER BY fix kept LIMIT 1000, so rows outside the newest 1000
were unfindable (3.38.3 and the sql.js path use 10000). Default is now
10000; CLAUDE_FLOW_SEARCH_WINDOW overrides (clamped 100..100000).

Co-authored-by: r <r@x>
2026-10-22 22:16:10 -04:00
rUvandr 92731bc6bf fix(ruflo-rure): security review fixes — gate coverage, symlinks, ReDoS, parser budgets (#4403)
Closes the findings from an independent review of ruRE 0.1.0-0.4.0:
screen every attacker-derived string and report screened/skippedFiles,
walk without following symlinks under a total-byte budget, linear EVM
file sniffing, an iteration budget for ELF/PE table walks, guarded ELF
e_entry, and single-descriptor reads. Each fix has a test that fails
without it.

Co-authored-by: r <r@x>
2026-10-22 19:39:46 -04:00
rUvandr e6ec9cd687 feat(ruflo-rure): Phase 4 — EVM bytecode static inventory (#4402)
Adds rure_analyze_evm per ADR-0005: aligned disassembly and pattern scan
for dispatcher selectors, hazardous opcodes, embedded addresses, event
topics, EIP-1167 minimal proxies, solc metadata and embedded strings.
No interpreter, RPC or network; PUSH data is never read as opcodes;
input capped at 256 KiB; embedded strings pass the AIDefence gate.

Co-authored-by: r <r@x>
2026-10-22 19:11:43 -04:00
rUvandr d835597e0b feat(ruflo-rure): Phase 3 — .NET assembly metadata inspection (#4401)
Adds rure_analyze_dotnet per ADR-0004: an original pure-TypeScript
ECMA-335 metadata reader (assembly identity and references, types,
method count, P/Invoke imports, user-string literals, entry point).
No dependency, no IL decoding, never loads or runs the assembly. Table
extents are validated against the stream before any row is read, and all
extracted text passes through the AIDefence gate.

Co-authored-by: r <r@x>
2026-10-22 18:52:56 -04:00
rUvandr b31d2b0471 feat(ruflo-rure): Phase 2 — native binary static analysis (ELF/PE/Mach-O) (#4400)
Adds rure_analyze_binary per ADR-0003: an original pure-TypeScript
container parser (format, arch, entry, sections, linked libraries,
imported/exported symbols, optional strings). No disassembler, no new
dependency, never loads or executes the target. Reads are bounds-checked,
tables and strings are capped, files over 128 MiB are refused, and all
extracted text passes through the AIDefence gate.

Co-authored-by: r <r@x>
2026-10-22 18:35:26 -04:00
rUvandr 1b31914579 feat(ruflo-rure): new reverse-engineering plugin, Phase 1 (JS/Electron static analysis) (#4396)
* docs(ruflo-rure): ADR-0001 scope/dual-use policy, ADR-0002 Phase 1 architecture

Fixes #4395 (part 1 — research/planning phase)

Co-Authored-By: RuFlo <ruv@ruv.net>

* feat(ruflo-rure): Phase 1 implementation — JS/Electron static analysis

Implements the plugin scaffold (manifest, skill, command, agent, smoke
test, benchmark) and the three Phase 1 MCP tools per ADR-0001/0002:

- rure_extract_asar: resource-capped .asar extraction via @electron/asar,
  checking the archive header's declared size/file count before writing
  anything (decompression-bomb guard).
- rure_analyze_js_app: TypeScript-compiler-API-based static analysis —
  import/export graph, Electron process-boundary detection (ipcMain/
  ipcRenderer/contextBridge, require("electron"), nodeIntegration flags),
  every finding evidence-sourced to file + line range.
- rure_find_feature: deterministic lexical/structural feature search,
  explicitly not semantic/LLM matching.

Every extracted string is routed through the AIDefence content gate
before being returned; flagged content is redacted, never dropped
silently. Parses only — never executes analyzed code, independently
verified by scripts/smoke.mjs grepping the real source (comments
stripped first, so the doc comment describing the property can't trip
its own check).

27 new tests, all passing. Typecheck clean. Tool-description audit
(ADR-112) clean. Benchmark: ~3.6k-18k files/s on a synthetic Electron
bundle (plugins/ruflo-rure/scripts/benchmark.ts).

Trust tier: experimental. Marketplace/directory submission is a
separate, explicit decision, not part of this change (ADR-0001).

Part of #4395.

Co-Authored-By: RuFlo <ruv@ruv.net>

* feat(console): add ruflo-rure to the plugin map (devtools section)

Every plugin directory needs a line in PLUGIN_MAP or the console's own
plugin-coverage test fails. ruflo-rure lands in devtools, same section
as the other inspection/analysis plugins (ruflo-browser, ruflo-jujutsu,
ruflo-testgen).

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix(ruflo-rure): fix CI failures — real deps, capability-brain classification

Three real bugs surfaced by PR #4396's CI, not flakes:

- typescript was only a devDependency of @claude-flow/cli, but
  src/rure/ts-walk.ts needs it at runtime. Moved to dependencies.
  (CI: "Cannot find package 'typescript'" in a from-source install.)
- @claude-flow/aidefence was never declared as a dependency of
  @claude-flow/cli at all (only resolvable locally via monorepo
  hoisting), so the AIDefence gate silently no-op'd in CI's isolated
  install. Added as an optionalDependency, matching the existing
  graceful-degradation code path and the browser_act precedent this
  also fixes.
- capability-brain.ts classified every registered MCP tool into a
  domain by prefix; the three new rure_* tools had no domain, so
  guidance-brain.test.ts's "classifies every live registered tool"
  assertion failed. Added a 'reverse-engineering' domain (experimental
  maturity, matching ADR-0001's trust tier) and its package-owner
  lookup entry.

All 35 rure + guidance-brain tests pass; typecheck clean;
audit-tool-descriptions and audit-cli-mcp-tools clean.

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix(ruflo-rure): revert @claude-flow/aidefence optionalDependency, mock it in tests instead

Reverts the optionalDependency added in 2570cf39d — it's on the #2561
forbidden-heavy-optionalDependencies list (CI's "Enforce CLI + ruflo
optionalDependencies budget" guard caught it): adding it back to
@claude-flow/cli regresses the cold `npx -y @claude-flow/cli@alpha
--version` startup time that #2561 fixed.

gateWithAIDefence's dynamic import was always meant to be speculative
— present only if something else in the install graph happens to pull
it in, same as the existing (unexported) copy in
browser-intent-tools.ts, which never declares it as a dependency
either. The real fix for "my tests need deterministic flagging
behavior" is mocking the module boundary, not forcing the real
optional package to be installed. Both rure-aidefence-gate.test.ts and
rure-tools.test.ts now vi.mock('@claude-flow/aidefence', ...), matching
the precedent in issue-2670-security-defend.test.ts. Added one more
test asserting graceful pass-through (not a thrown error) when the
module genuinely isn't resolvable.

36/36 rure + guidance-brain tests pass; typecheck clean; CLI
optionalDependencies back to 12 (budget 13), no forbidden entries.

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix(ruflo-rure): alias typescript/@electron/asar in the isolated analyze-command test

analyze-json-output.test.ts runs analyzeCommand in a fully isolated temp
directory (scrubbed PATH/HOME/TMPDIR, no real node_modules reachable),
resolving only the handful of packages it explicitly aliases to this
package's own resolved paths (vitest, semver, bcryptjs, zod, plus the
cli-core/security source trees). The capability-brain / mcp-tools
registry analyzeCommand transitively loads now reaches rure-tools.ts,
so the same treatment is needed for its two real dependencies:
typescript (compiler API, src/rure/ts-walk.ts) and @electron/asar
(src/rure/extract-asar.ts) — both aliased to requireHere.resolve(...),
matching the existing zod/semver/bcryptjs pattern exactly. This is the
sanctioned mechanism this test already uses for a dependency it
genuinely needs to execute; it's unrelated to the heavy-package
absence assertions earlier in the same file (ruvector, agentic-flow,
@huggingface/transformers, etc.), which stay untouched.

20/20 analyze-json-output tests pass; full rure + guidance-brain suite
(56 tests) passes alongside it in the same run.

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix(ruflo-rure): memoize the aidefence module resolution, make gateAll sequential

CI's "Test V3 Packages" job reproducibly failed exactly one test —
gateAll's "preserves order and length across mixed benign/flagged
input" — twice, while every other mock-dependent test in the same file
passed both times. gateAll was the only caller that resolves
@claude-flow/aidefence via several concurrent gateWithAIDefence calls
(Promise.all over the input array); every other test calls it once.
In CI, @claude-flow/aidefence is genuinely absent from node_modules
(confirmed by this job's own "@claude-flow/aidefence not loadable"
log line elsewhere in the same run) — unlike this dev environment,
where it's pnpm-hoisted and physically present, just mocked over.

Fix: memoize the dynamic import behind a single shared promise, so
concurrent callers all await the same resolution attempt instead of
each firing their own against a module vi.mock has to intercept
without a real file backing it. Also made gateAll sequential as
defense in depth — gating is cheap, and there's no reason multiple
calls need to race the first-ever resolution attempt at all.

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix(ruflo-rure): add @electron/asar to the root package.json for CI's Test Suite job

Root CI's "Test Suite" job never installs v3/'s own pnpm workspace —
it runs `npm ci` at repo root only, then runs vitest from repo root
against v3/@claude-flow/cli/__tests__/*. Module resolution for those
test files walks up from v3/@claude-flow/cli (no node_modules there in
this job) through v3/ (also none) to the repo root's node_modules,
which is why `typescript` already resolved (already a root
devDependency, for an unrelated reason) while `@electron/asar` did
not (never referenced anywhere in the root tree before this).

Added @electron/asar to the root dependencies, matching how zod/ws
are already placed there for the same reason, and regenerated
package-lock.json. The regen also synced two pieces of real,
pre-existing lockfile staleness this surfaced: the recorded root
version (3.58.0, several releases behind the actual 3.59.17) and a
leftover `toml` dependency entry (removed from package.json before
this PR, but never swept from the lockfile until this install).

Verified against CI's actual invocation shape: `node
node_modules/vitest/vitest.mjs run v3/@claude-flow/cli/__tests__/rure-*`
from repo root, not just the v3-workspace-scoped `npx vitest run` I'd
been using to verify until now.

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix(ruflo-rure): lazy-load the TypeScript compiler instead of a top-level import

ts-walk.ts statically imported `typescript` at module scope. Since
rure-tools.ts is registered into mcp-client.ts's shared tool list, every
CLI invocation loaded the full TypeScript compiler into memory at
startup, not just commands that use ruRE — the same failure shape as the
#2561 cold-start regression (@claude-flow/aidefence).

This also explains three straight CI failures on the exact same file
(mods-cli-bin.test.ts, which exec's the built CLI's `init` through
execFileSync): the heavier startup made it the most likely victim of the
repo's known tinypool worker-crash flake under CI's resource limits.

Fix: import type-only `typescript` for AST types, and lazy-load the real
module (memoized) inside analyzeModuleSource, matching the pattern
already used by aidefence-gate.ts and extract-asar.ts. This makes
analyzeModuleSource/analyzeJsApp/findFeature async; updated their two
call sites in rure-tools.ts and the benchmark script, plus the test
files's call sites.

Verified: `node bin/cli.js --version` succeeds with node_modules/typescript
physically removed (previously would have crashed), confirming the
compiler is no longer loaded on every invocation.

Co-Authored-By: RuFlo <ruv@ruv.net>

---------

Co-authored-by: r <r@x>
2026-10-22 17:45:55 -04:00
nk-batman21 ac9f8b879e fix(update): honor --dry-run in update all (#4398)
The parser stores `--dry-run` as `dryRun`, but `update all` read
`flags['dry-run']`, which is always undefined. executeMultipleUpdates
then ran with dryRun = false, so a dry run ran `npm install
<pkg>@<latest> --save-exact` for every package and recorded it in the
update history. Read `dryRun` (keeping the kebab key as a fallback, as
daemon, init and mods do) for the executor call and the summary.
2026-10-22 17:07:05 -04:00
rUvandr c8b2f66290 docs(federation): document the existing custom-gateway override (#4399)
ruflo's federation layer already supports pointing at a self-hosted
gateway/relay instead of x.ruv.io — RUFLO_X_GATEWAY_URL,
RUFLO_X_RELAY_WS and RUFLO_X_RELAY_HTTP are already read by
v3/@claude-flow/cli/src/mcp-tools/x-federation-{tools,join,channels}.ts
before falling back to the x.ruv.io/relay.ruv.io defaults. The gap was
discoverability, not capability: nothing told a user this override
exists, and the console's x.ruv.io board never mentioned it.

- plugins/ruflo-x-gateway/README.md: new "Self-hosting a custom
  federation gateway" section explaining both layers (client-side env
  vars for the CLI's own tool calls; server-side deploy vars for
  running your own gateway instance) and what deliberately stays
  fixed (the Claude plugin directory submission's .mcp.json, guarded
  by test/claude-directory.test.mjs — that one endpoint is reviewed
  and vetted by Anthropic, so it can't be templated).
- plugins/ruflo-console/hooks/xruv.ts: a one-line comment pointing at
  the README from the RELAY constant. The constant itself is
  unchanged — this file is documented "Pure: entries and parsers
  only, no $", so it can't read process.env to show a self-hoster's
  actual configured relay; the honest fix is pointing at the real
  override, not guessing at a value this file can't observe.
- plugins/ruflo-console/hooks/help-topics-more.ts: the "Use the
  x.ruv.io board" help topic now has a tip naming the three env vars
  and pointing at the README.

No behavior change — x.ruv.io/relay.ruv.io remain the defaults
everywhere they already were.

Co-authored-by: r <r@x>
2026-10-22 16:12:24 -04:00
randRuFlo 540af1ff21 docs(readme): add Follow @rUv on X badge
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-10-22 15:01:07 -04:00
rUv 3daca8f628 docs(adr): ADR-322D — accept the cross-repo anchor record (was stale PROPOSED-EXTENSION) (#3127)
While researching a cross-ecosystem architecture question, found that
witness-receipt-contract.md §9 and the ruflo-anchor-record-v1 schema
both still say "PROPOSED-EXTENSION... nothing above is decided" —
but the two consumers that placeholder was gating (ruvnet/rvm#35,
ruvnet/autogenous#10) already merged real implementations against
exactly this shape:

- ruvnet/rvm#37 (merged): verify and anchor ruflo ADR-322C receipts
  into the witness chain
- ruvnet/autogenous#13 (merged): export promotion evidence in the
  ruflo ADR-322C receipt shape
- ruvnet/ruflo#3066, the PIR tracking issue that scoped this work,
  is itself closed with a status comment confirming the schema
  (PR #3067) is canonical on main and both conformance audits cleared

No schema or behavior change — this is a documentation-accuracy fix.
ADR-322D formally promotes the record from PROPOSED-EXTENSION to
Accepted to match what's already shipped and consumed, and explicitly
scopes it against a different, still-unwired "RVM" relationship
(LatentMesh's own aspiration to have RVF/RVM gate its candidate
cognition) so the two aren't conflated in future reads.


Claude-Session: https://claude.ai/code/session_01G3Fkc9qcZ2AkwGPce83yTa
2026-10-22 13:11:47 -04:00
263fd2431d Add structural validation report for .claude/agents catalog (#3117)
Adds a JSON report from a structural validation pass over the 108 agent
definition files under .claude/agents/. All 108 files have valid
frontmatter (name + description) and non-empty system-prompt bodies.

Also surfaces 11 agent names that are defined twice under different
paths (e.g. code-analyzer, backend-dev, pr-manager, python-specialist,
typescript-specialist, tdd-london-swarm, production-validator,
goal-planner, sublinear-goal-planner, database-specialist,
project-coordinator) - mostly a legacy flat file plus a newer
categorized copy. Not a parse failure, but worth a look since it's
ambiguous which definition wins when agents are loaded by name.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: rUv <ruv@ruv.net>
2026-10-22 12:51:57 -04:00
rUvandr fac6d193ce fix(ruflo-ai-team): annotation-review.md missed team_board and run_complete (#4394)
annotation-review.md's readOnly/destructive/idempotent/openWorld classification
table only listed 12 of the 14 live MCP tools. team_board (added at 0.2.0) and
run_complete (added at 0.2.1) were never added to the table, even though the
doc claims "the server tests compare the live tool list with this
classification" — nothing actually enforced that until now.

Both tools' real annotations (read from src/server.mjs) fit the existing rows
exactly: team_board uses READ(...) like the other read-only tools, and
run_complete uses WRITE(..., true) like the other idempotent upserts.

scripts/smoke.mjs now asserts the doc's documented tool set matches the live
server tool set exactly, so this can't silently drift again. Verified by
reverting the doc fix alone and confirming smoke failed with the exact
"documents every live tool" check, then restoring it.

Co-authored-by: r <r@x>
2026-10-22 11:57:12 -04:00
rUv 96c6aa89b5 docs: add ChatGPT app submission reference (#3310) 2026-10-22 10:26:46 -04:00
rUvandr f5af26322b release: 3.59.17 (#4393)
fix(init): stop advertising a memory path the runtime ignores (#4392)

Co-authored-by: r <r@x>
v3.59.17
2026-10-22 09:20:12 -04:00
ordinary being 69f4daaec6 fix(init): stop advertising a memory path the runtime ignores (#4392)
ruflo init wrote memory.persistPath: .claude-flow/data into .claude-flow/config.yaml, but getMemoryRoot() only reads the JSON config candidates (claude-flow.config.json and .claude-flow/config.json) and otherwise uses .swarm. The YAML key was dead text: editing it moved nothing and could mislead users into thinking a new store location was active (#3193 part b).\n\nRemove the ignored key and point the YAML at the JSON key that actually controls the store. Add an init regression test that fails on the old YAML and asserts the generated file no longer advertises persistPath.
2026-10-22 07:51:20 -04:00
rUvandr d5c341f853 release: 3.59.16 (#4391)
Co-authored-by: r <r@x>
v3.59.16
2026-10-22 07:18:56 -04:00
ordinary being 6e213d5bb3 fix(memory): distinguish recent dead locks from live writers (#4389)
* fix(memory): distinguish recent dead locks from live writers

inspectAgentdbLock() labeled a dead holder as 'live' when the lock was younger than the 10s stale threshold. The same state then produced 'another process is writing this database' guidance, so callers could wait for a writer that did not exist or leave a recoverable lock in place indefinitely (#3979 item 5).\n\nAdd a distinct 'recent' LockState for dead-PID locks still inside the stale window, clamp clock-skewed ages to zero, and give withAgentdbLockRecovery a precise retry message. The guard still refuses to clear the lock until it is old enough; only the diagnosis changes.

* chore(memory): bump @claude-flow/memory to 3.0.9

The leaf-package-publish audit correctly requires a version bump whenever publishable @claude-flow/memory source changes. Bump the leaf and both CLI dependency pins, and update the pnpm importer specifier, matching the #4279/#4152 release pattern.
2026-10-22 06:44:31 -04:00
Dragan SpiridonovandClaude Opus 5.5 732e9f07c0 chore(marketplace): pin agentic-qe-fleet to v3.15.11 (#4390)
Move the external agentic-qe-fleet entry from v3.15.10 (e5814a2f) to
v3.15.11 (221d6de15c73bb385961e4e649d8fcc1f5dc2dea). The plugin folder
changed only its version and its MCP pin (agentic-qe@3.15.11). Updates
the README links, the external-plugins test fixture and ADR-484.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 06:09:40 -04:00
nk-batman21 11e35e39c1 fix(auth): honor --no-browser for auth login (#4388)
The parser stores `--no-browser` as `browser: false`, and the declared
`no-browser` option defaults `noBrowser` to `false`, so
`noBrowser ?? ['no-browser']` was always false and the login used the
loopback browser flow unless the host looked headless. Treat
`browser: false` as the opt-out too.
2026-10-22 05:41:56 -04:00
nk-batman21 9f35d160e6 fix(hooks): honor --no-color in hooks statusline (#4387)
The parser stores `--no-color` as `color: false`, and the declared
`no-color` option leaves `noColor` at its default of `false`. The
statusline action only read `no-color` and `noColor`, so
`hooks statusline --full --no-color` and `ruflo --no-color hooks
statusline` still printed ANSI escape codes. Also check
`color === false`, as index.ts and init.ts already do.
2026-10-22 05:41:53 -04:00
rUvandr 76efb01e5a release: @claude-flow/hooks 3.0.1 (#4386)
Standalone publish for #4385 (stop injecting retracted performance
claims in GuidanceProvider.generateSessionContext()) — @claude-flow/cli
does not depend on @claude-flow/hooks directly, but @claude-flow/guidance
and @claude-flow/plugins do (both via ^3.0.0, so no pin bump needed there).

Co-authored-by: r <r@x>
2026-10-22 05:10:04 -04:00
ordinary being 731661c8f6 fix(hooks): stop injecting retracted performance claims (#4385)
GuidanceProvider.generateSessionContext() still emitted the exact multipliers the May 2026 intelligence audit retracted: HNSW '150x-12,500x' and Flash Attention '2.49x-7.47x'. This string is the SessionStart additionalContext path, so every session received claims the repo's own docs had already corrected. The existing test even pinned '150x' (#4381 memory-scan finding).\n\nReplace them with honest status lines: HNSW points at the memory package's measured benchmarks instead of a universal multiplier, Flash Attention is labeled as pending an in-tree benchmark, and the memory reduction is labeled a target. Update the regression test to assert the retracted substrings never reappear.
2026-10-22 04:36:27 -04:00
rUvandr 450f653c90 release: 3.59.15 (#4383)
Co-authored-by: r <r@x>
v3.59.15
2026-10-22 03:58:44 -04:00
kevin 6250c92036 fix(cli): report every stored entry in terminal_history total, not just the returned page (#4380) 2026-10-22 03:03:01 -04:00
rUvandSID-6921 5d99b57e78 fix(daemon): read config_set's values envelope in config.json reader (#3192) (#4379)
* fix(daemon): read config_set's values envelope in config.json reader

Fixes #3192

config_set's default-scope handler writes {"values": {key: val}, ...},
but the daemon's readDaemonConfigFromFile() only ever checked
scopes.project or the file's flat root — never `values`, which is where
config_set actually puts default-scope writes. As a result,
config_set daemon.idleSecs 0 silently had no effect on the running
daemon, even though it "succeeded" and updated config.json.

Add raw?.values as a fallback between scopes.project and the flat-root
read, matching the fix the issue reporter verified against the pinned
upstream sha.

* fix(daemon): resolve each config key independently (scopes.project > values > root)

Addresses review feedback on #3239 (Copilot): picking one whole object
as "cfg" meant a key only present in the values envelope was still
missed whenever scopes.project existed at all, even for unrelated
keys, because cfg resolved to scopes.project and the per-key fallback
only ever dropped to raw (root), never to raw.values.

Resolve each daemon.* key independently through
scopes.project -> values -> root so a config.json with keys spread
across more than one shape (e.g. one project-scoped config_set call
and one default-scope call) still resolves correctly.

---------

Co-authored-by: SID-6921 <99672439+SID-6921@users.noreply.github.com>
2026-10-22 01:54:09 -04:00
rUvandAniruddha Adak c949f62703 fix(cli): report actual flag state when decrypting an encrypted store without a key (#4378)
Co-authored-by: Aniruddha Adak <aniruddhaadak80@users.noreply.github.com>
2026-10-22 01:49:21 -04:00