Commit Graph

2616 Commits

Author SHA1 Message Date
rUv fa13ee4ad6 feat(watermark): add browser/Deno ESM entry (@claude-flow/watermark 0.2.0) (#3041)
Adds a `@claude-flow/watermark/web` ESM entry (wasm-pack `--target web`) so the
package works in browsers, Deno, and bundlers — not just Node. Instantiate once
with `await init()` (auto-fetches the wasm in a browser; accepts bytes/URL/
Response), then the same ergonomic API (Watermarker, detect, detectSelfSync,
detectExact) as the Node build.

- package.json: conditional exports (`.` = Node CJS/ESM, `./web` = browser ESM,
  `./package.json` re-exported); web/ marked ESM via a nested package.json.
- build:wasm now builds both nodejs and web targets.
- Added test/smoke-web.mjs; `npm test` runs Node + web. Both verified, plus a
  fresh dual-entry tarball install (node z=64.7, web z=64.7).

Bumps to 0.2.0 (new capability, backward-compatible). No removal tooling.


Claude-Session: https://claude.ai/code/session_01VYDa3Hah5VJLS2ceEuTLKz
2026-08-15 19:18:24 -04:00
rUv 72fb129397 feat(watermark): @claude-flow/watermark npm package (WASM bindings) (#3040)
Widens the ruflo-watermark WASM surface (src/wasm.rs) to select schemes by
name ("tournament" | "tournament_nd" | "gumbel") and adds the indel-robust
self-sync and exact-Gamma short-text detectors to the bindings.

Adds v3/@claude-flow/watermark: a Node (CommonJS) npm package wrapping the
wasm-pack-generated bindings behind an ergonomic API (Watermarker, detect,
detectSelfSync, detectExact), with a clean .d.ts, a committed 37KB .wasm so
`npm install` needs no Rust toolchain, a regenerate script (build:wasm), a
README, and a smoke test. Verified: all 3 schemes generate+detect, wrong-key
and null streams rejected, and the specialized detectors fire — both in-repo
and from a fresh tarball install.

No watermark-removal tooling is exposed.


Claude-Session: https://claude.ai/code/session_01VYDa3Hah5VJLS2ceEuTLKz
2026-08-15 17:23:30 -04:00
ruv 693385eab0 test(ci): keep safety guard root-compatible 2026-08-15 11:23:26 -04:00
ruv 13cbd697db fix(release): enforce CI and idle assignment safety 2026-08-15 11:14:06 -04:00
ruv 21f7c0adb8 fix(release): align bundled runtime metadata 2026-08-14 19:21:33 -04:00
ruv 392e71912e fix(mcp): rate-limit authenticated HTTP routes 2026-08-14 19:04:54 -04:00
ruv 2ec82b0cd1 chore(release): prepare stable 3.38.10 train 2026-08-14 17:35:47 -04:00
ruv b9a5077292 test(cli): model atomic fs operations in MCP suite 2026-08-14 17:35:36 -04:00
ruv 3f285bfb26 fix(memory): disable unsafe native bridge on Windows (#3024) 2026-08-14 17:16:05 -04:00
dajiaohuang 5c676fe242 fix(doctor): report missing native sqlite bindings 2026-08-14 17:10:34 -04:00
dajiaohuang 6b8f0d0b5a fix(mcp): share state across HTTP transports 2026-08-14 17:10:34 -04:00
dajiaohuang 2dbb790a0b fix(mcp): restore HTTP protocol and CLI tools 2026-08-14 17:10:34 -04:00
dajiaohuang fa11ee8c24 fix(cli): honor helper refresh opt-out before locking 2026-08-14 17:10:34 -04:00
dajiaohuang 0c73bab4d0 fix(cli): serialize helper refreshes across processes 2026-08-14 17:10:34 -04:00
rUv 4dcff48348 fix: correct CLAUDECODE env detection and neural-tools source label (#3028)
doctor.ts's API-key health check tested process.env.CLAUDE_CODE, but
Claude Code actually sets CLAUDECODE (no underscore) — the check could
never detect a real Claude Code session via that variable. Add the
correct CLAUDECODE check alongside the existing fallbacks (#3023).

getNeuralStoreStats() in neural-tools.ts always reads/writes
.claude-flow/neural/models.json but self-reported its source as
patterns.json — a separate file owned by the ReasoningBank store in
memory/intelligence.ts. Corrected the label and removed the unused
PATTERNS_FILE constant that caused the confusion (#3025).

Fixes #3023, Fixes #3025
2026-08-14 11:39:43 -04:00
Dragan Spiridonov 3030924f22 fix(security): compare candidate and prefix in the same canonical form (#3011)
* fix(security): compare candidate and prefix in the same canonical form (#3010)

`validate()` resolved the candidate with `fs.realpath` but the allowed
prefixes were only `path.resolve()`d, so the two sides were compared in
different forms and nothing under a prefix reached through a symlink could
ever match. On macOS that is every temp directory — `os.tmpdir()` is
`/var/folders/...` and `/var` is a symlink to `/private/var` — so
`ruflo proxy install` rejected the binary it had just signature-verified.
Linux is unaffected because `/tmp` is real, which is why CI stayed green.

Keep both forms of every prefix and compare against the one matching how
the candidate was resolved. Canonicalizing the prefixes outright would have
broken `validateSync()` / `isWithinAllowed()`, which are documented as
lexical-only, in the mirror direction.

When the candidate's leaf does not exist, canonicalize its longest existing
ancestor instead of falling back to the lexical path — otherwise
`allowNonExistent` (the default) would regress under a symlinked prefix.
That also closes a hole this exposed: `<allowed>/escape/not-written-yet`,
where `escape` symlinks outside the prefix, was previously accepted because
`realpath` threw ENOENT on the missing leaf.

Escapes stay blocked, and the regression tests construct the symlink
explicitly rather than relying on the macOS quirk, so they hold on every
platform. 3 of the 6 fail without this change.

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

* fix(security): keep root prefixes usable and leave strict results untouched

Three defects found by an adversarial review of the previous commit.

`allowNonExistent: false` was still canonicalizing the candidate on ENOENT.
The call already fails, so the only effect was rewriting the `resolvedPath`
and `matchedPrefix` reported back — a silent shape change for any caller
reading those off a rejected result. Canonicalize only when non-existent
paths are actually allowed.

A root prefix (`/`, `C:\`, a UNC share root) already ends in a separator, so
anchoring the boundary as `prefix + path.sep` searched for `//` and rejected
every descendant. Pre-existing at all three comparison sites — this change
only widened how a prefix can end up being a root, since one can now
canonicalize to it. Extracted `isWithinPrefix`, which keeps the boundary
anchored so `/srv/app-secrets` is still outside `/srv/app`.

The guarded symlink tests returned early instead of skipping, so they
reported as passed on a host that cannot create links, and the flag was
never reset between cases. They now skip properly and reset per case.

Also documents that prefixes are canonicalized once, at construction: the
allowlist keeps denoting what it named when configured, rather than handing
whoever can rewrite a link the ability to redirect it later.

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

---------

Co-authored-by: RuFlo <ruv@ruv.net>
2026-08-13 17:02:17 -04:00
dependabot[bot] 761dfcaa6d chore(deps): bump agentic-flow in /v3/@claude-flow/browser (#2907)
Bumps [agentic-flow](https://github.com/ruvnet/agentic-flow) from 2.0.13 to 2.1.2.
- [Release notes](https://github.com/ruvnet/agentic-flow/releases)
- [Changelog](https://github.com/ruvnet/agentic-flow/blob/main/docs/CHANGELOG.md)
- [Commits](https://github.com/ruvnet/agentic-flow/commits)

---
updated-dependencies:
- dependency-name: agentic-flow
  dependency-version: 2.1.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-13 17:02:07 -04:00
ruv cc5d9c5fe7 chore(release): bump to 3.38.9 — path-validator symlinked-prefix fix 2026-08-13 08:41:11 -04:00
rUv c1e8e0c41f fix(security): realpath allowed prefixes so a symlinked prefix isn't rejected (#3010) (#3013)
PathValidator.validate() canonicalizes the candidate path through
fs.realpath (when resolveSymlinks is on, the default), but the constructor
only ever path.resolve()d the allowed prefixes — never realpath'd them. On
any platform where a prefix is itself reached through a symlink (e.g.
macOS os.tmpdir() under /var, itself a symlink to /private/var), the
realpath'd candidate can never match the non-realpath'd prefix, and every
path under that prefix — including the prefix's own contents — is
rejected as "outside allowed directories".

This broke `ruflo proxy install` on macOS: the download and Ed25519
signature verification both succeed, then the defense-in-depth path check
in proxy/install.ts fails on the extracted binary, which lives under a
workDir created via os.tmpdir().

Fix: realpath the prefixes at construction time too, with the same
ENOENT-tolerant fallback to path.resolve() that validate() already uses
for candidates (a prefix directory that doesn't exist yet degrades
gracefully instead of throwing).
2026-08-13 08:36:25 -04:00
ruv 5efd5937e5 chore(release): bump to 3.38.8 — provider/model config propagation fix (#2962)
Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-12 18:30:46 -04:00
rUv 1e24bb8878 fix(agent): propagate explicit provider/model config into agent execution (#2962) (#3007)
* fix(agent): propagate explicit provider/model config into agent execution (#2962)

`providers configure` and `agent spawn --provider/--model` persisted user
intent but the execution path (callAnthropicMessages, executeAgentTask,
determineAgentModel) only ever consulted env vars and a 5-alias model list,
silently discarding both. A local Ollama/OpenRouter setup with explicit
config would either fail closed or fall back to whatever provider the
env vars happened to select.

- determineAgentModel(): treat any non-alias config.model string as an
  explicit selection via the existing modelId fast-path, instead of
  falling through to task-based routing / agent-type defaults.
- agent_spawn: forward config.provider to the stored (and returned) agent
  record when it's an unambiguous explicit choice ('ollama'/'openrouter';
  'anthropic' is excluded since the CLI silently defaults to it when
  --provider isn't passed).
- callAnthropicMessages(): accept an optional provider param and consult
  the persisted `agents.providers` config for baseUrl/apiKey/model when
  env vars are absent. A self-hosted Ollama baseUrl no longer requires
  the undocumented OLLAMA_API_KEY='local' sentinel.
- executeAgentTask(): forward agent.provider into the first dispatch call.

Precedence: explicit per-agent flag > env vars > persisted config >
key-presence inference (unchanged, last resort).

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

* fix(ci): update #2042 smoke to match the widened OpenRouter branch shape

The #2042 regression smoke statically matched the literal token sequence
`useOpenRouter && openrouterKey`. #2962 widened that condition to
`if (useOpenRouter) { const apiKey = openrouterKey || persistedOpenRouter?.apiKey; ... }`
so a persisted `agents.providers` config can supply the key when no env
var is set — the smoke's ordering guarantee (OpenRouter branch reachable
before the Anthropic-key early-return) still holds, only the literal
pattern needed updating.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-12 18:28:25 -04:00
ruv 0d9a4f7e85 chore(release): bump to 3.38.7 — HNSW status honesty fix, cache-fingerprint follow-up 2026-08-12 17:48:11 -04:00
rUv 57c0fcba98 fix(memory): stop misreporting bridge brute-force search as HNSW-accelerated (#3006)
* fix(intelligence): stamp contentFingerprint in consolidate()'s graph write too

consolidate() (the session-end path) rebuilt graph-state.json unconditionally
per the #2920 fix in v3.38.6, but its write never included the
contentFingerprint field that init()'s cache-hit gate checks — only init()'s
own write set it. So every init() immediately following a consolidate saw
nodeCount match but contentFingerprint undefined !== a real hash, missed the
cache, and did a full rebuild even with zero content changes. Fail direction
was safe (extra rebuilds, never stale data) so it didn't block the v3.38.6
release, but it meant the 60s graph cache never actually fired across a
consolidate/restart boundary.

* fix(memory): stop misreporting bridge brute-force search as HNSW-accelerated (#2922)

getHNSWStatus() reported `available: true` whenever the AgentDB v3 bridge
was loaded, on the theory that "AgentDB v3 is HNSW-equivalent" — but the
bridge's actual default search path is a full-table SELECT + brute-force
cosine similarity loop over every embedding row. It never touches
@ruvector/core or an HNSW index, regardless of whether ruvector-core is
installed and resolvable. Same issue in bridgeGetHNSWStatus (unconditional
available: true) and in three misleadingly-named functions that do no HNSW
work at all: bridgeSearchHNSW, bridgeAddToHNSW, bridgeGetHNSWStatus.

- getHNSWStatus(): available=false + new algorithm field
  ('hnsw' | 'brute-force-cosine') when the bridge is the active path,
  instead of a blanket available=true that implied acceleration that
  was never in the request path.
- memory-bridge.ts: bridgeGetHNSWStatus -> bridgeGetVectorSearchStatus
  (adds algorithm: 'brute-force-cosine' to its return shape),
  bridgeSearchHNSW -> bridgeSearchBruteForceCosine,
  bridgeAddToHNSW -> bridgeAddEmbedding. No behavior change — call sites
  updated in memory-initializer.ts, no other source callers existed.
- memory-tools.ts: the `backend: 'sql.js + HNSW'` field on 7 MCP tool
  responses (memory_store, memory_retrieve, memory_search, etc.) was a
  hardcoded literal regardless of which path actually ran. Replaced with
  describeBackend(), which reports the real algorithm via the fixed
  getHNSWStatus() probe.

Actually wiring AgentDBBackend's HNSW into the bridge's default search
path (the issue's broader architectural question) is out of scope here —
this fixes the status/naming layer lying about what's already true, not
the search path itself.
2026-08-12 17:40:19 -04:00
rUv c0c3ab071a fix(intelligence): stamp contentFingerprint in consolidate()'s graph write too (#3003)
consolidate() (the session-end path) rebuilt graph-state.json unconditionally
per the #2920 fix in v3.38.6, but its write never included the
contentFingerprint field that init()'s cache-hit gate checks — only init()'s
own write set it. So every init() immediately following a consolidate saw
nodeCount match but contentFingerprint undefined !== a real hash, missed the
cache, and did a full rebuild even with zero content changes. Fail direction
was safe (extra rebuilds, never stale data) so it didn't block the v3.38.6
release, but it meant the 60s graph cache never actually fired across a
consolidate/restart boundary.
2026-08-12 17:23:59 -04:00
ruv c76bbdcb16 chore(release): bump to 3.38.6 — codex stdin fix, intelligence cache staleness fix, daemon config restore fix 2026-08-12 16:49:23 -04:00
rUv 35a6ad3697 fix(daemon): config.json resourceThresholds overrides were lost to stale daemon-state.json (#2935) (#3000)
initializeWorkerStates()'s restoration guard only checked
`originalConfig` (the constructor arg) before restoring
resourceThresholds from a saved daemon-state.json — a value explicitly
set via .claude-flow/config.json (e.g. minFreeMemoryPercent: 0, a
workaround for Darwin's os.freemem() undercounting reclaimable memory)
was not covered by that guard, so a stale daemon-state.json from
before the override existed kept winning on every restart. Same bug
class as #2661 (aiWorkersEnabled), which already documents the
"deliberately not restored from stale state" pattern this now follows
for resourceThresholds too — per-field, so an unrelated field with no
explicit source can still legitimately restore from saved state.

Also fixes a secondary diagnostic bug reported in the same issue:
readDaemonConfigFromFile() logged "Daemon config loaded from ..." via
this.log(), but runs from the constructor before this.config (and
therefore this.config.logDir) is assigned — the log() call threw
inside its own try/catch and the line never reached daemon.log,
making "is the config file even being read?" impossible to answer
from the log alone. The chosen path (or a YAML-parser-unavailable
warning) is now returned and logged once this.config exists.

Does not address this issue's primary Darwin os.freemem() memory-gate
bug — that needs vm_stat/memory_pressure-based measurement, a larger,
platform-specific change left for a follow-up.
2026-08-12 16:43:21 -04:00
rUv 9039e2bb33 fix(intelligence): same-ID content edits were cached/dropped instead of refreshed (#2920) (#2999)
* fix(intelligence): same-ID content edits were cached/dropped instead of refreshed (#2920)

Two related staleness bugs in the intelligence helper's graph/store
caching, both keyed on entry count instead of content:

- init()'s graph-cache-hit gate compared only graphState.nodeCount to
  the deduped store length. Hand-editing a MEMORY.md section's body
  while its heading (and therefore generated ID) stays the same
  changes neither the count nor the ID set, so the stale cache kept
  being served and ranked-context.json was never refreshed.

- consolidate()'s STORE_PATH persist gate
  (`newEntries > 0 || store.length < preDedupCount`) only fired on
  insight creation or a dedup-driven shrink. Any other in-memory
  content change — e.g. assigning an id to a previously id-less entry
  while building the graph — left entry count and the dedup outcome
  unchanged, so the update was silently never written back to disk.

Fix: add a `storeFingerprint()` helper that folds every entry's own
content fingerprint into one aggregate value, used to (1) gate the
init() cache hit alongside nodeCount, and (2) make consolidate()
always persist the store — trivial cost given GRAPH_PATH and
RANKED_PATH are already rewritten unconditionally on every call.

Regenerated and re-signed helpers.manifest.json (ADR-174) for the
changed intelligence.cjs content via scripts/sign-helpers.mjs.

* fix(intelligence): re-sign helpers manifest with the correct auto-memory-hook.mjs hash

The previous commit's manifest captured a transient, incorrect hash for
auto-memory-hook.mjs — a file this change never touches — because a
concurrent Claude Code session briefly overwrote it with stale content
at the exact moment sign-helpers.mjs ran (the known helper-drift race
documented in CLAUDE.md). Re-signed with a before/after git-status
check bracketing the signing step to confirm no file changed underneath
it this time.
2026-08-12 16:38:25 -04:00
rUv 370355015b fix(codex): close worker stdin so codex exec doesn't hang waiting for EOF (#2947) (#2997)
DualModeOrchestrator spawned both Claude and Codex workers with
stdio: ['pipe', 'pipe', 'pipe'] but never wrote to or closed proc.stdin.
Both platforms receive their prompt positionally/via flag, never over
stdin — claude -p ignores the open pipe, but codex exec blocks in
resolve_root_prompt waiting for stdin EOF that never arrives, hanging
every real Codex worker until the orchestrator's own multi-minute
timeout kills it.

Close stdin immediately after spawn for both platforms.

Regression test spawns a real child process standing in for codex exec
(waits for stdin EOF before exiting) and asserts the worker completes
well under a short configured timeout instead of failing via timeout.
2026-08-12 16:11:14 -04:00
ruv 506264a9d7 chore(release): bump to 3.38.5 — .brain skip, --repo passthrough, secret regex
Ships #2994 (#2911 — ruvnet-brain clones no longer poison ADR
indexing), #2995 (#2963 — github_issue_track passes --repo to gh),
#2996 (#2931 — hardcoded-secret regex catches shorter/embedded
Stripe/OpenAI-shaped keys).

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-12 15:46:13 -04:00
rUv 7715eb5ea9 fix(security): loosen Stripe/OpenAI secret regex boundaries (#2931) (#2996)
The 20-char floor after the sk-/sk_live_/sk_test_ prefix missed
shorter-but-real keys, and the quote-adjacency anchor required a
quote literally next to the prefix, missing the common
`Authorization: "Bearer sk_live_..."` shape where the quote sits
next to "Bearer", not the key. Lookaround boundaries match the
prefix as a standalone token wherever it appears, with a lower
(but still non-trivial) length floor.
2026-08-12 15:43:27 -04:00
rUv 2fd63cf3db fix(github-tools): pass --repo to gh in github_issue_track (#2963) (#2995)
owner/repo were validated but never passed to gh, so `gh` silently
resolved the target repository from the cwd's git remote instead of
the caller-specified one. A caller supplying owner/repo expecting the
issue to land there had no guarantee it did, even on the "real"
gh-backed success path.
2026-08-12 15:36:06 -04:00
ruv 7c738174c1 chore(release): bump to 3.38.4 — init flag fix + graph edge race fix
Ships #2989 (init --all-agents/--skip-claude/--only-claude/--cloud-mcp
were dead kebab-case flag reads) and #2991 (CLI hooks post-task
dropped the ADR-130 reinforced-by graph edge due to an unawaited
fire-and-forget write racing process.exit(0)).

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-12 14:45:16 -04:00
rUv d41af6e4d5 fix(hooks): await the ADR-130 reinforced-by graph edge write (#2961) (#2991)
`hooks post-task` (CLI) reliably dropped the "reinforced-by" graph
edge on every successful call. The write was a fire-and-forget async
IIFE the handler never awaited — invisible from the long-running MCP
server, but the CLI is a one-shot process that calls process.exit(0)
immediately after the command action resolves (#1552), killing the
still-pending write before its first tick, every time.
2026-08-12 14:41:18 -04:00
rUv 3d3f6e08cc fix(init): read the parser's actual camelCase flag keys (#2952) (#2989)
`--all-agents`, `--skip-claude`, `--only-claude`, and `--cloud-mcp` were
silent no-ops. Every long-flag write path in the parser normalizes
kebab-case to camelCase before storing (`--all-agents` -> `flags.allAgents`),
but `initClaudeAction` read the literal kebab-case keys, which the parser
never produces. Same bug class already fixed for `--no-global` (#2098A)
five lines above — never applied to these siblings.
2026-08-12 14:40:50 -04:00
ruv f14453c3b7 chore(release): bump to 3.38.3 — hooks intelligence --train fix
Ships #2988: `hooks intelligence --train` now actually distills
(#2940) instead of printing a canned success message with no state
change behind it.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-12 14:15:15 -04:00
rUv 1e2178f246 fix(hooks): make hooks intelligence --train actually train (#2940) (#2988)
`--train` declared a `forceTraining` input on the `hooks_intelligence`
MCP tool but never read it — the handler always returned the same
read-only status dashboard, and the CLI action's --train branch was a
cosmetic 500ms sleep followed by an unconditional "Training cycle
completed". `lastAdaptation` (what `--status` reports as "Last
Training") never moved, so it could only ever age no matter how many
times --train ran.

Now `hooks_intelligence` calls the existing `distillLearning()` when
`forceTraining` is set and reports what actually happened: patterns
distilled, ran with nothing new to distill, or MCP unavailable — so a
real training cycle and a no-op are no longer the same output.
2026-08-12 13:56:55 -04:00
ruv 8082fbc0f8 chore(release): bump to 3.38.2 — removed-agent restore + mcp http foreground fix
Publishes #2986 (fix(migrate): correct removed-agent install suggestion
and add `migrate fix --agents` restore, #2985) and #2987 (fix(mcp): mcp
start -t http exits after false-success table + doc drift, #2984). Pure
bug fixes, no breaking changes -> PATCH per repo semver policy.

@claude-flow/cli, claude-flow, ruflo published in lockstep at 3.38.2,
all three dist-tags (latest/alpha/v3alpha) verified against the live
registry, published tarball pulled and confirmed to contain both fixes.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-12 11:43:40 -04:00
rUv 87f4f8ab67 fix(mcp): mcp start -t http exits after false-success table + doc drift from #2985 (#2987)
* fix(migrate,mcp): unresolvable remediation command + false-success HTTP server exit

Two independently-reported, freshly-filed defects:

- #2985: `ruflo migrate status`'s ADR-128 removed-agent remediation
  (shipped today via #2973/ADR-382 Part B) printed a command that cannot
  succeed twice over: `ruflo plugins install <plugin>` requires -n/--name
  (not a positional) AND targets the npm-package plugin system, while the
  gap detector's own registry check (installed_plugins.json) is for the
  Claude Code marketplace system entirely. Fixed to print the correct
  `/plugin install <plugin>@ruflo` slash-command flow, with a one-time
  "run /plugin marketplace add ruvnet/ruflo first" hint. Also corrects
  three adjacent doc-drift items the reporter found while verifying:
  README/USERGUIDE's `claude mcp add ruflo` examples (should be the
  canonical `claude-flow` key per #2206 — the exact stray-key duplicate
  #2612's healing logic exists to clean up), README's blanket "MCP server
  registered: No" claim for the plugin path (ruflo-core ships its own
  .mcp.json and does register one), and a stale comment in executor.ts
  conflating the CLI-track's mcp__claude-flow__* tool refs with the
  marketplace plugin binding (mcp__plugin_<plugin>_<server>__*).

- #2984: `ruflo mcp start -t http` printed a full "Status: Running"
  success table — PID, health/RPC URLs — then exited 0 within seconds,
  never actually leaving the port bound. Root cause: bin/cli.js's
  "normal CLI mode" branch (which any explicit non-stdio --transport
  always takes) unconditionally exits once the dispatched command's
  action() promise resolves (#1552's "one-shot command" assumption).
  mcp.ts's `start` action returned immediately after printing the table,
  so the freshly-bound http.Server was torn down within milliseconds of
  the printed success claim. Fixed by blocking in the foreground after a
  successful start (SIGINT/SIGTERM-driven graceful shutdown), matching
  daemon.ts's established `--foreground` pattern — `daemonize` isn't
  currently wired to fork/detach anywhere in MCPServerManager, so every
  successful start via this path is a foreground server regardless of
  the -d flag.

Tests: migrate-agent-detection.test.ts updated for the corrected command
string; new mcp-http-foreground-2984.test.ts spawns the built CLI exactly
like the bug report and asserts the port actually accepts connections
after the success table prints (not just that it printed), then confirms
SIGTERM still produces a clean exit. Both verified via git-stash A/B:
fail against the pre-fix code, pass against the fix.

Full suite otherwise unaffected — same 9 pre-existing sandbox-environment
failures (ruvector/wasm bindings, embedder model downloads) reproduced
identically before this change.

Closes #2985. Closes #2984.

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

* revert: drop migrate-agent-detection changes, superseded by PR #2986

An external contributor (pacphi) independently opened #2986 fixing the
same #2985 with the identical installCommand correction plus a real
`migrate fix --agents` restore command — strictly more complete than
this branch's minimal fix. Dropping the overlapping files here to avoid
a duplicate/conflicting PR; reviewed #2986 and recommended it for merge.

Remaining on this branch: the #2984 mcp start -t http fix and the
doc-drift items #2986 explicitly scoped out (README/USERGUIDE mcp-add
key, executor.ts comment).

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-12 11:39:24 -04:00
Chris Phillipson dbd88254e3 fix(migrate): correct removed-agent install suggestion and add 'migrate fix --agents' restore (#2985) (#2986)
The suggestion printed by 'migrate status' (ADR-382 Part B) could not
succeed: 'ruflo plugins install <plugin>' fails on syntax (--name is
required) and targets the npm-package PluginManager, while the owning
plugins are Claude Code marketplace plugins (not npm packages). The
detection module's own header documents that split.

- point installCommand at the marketplace flow the registry check
  actually verifies: /plugin install <plugin>@ruflo
- add 'migrate fix --agents' (with --dry-run): restores missing agents
  from canonical plugin content — marketplace clone, then repo checkout,
  then GitHub raw pinned to the CLI's version tag with main fallback —
  rewriting the plugin-only mcp__plugin_ruflo-core_ruflo__ namespace to
  the canonical claude-flow server key (#2206) and recording provenance
  after the frontmatter. Never overwrites existing files.
- status now hints at the fix command when gaps are found
2026-08-12 11:33:21 -04:00
ruv 711ca3f6fe chore(release): bump to 3.38.1 — memory search truncation + persistence honesty fixes
Publishes the #2983 fixes (#2982/#2976 unordered LIMIT truncation in
memory search, #2968 sql.js persistence honesty). Pure bug fixes, no
API/behavior additions -> PATCH per repo semver policy.

Also corrects package-lock.json's top-level version field, which had
drifted to a stale 3.35.0 independent of this bump.

@claude-flow/cli, claude-flow, ruflo published in lockstep at 3.38.1,
all three dist-tags (latest/alpha/v3alpha) verified against the live
registry.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-12 10:08:34 -04:00
rUv b8c37da8e9 fix(memory): unordered LIMIT truncation in search + honest sql.js persistence status (#2983)
Two independently-reported, verified defects in the memory bridge:

- #2982/#2976 (duplicate reports): bridgeSearchEntries() and
  bridgeSearchHNSW() pre-ranked their corpus with `LIMIT 1000`/`LIMIT
  10000` and no ORDER BY, so SQLite returned rows in arbitrary storage
  order (insertion order on a fresh table) before BM25/embedding scoring
  ever ran. On any corpus over the limit, newest entries were silently
  excluded from scoring regardless of relevance. bridgeListEntries()
  already ordered by updated_at DESC for the same reason; both search
  paths now match it. Verified via a stash A/B: identical repro returns
  zero results without the fix, recalls the target with it.

- #2968: bridgeStoreEntry() swallowed every wal_checkpoint(PASSIVE)
  failure as "non-fatal" and unconditionally reported store success. On
  the sql.js fallback driver (engaged when better-sqlite3's native
  binding never got built — e.g. a skipped optionalDependency
  postinstall), that PRAGMA throws "Invalid PRAGMA command" every time,
  and writes may never reach disk. The specific failure signature is now
  distinguished from ordinary busy/non-WAL pragma failures and surfaced
  as `persistWarning`, which `memory store` prints instead of an
  unconditional green "Data stored successfully".

Both fixes stay within the CLI's own bridge layer — no changes to the
external agentdb/better-sqlite3 packages.

Tests: 2 new regression files (3 tests), full CLI suite otherwise
unaffected (10 pre-existing failures reproduced identically against a
clean baseline with these changes stashed — all in ruvector/wasm/
embedder-model-download paths, unrelated to this change).

Closes #2982, #2976. Part of #2968 (root-cause guarded; upstream
agentdb sql.js persistence itself is unchanged).
2026-08-12 10:02:31 -04:00
ruv 6b01dc5a68 chore(release): bump to 3.38.0 — ADR-382 init scaffold drift remediation
Parts A/B/C: pinned ruflo-core's marketplace-plugin MCP launch path
(local-bin-first resolver, mirroring hook-handler.cjs), registered 3
plugins missing from marketplace.json, implemented ADR-128's promised
`ruflo migrate` removed-agent detection, and shipped a warn-only CI
guard (scripts/smoke-init-scaffold-references.mjs) plus a deterministic
remap of 701 dead `npx claude-flow` references across 142 scaffold files.

Ref: #2971, ADR-382
2026-08-11 18:41:34 -04:00
rUv 83e536396f fix(scaffold): add dead-reference guard + deterministic CLI remap (ADR-382 Part C) (#2974)
Adds scripts/smoke-init-scaffold-references.mjs (ADR-382 Part C, #2971):
four static assertions over v3/@claude-flow/cli/.claude/** and the
plugin/marketplace surface, deriving the live MCP tool set and canonical
CLI form from source rather than hand-maintaining them.

  1. dead `npx claude-flow` (bare) invocation
  2. dead `mcp__claude-flow__<tool>` references not in the live registry
  3. plugin .mcp.json launches with no local-bin-first resolver (Part A regression guard)
  4. plugins/* directories missing from .claude-plugin/marketplace.json

Ships warn-only (no --strict) with a documented --strict flag to flip once
backlogs clear. Wired into .github/workflows/v3-ci.yml as a new job,
gated on plugins/*/.mcp.json, .claude-plugin/marketplace.json, and the
script itself (v3/@claude-flow/cli/.claude/** was already a trigger path).

Deterministic remap applied: 701 occurrences of bare `npx claude-flow`
across 142 files -> `npx @claude-flow/cli@latest` (mechanical, 1:1, verified
by rerunning the guard: check 1 701 -> 0, check 2 unchanged at 410). The
386 legacy `npx claude-flow@alpha` / `@v3alpha` occurrences elsewhere in
the same tree were left untouched (still-maintained dist-tags).

NOT remapped in this PR (tracked follow-up, per ADR-382 Part C's own
guidance not to guess): 410 dead `mcp__claude-flow__<tool>` occurrences
across 98 files, 54 distinct dead tool names (memory_usage: 53 files,
task_orchestrate: 41, sparc_mode: 34, swarm_monitor: 16, plus 50 more at
lower counts). Each requires per-call-site judgment about the surrounding
example's intent (store vs retrieve vs list, 1-line vs restructured
2-line orchestration calls) that a bulk regex pass cannot make safely.

Checks 3-4 are expected-red until ADR-382 Part A merges (plugin/ruflo-core
.mcp.json resolver + the 3 missing marketplace entries:
ruflo-agntcy, ruflo-bbs-federation, ruflo-business-pods).
2026-08-11 18:39:17 -04:00
rUv 2c8f863583 fix(migrate): implement ADR-128's promised removed-agent detection (ADR-382 Part B) (#2973)
ADR-128 Phase 2 deleted 9 forked agent files from the init template
(coder.md, researcher.md, reviewer.md, tester.md, memory-specialist.md,
security-auditor.md, sparc-orchestrator.md, goal-planner.md,
adr-architect.md), making each plugin canonical. Its Consequences
section promised "`ruflo migrate` should detect removed agents and
print install suggestions" — never implemented until now.

Adds detectRemovedAgentGaps() (new sibling module
migrate-agent-detection.ts, kept out of the already-783-line migrate.ts
per the file-size budget) which, for each of the 9 agents, checks
whether the basename is missing from the project's .claude/agents/
tree and whether the owning plugin is installed per Claude Code's
global ~/.claude/plugins/installed_plugins.json registry (the only
real on-disk signal for marketplace-plugin install state; ruflo's own
PluginManager manifest tracks a separate npm-package-based system).
Pure filesystem checks, no CLI version comparison, per ADR-382 Part B
scope.

`ruflo migrate status` now prints a second table naming any such gap,
its owning plugin, and the exact `ruflo plugins install <plugin>`
command to restore it (JSON output via ctx.flags.format === 'json'
gains a removedAgentGaps field, matching this file's existing
patterns).

See v3/docs/adr/ADR-382-init-scaffold-content-drift-remediation.md
(PR #2972) and https://github.com/ruvnet/ruflo/issues/2971.
2026-08-11 18:26:21 -04:00
ruv 6ce18b5a7f chore(release): bump to 3.37.0 — proxy fixes + new config flags
Minor bump (not patch) because #2927/#2928 add backward-compatible CLI
flags (--routing-mode, --passthrough), not just bug fixes:

- fix(proxy): bump pinned meta-proxy release 0.4.0 -> 0.7.3 (#2925)
- fix(proxy): fall back to tar when Expand-Archive fails on Windows (#2926)
- fix(proxy): disclose what enabling cloud routing decides, --routing-mode (#2927)
- fix(proxy): make the passthrough plane reachable again after cloud routing (#2928)

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-11 13:05:13 -04:00
Dragan Spiridonov 6d58837f4f fix(proxy): make the subscription plane reachable again after cloud routing (#2928)
* fix(proxy): disclose what enabling cloud routing actually decides, and let users pin a tier

Closes the terminal half of cognitum-one/meta-proxy#43 M5b, and adds the
`routing_mode` control ruflo never had.

meta-proxy ADR-321 rev-2 (shipped v0.6.0, #43 M2) applies tier selection to
ALL Cloud traffic. Before it, a deliberately configured Cloud plane forwarded
the client's own model name -- and since Claude Code names a frontier model by
default, every request was served at the top tier with no ceiling and no proxy
daily cap. Difficulty-routing all Cloud traffic is the fix for that defect, not
a regression, and it deliberately does not touch Passthrough or Local: those
are the user's own subscription and their own backend.

But ruflo never told anyone. `ruflo proxy config --cloud` said only that
prompts go to api.cognitum.one "and forwarded to the selected provider" -- so a
user could enable cloud routing still believing their model choice held. #43
M5a fixed the Developer Console selector; M5b ("equivalent disclosure to any
terminal flow that explicitly activates Cloud") is this command, and it was
still open.

The disclosure now answers the three things activating Cloud actually decides,
each of which has a different answer than the plane being left:

  - who processes the prompt (Cognitum, server-side)
  - who pays (the Cognitum account -- NOT the user's own Claude subscription,
    which is what Passthrough uses and is unaffected)
  - which model runs (the plane picks a tier per prompt rather than honoring
    the requested model)

`--routing-mode <auto|low|mid|high>` is rev-2's escape hatch. The console got a
control for it in meta-proxy#52; ruflo had none, so a ruflo user was stuck on
auto with no way to disagree -- and the scorer reads prompt SHAPE, not task
difficulty, so a short-but-hard prompt can under-escalate.

Setting a tier must never activate Cloud (ADR-321 Revision 3 keeps the plane
choice and this Cloud-only setting as separate controls), so `--routing-mode`
alone writes only `routing_mode`, asks for no consent it does not need, and
says the setting is inert until Cloud is on. `--routing-mode` with
`--local-only` is refused rather than silently resolved, and an unconfirmed
`--cloud` still writes nothing at all.

Wire values confirmed against meta-proxy's `RoutingMode` enum (`src/config.rs`,
same `#[serde(rename_all = "snake_case")]` treatment as `DataPlane`), and
`#[serde(default)]` there means an existing config that omits the field keeps
working and reads as auto. An unrecognized value already in the file reports as
auto rather than being echoed back, matching the proxy's own
degrade-to-safe-default behavior.

ADR-304 records both, and drops its inline copy of the disclosure text -- that
copy had already fallen behind the shipped string, and a reader could not tell
which was real. #43 M3 (a Cloud tier ceiling and daily cap) stays open upstream
as a product decision, so the disclosure makes no claim about caps either way.

Test: 9 proxy test files, 68 tests, all passing (was 56).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(proxy): make the subscription plane reachable again after cloud routing

`--local-only` was the only exit from the cloud plane, and it writes
`default_data_plane = "local"` -- the user's own Ollama/vLLM/SGLang backend.
`passthrough`, which is meta-proxy's own default AND the plane that uses the
user's own Claude subscription, was unreachable from this command by design
("passthrough is never written", ADR-304's 07-16 addendum).

So the undo we advertised was wrong. A user on passthrough who followed our own
disclosure -- "Disable anytime: ruflo proxy config --local-only" -- landed on a
THIRD state they never chose, pointed at a local backend that may not be
installed. Two consequences, neither visible:

  1. Their Claude subscription stops being used at all. That is the same harm
     cognitum-one/meta-proxy#51 closed on the login path ("silently switches a
     user away from Passthrough -- their own Claude subscription"), reached
     through a different door.

  2. Automatic quota failover silently stops applying. meta-proxy gates it on
     the plane (`src/routing.rs`: `automatic_eligible =
     cfg.default_data_plane == DataPlane::Passthrough`) because Passthrough is
     the only plane that sees Anthropic's own rate-limit headers (ADR-320). A
     user parked on `local` is opted out of ADR-321 entirely and is never told.

The no-flag report papered over the difference, printing one line -- "Cloud
routing is OFF -- requests never leave this machine (or use your own Claude
subscription on Passthrough)" -- for two planes that do not behave alike.

`--local-only` keeps its meaning exactly; the flag name and ADR-304 both
promise a local backend, and silently retargeting it would just be a second
surprise. Instead:

  - `--passthrough` is added, writing `default_data_plane = "passthrough"` and
    revoking cloud-routing consent the same way `--local-only` does.
  - The disclosure names both exits instead of presenting one as THE undo.
  - `--cloud` reads the plane it is leaving before overwriting it and prints
    the command to restore it. That read is the only moment ruflo knows where
    the user was, and it beats persisting a "previous plane" that could go
    stale against a hand-edited TOML.
  - `--local-only` now says the Claude subscription is not used on that plane,
    and how to choose it instead.
  - The no-flag report describes each plane distinctly and calls out the
    ADR-321 failover consequence while on `local`.
  - Plane flags are mutually exclusive -- more than one is refused rather than
    resolved by precedence, since any precedence would discard something the
    user explicitly asked for.

No default changes: a user who never runs this command is on passthrough,
exactly as before. ADR-304 records the decision and supersedes its own
"passthrough is never written" claim.

Test: 9 proxy test files, 80 tests, all passing (was 68).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(proxy): correct disclosure's who-pays claim (post-merge reapply)

Same fix as on #2927 — the disclosure asserted Passthrough "is the plane
in effect until you turn this on", which is false whenever the user is
already on --local-only. Reapplying after merging main (which already
had this fix on the pre-passthrough disclosure text) since this branch's
own commit still carried the original wording.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: ruv <ruv@ruv.net>
2026-08-11 13:03:23 -04:00
Dragan Spiridonov e1f7e4a232 fix(proxy): disclose what enabling cloud routing decides, and let users pin a tier (#2927)
* fix(proxy): disclose what enabling cloud routing actually decides, and let users pin a tier

Closes the terminal half of cognitum-one/meta-proxy#43 M5b, and adds the
`routing_mode` control ruflo never had.

meta-proxy ADR-321 rev-2 (shipped v0.6.0, #43 M2) applies tier selection to
ALL Cloud traffic. Before it, a deliberately configured Cloud plane forwarded
the client's own model name -- and since Claude Code names a frontier model by
default, every request was served at the top tier with no ceiling and no proxy
daily cap. Difficulty-routing all Cloud traffic is the fix for that defect, not
a regression, and it deliberately does not touch Passthrough or Local: those
are the user's own subscription and their own backend.

But ruflo never told anyone. `ruflo proxy config --cloud` said only that
prompts go to api.cognitum.one "and forwarded to the selected provider" -- so a
user could enable cloud routing still believing their model choice held. #43
M5a fixed the Developer Console selector; M5b ("equivalent disclosure to any
terminal flow that explicitly activates Cloud") is this command, and it was
still open.

The disclosure now answers the three things activating Cloud actually decides,
each of which has a different answer than the plane being left:

  - who processes the prompt (Cognitum, server-side)
  - who pays (the Cognitum account -- NOT the user's own Claude subscription,
    which is what Passthrough uses and is unaffected)
  - which model runs (the plane picks a tier per prompt rather than honoring
    the requested model)

`--routing-mode <auto|low|mid|high>` is rev-2's escape hatch. The console got a
control for it in meta-proxy#52; ruflo had none, so a ruflo user was stuck on
auto with no way to disagree -- and the scorer reads prompt SHAPE, not task
difficulty, so a short-but-hard prompt can under-escalate.

Setting a tier must never activate Cloud (ADR-321 Revision 3 keeps the plane
choice and this Cloud-only setting as separate controls), so `--routing-mode`
alone writes only `routing_mode`, asks for no consent it does not need, and
says the setting is inert until Cloud is on. `--routing-mode` with
`--local-only` is refused rather than silently resolved, and an unconfirmed
`--cloud` still writes nothing at all.

Wire values confirmed against meta-proxy's `RoutingMode` enum (`src/config.rs`,
same `#[serde(rename_all = "snake_case")]` treatment as `DataPlane`), and
`#[serde(default)]` there means an existing config that omits the field keeps
working and reads as auto. An unrecognized value already in the file reports as
auto rather than being echoed back, matching the proxy's own
degrade-to-safe-default behavior.

ADR-304 records both, and drops its inline copy of the disclosure text -- that
copy had already fallen behind the shipped string, and a reader could not tell
which was real. #43 M3 (a Cloud tier ceiling and daily cap) stays open upstream
as a product decision, so the disclosure makes no claim about caps either way.

Test: 9 proxy test files, 68 tests, all passing (was 56).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(proxy): correct disclosure's who-pays claim for --local-only users

The disclosure text asserted "[Passthrough] is the plane in effect until
you turn this on" — false whenever default_data_plane is already "local"
(i.e. exactly a --local-only user considering re-enabling cloud routing).
Reword to state Passthrough is the proxy's default and what the user's
Claude subscription is used by, without claiming it's necessarily the
plane currently in effect.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: ruv <ruv@ruv.net>
2026-08-11 12:48:26 -04:00
Dragan Spiridonov d3e647a5dd fix(proxy): bump pinned meta-proxy release 0.4.0 -> 0.7.3 (#2925)
* fix(proxy): bump pinned meta-proxy release 0.4.0 -> 0.7.3

`DEFAULT_PROXY_RELEASE` had not moved in six meta-proxy releases, so
`ruflo proxy install` shipped a binary predating every fix since v0.4.0:

  - require explicit Cognitum Cloud routing consent (#60)  first in v0.7.0
  - handle --help before binding (#74/#76)                 first in v0.7.2
  - --help/--version win from any argv position (#85)       first in v0.7.3
  - reload live daemon config on login/logout (#28)         first in v0.4.1
  - pin the Linux glibc floor to 2.28 (#66)                 first in v0.7.1

The consent one matters most: the install disclosure promises cloud
routing is "never enabled by install alone", but the commit that
separated authentication from routing consent landed in v0.7.0 — after
the pinned build.

The --version regression is directly observable on 0.4.0: the binary
starts the daemon, writes the token file and binds 127.0.0.1:11435,
where meta-proxy's own source documents --version as side-effect-free
("no server, no I/O"). Any probe silently leaves a listener running.

Also stop hardcoding the version in proxy.ts's example text. That string
was written as a literal rather than interpolating the constant, so the
advertised version could — and did — drift from the installed one. Both
tests now read the pin from the module for the same reason; asserting a
literal means every bump needs hand-edited test expectations.

Verified on Windows x64: `ruflo proxy install --release 0.7.3 --yes`
installs with signature + checksum verification intact, and `--version`
then prints and exits with no process left running and no port bound.

Test: 9 proxy test files, 56 tests, all passing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(proxy): surface pin drift to already-installed users

Bumping DEFAULT_PROXY_RELEASE only changed what a NEW install receives.
Someone who had already run `proxy install` on 0.4.0 got no signal that
anything moved: `proxy status` reported installation as a boolean, and
`proxy update` required an explicit --release, so reaching a newer binary
meant already knowing its version number. The fixes that motivated the bump
therefore reached only the users who did not yet have the problem.

ProxyStatus now carries `version`, read from the install manifest install.ts
already writes. `proxy status` prints it, the console guidance names the gap
and the command that closes it, and `proxy update` defaults to the pin the
same way `install` does. Updating stays explicit and user-initiated.

The version is read from the manifest, never by executing the binary:
releases before v0.7.2 start the server and bind a port on ANY invocation,
so a `--version` probe against an old build leaves a listener running --
precisely the state this check exists to detect. A missing or malformed
manifest reports "unknown", never "up to date".

Also corrects two stale claims this change makes load-bearing:

  - proxy/lifecycle.ts's header asserted as fact that the binary "takes no
    CLI flags -- no --version/--help, any invocation just starts the server"
    (confirmed 2026-07-16). v0.7.2 handles --help before binding and v0.7.3
    makes --help/--version win from any argv position. Left as-is, the next
    reader concludes no version handshake is possible.

  - ADR-307 still described the pin as v0.4.0 -- a fourth copy of the literal
    this PR is de-duplicating, and the one a reviewer reads to decide what
    the pin should be. It now names no version at all and points at the
    constant, plus records the drift-surfacing behavior.

Test: 9 proxy test files, 64 tests, all passing (was 56).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 12:22:55 -04:00
Dragan Spiridonov a39a1dccd0 fix(proxy): fall back to tar when Expand-Archive fails, and cover extraction (#2926)
`ruflo proxy install` aborted on Windows after the release had already been
downloaded and its Ed25519 signature + sha256 verified:

  [ERROR] Install failed
    Expand-Archive failed (exit 1): Expand-Archive : The 'Expand-Archive'
    command was found in the module 'Microsoft.PowerShell.Archive', but the
    module could not be loaded.

`Microsoft.PowerShell.Archive` is a script module reached through PowerShell's
autoloading, so it can fail for environment reasons unrelated to the archive.
The same command succeeded on the same machine minutes later, so this is
intermittent rather than a hard platform break — which makes it worse, not
better: a verified archive was discarded and the install left with nothing.

Windows is the only platform exposed to this. releaseArchiveExtension() sends
`*-windows-msvc` to .zip and every other target to .tar.gz, and the .tar.gz
branch already shells out to tar.

Expand-Archive stays the PRIMARY zip path — bsdtar's zip support is still not
something to lean on by default, per the existing comment. It now falls back to
`tar -xf` (bsdtar ships on Windows 10+) instead of giving up, and only a failure
of BOTH extractors is fatal, reporting each cause. Extraction is split into two
helpers so each path is independently exercisable.

Adds __tests__/proxy-extract-archive.test.ts (10 tests). Extraction was the only
platform-divergent step in the install pipeline and had no direct coverage:
nothing in __tests__ referenced extractArchive, ExtractionError, or
Expand-Archive. The executor is mocked, so routing is asserted on every platform
without needing a real powershell.exe or bsdtar.

`extractArchive` is exported for those tests.

Verified the tests pin the fallback rather than merely the new export: with the
fallback disabled but the export kept, exactly the 5 fallback-specific tests
fail and the other 5 still pass.

Test: 10 proxy test files, 66 tests, all passing. Changed files typecheck clean.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 12:22:31 -04:00
ruv efd0406e7a chore: sync committed versions to already-published 3.36.0 / memory@alpha.22
@claude-flow/cli, claude-flow, and ruflo were published at 3.36.0 (and
@claude-flow/memory at 3.0.0-alpha.22, carrying the #2887 durability fix)
without the version bump landing back in git — this just reconciles the
committed package.json versions with what's actually live on npm, and bumps
the cli's @claude-flow/memory floor to the version that has the fix.

No code changes.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-08-11 08:56:05 -04:00