Commit Graph

7 Commits

Author SHA1 Message Date
rUv 401e02d511 fix: complete reports and consistent initialization for v3.32.37 (#2851)
* fix(metaharness): preserve readiness verdict payloads

* test(metaharness): cover blocked genome verdicts

* fix(adr): parse bullet metadata and relationships (#2659)

* fix(adr): align adr-create with AgentDB schema (#2651)

* fix(adr): make index updates idempotent (#2660)

* fix(memory): bound session-end graph consolidation (#2628)

* fix(memory): align active row visibility (#2652)

* fix(memory): honor database path during init

* fix(hooks): keep all shim fallback tags aligned

* fix(codex): omit unbacked full-template skills

* fix(init): generate complete native dual projects

* test(memory): isolate path and legacy-row regressions

* chore(release): prepare v3.32.37
2026-07-29 15:40:17 -04:00
rUv 810b13dcd6 fix: tracker-sweep 2026-07-26 (v3.32.10) — 9 bugs + promo seed + follow-ups (#2788)
* fix: tracker sweep 2026-07-26 — 9 bug fixes + promo seed

Closes: #2770 #2774 #2776 #2777 #2781 #2782 #2785 #2786 (Codex follow-up)
Refs:   #2775 (memory upsert semantics)

- Statusline promo row blank on new installs — restored cold-start local
  seed pool that ADR-311 had emptied. Remote Cognitum-served pool
  (funnel.ruv.io/v1/messages) remains authoritative via
  eligibleMessagesFromPools (remote wins by id); seed only fills the
  ≤ 60s cold-start window before first successful remote fetch.
- #2777 — ruflo init no longer imports the entire ruvnet/ruflo repo
  (97 MB, 384 SKILL.md) into .agents/skills/ruflo/. Materializes single
  platform SKILL.md; detects "bloated" prior install and re-materializes.
- #2781 — ruflo-adr adr-index no longer silently drops ADR data: status
  regex accepts "- **Status**: proposed"; single-line and wrapped
  relation lines parse fully; CLI_CORE=1 warns and unifies namespace.
- #2770 — Windows: browser-session MCP tools + two init execFileSync npx
  sites now set shell: process.platform === 'win32' so cmd.exe resolves
  npx.cmd. POSIX behavior unchanged.
- #2782 — WorkerDaemon.saveState + autopilot-state.saveState + appendLog
  no longer race on shared .tmp filename; all three call writeFileAtomic
  (pid + timestamp + random-suffixed temp).
- #2785 — ruflo hooks post-task accepts --task/-t and --store-results
  flags matching CLAUDE.md-documented usage; routing outcomes finally
  persist to the namespace hooks_metrics reads.
- #2786 — AgentDB no longer silently fails to initialize under
  CLAUDE_FLOW_ENCRYPT_AT_REST=1. New getAgentDbPath() returns
  agentdb-memory.db in the same directory as memory.db, so the
  ControllerRegistry native better-sqlite3 opens a distinct file from
  the sql.js CRUD writer's encrypted memory.db.
- #2776 — Statusline security STALE/IN_PROGRESS branches reachable via
  local overlay recomputing freshness on every render from
  .claude/security-scans/scan-*.json. Env: RUFLO_SCAN_STALE_HOURS (24),
  RUFLO_SCAN_PENDING_CAP_MIN (30). STALE renders dim gray.
- #2774 — Codex MCP generator registers dedicated stdio server binary
  claude-flow-mcp instead of the management CLI ruflo mcp start that
  never answers initialize. All 7 wrong-command sites fixed
  (initializer.ts + 6 template sites in generators/config-toml.ts + 2
  in migrations/index.ts + generators.test.ts:653 assertion).
- #2775 — Memory store to existing key no longer dead-ends:
  bridgeStoreEntry uses INSERT ... ON CONFLICT with tombstone
  auto-resurrect; UNIQUE returns typed error instead of null (no more
  misleading demotion into #2735 guard); bridgeDeleteEntry runs
  wal_checkpoint(PASSIVE); CLI memory store --upsert default
  materialized locally (parser.applyDefaults bug).
- Stale ruflo-rebrand assertion in codex/tests/generators.test.ts:192
  updated Co-Authored-By: claude-flow → ruflo-bot.

- #2786 fix-2/3 (hooks_metrics reads dead .claude-flow/memory/store.json;
  bridgeRecordFeedback calls non-existent agentdb API) — architectural
  rewire, needs separate PR.
- #2775 defect #3 root cause (parser.ts applyDefaults doesn't apply
  subcommand-declared defaults) — flagged by agent, other subcommands
  affected too.
- #2775 memory-tools.ts MCP tool schema still defaults upsert:false
  (parity fix flagged).

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

* fix(parser): applyDefaults now walks subcommand + command options (#2775 follow-up)

Previously only `this.globalOptions` were walked. Any subcommand-declared
`default: <value>` silently dropped and the action handler received
`undefined`. The immediate victim was `memory store --upsert` (per #2775),
but the bug is generic — every subcommand that relies on a per-flag
default was affected.

Fix: applyDefaults now accepts optional command + subcommand, walks all
three layers narrow-to-broad (subcommand > command > global), and the
first layer to supply a value wins (because it only writes when the flag
is `undefined`, so earlier writers stick).

Parser tests: 52/52 pass. Build: tsc exit 0.

Refs: #2775 (root cause behind the CLI --upsert workaround already in
`commands/memory.ts`; the workaround is now redundant but harmless).

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

* fix(mcp): memory_store defaults upsert=true for CLI parity (#2775 follow-up)

The CLI `memory store` command defaults `--upsert=true` (issue #2594, fix
c36cb4d66) so `store → delete → store` on the same (namespace, key)
doesn't trip the `UNIQUE(namespace, key)` constraint against a
soft-deleted row. The `memory_store` MCP tool schema still defaulted
`upsert: false`, so every agent-facing store-to-existing-key hit the
strict-insert path unless the caller explicitly passed `upsert: true` —
different behavior than the CLI documented.

Fix: schema description now states default true, handler reads
`input.upsert !== false` (only explicit `upsert: false` opts out).
Symmetric with the CLI-side workaround in `commands/memory.ts`.

Refs: #2775 (MCP parity follow-up flagged by the sweep agent).

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

* docs(changelog): re-scope tracker sweep to 3.32.10 + reflect final commit set

- Rename header 3.28.1 → 3.32.10 (patch on the actual current 3.32.9;
  the original 3.28.1 heading was left over from the stale-base draft
  before the rebase onto origin/main).
- Remove the #2774 bullet from the "Fixed" list — that fix was reverted
  during rebase (upstream d20f1323b superseded it and the reporter's
  diagnosis appears incorrect). Move #2774 to a new "Investigated"
  subsection with the analysis and a note recommending closure.
- Fold the #2775 parser follow-up (parser.ts:applyDefaults walks
  subcommand + command options) and the MCP parity follow-up
  (memory_store default upsert=true) into the #2775 bullet, since
  they're the same defect surfacing at three layers.
- Drop the "stale rebrand test assertion" bullet — that assertion is
  now on origin/main already; no notable delta in this PR.

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

* fix(hooks): dual-write routing decisions to JSON store for metrics reader (#2786 fix-2)

Previously `hooks_post-task --store-results` wrote routing outcomes to
AgentDB namespace `patterns` via `storeFn`, but the sync reader
`getIntelligenceStatsFromMemory()` only reads
`.claude-flow/memory/store.json`. Result: the "Pattern Learning" and
"Agent Routing" numbers in `hooks_metrics` stayed at zero even when
routing decisions were being recorded.

Fix: after the AgentDB write, mirror the same entry into the JSON store
with `metadata.type = 'routing-decision'` so the reader's filter
picks it up. AgentDB stays authoritative for cross-session retrieval;
the JSON store is the counter surface the sync reader depends on. Both
writes are wrapped in try/catch so a failure at either layer doesn't
break `hooks_post-task`.

Same shape as the existing dual-write pattern for `hooks_post-command`
at line 910-931. Keeps `getIntelligenceStatsFromMemory` synchronous
(would require async ripple through 3 callers otherwise).

Refs: #2786 fix-2 (flagged as architectural by the sweep agent — turns
out to be one bounded additive write, not a rewire).

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

* test(parser): regression coverage for command + subcommand defaults (#2775)

Three new tests in the existing `defaults` describe block:
- command-level `option.default` applies to a flagless invocation
- subcommand-level `option.default` applies to a subcommand invocation
  (this is the exact shape that trapped `memory store --upsert`)
- explicit `--no-upsert` still overrides the default:true

Guards against a re-regression of parser.ts:applyDefaults collapsing
back to only walking `globalOptions`.

52/52 → 55/55 parser tests pass.

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

* test(hooks): regression guard for #2786 fix-2 JSON dual-write

New test file `hooks-post-task-routing-dual-write-2786.test.ts` runs
`hooksPostTask.handler` in a temp cwd and asserts that when called with
`storeDecisions=true`:

  - `.claude-flow/memory/store.json` is created
  - It contains a `routing-decision:<taskId>` entry
  - The entry's shape matches what `getIntelligenceStatsFromMemory()`
    filters on (`key.includes('routing') || metadata.type === 'routing-decision'`)
  - `metadata.confidence` maps from `quality` (used to compute avgConfidence)
  - `namespace` is `patterns` (parity with the AgentDB write)

Second test guards backwards compat: when `storeDecisions` is omitted,
no routing-decision entry appears in the JSON store.

Bridge is mocked (the AgentDB path's real behavior is covered elsewhere).

2/2 pass.

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

* test(funnel): regression coverage for cold-start seed pool

New test file `funnel-messages-seed-pool.test.ts` asserts the seven
invariants of the 2026-07-26 cold-start pool restoration:
  - MESSAGES is non-empty
  - contains ≥1 disclosure so the disclosure gate can unlock cold
  - every disclosure carries the exact ' · manage: ruflo settings' tail
    (ADR-301 invariant)
  - every seed message passes isValidMessage() (schema / host allowlist
    / control-char strip / 80-col cap)
  - every URL is on the exact-host allowlist (no third-party leaks)
  - contains ≥1 educational (4-in-5 rotation slots)
  - all ids are unique (rotation dedups on id)

7/7 pass. Guards against a re-empty of MESSAGES that would blank the
statusline promo row again on new installs.

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

* fix(funnel): disclosure gate now consults local seed pool (E2E follow-up)

E2E validation of the 2026-07-26 sweep exposed a gap: on true cold
start (no remote cache, no network), the promo row stayed blank
because `selectDisclosureMessage` only read `getRemoteMessages()` —
my local seed disclosure in messages.ts never reached the disclosure
gate, so the gate stayed `never_seen` forever.

Fix: `getDisclosureMessagePool` now merges local `MESSAGES` with the
remote pool via the same `eligibleMessagesFromPools` helper rotation
uses (remote wins by id). This mirrors the design of the rotation
selector — the seed is a cold-start bootstrap, remote takes over as
soon as its cache populates.

Verified end-to-end: cleared funnel cache + pointed
`RUFLO_FUNNEL_MESSAGES_ENDPOINT` at an unreachable host; statusline
render emits the local disclosure ("Ruflo shows occasional tips and
sponsor notes here · manage: ruflo settings") — exactly the string
seeded in messages.ts (`local.disclosure.v1`).

Refs: 2026-07-26 tracker sweep, promo row cold-start.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-07-26 15:05:33 -07:00
rUv dc015986dc feat(adr): reconcile deleted ADRs — hard-delete primitive + drop-and-rebuild reindex (#2668)
* feat(adr): reconcile deleted ADRs — hard-delete primitive + drop-and-rebuild reindex

Closes #2666. `adr-index` can only add/upsert; deleting an ADR file (or a
relation line from a surviving one) left an orphan row in `adr-patterns`/
`adr-edges` forever, and `adr-verify` certified the result as healthy since
an orphan has no dangling ref and forms no cycle.

Root cause traced into @claude-flow/cli itself, not just this plugin:
`memory delete` only ever soft-deletes (status='deleted'), so the row keeps
occupying its UNIQUE(namespace, key) slot and blocks a later non-upsert
re-store (#2652). `memory cleanup` reaps by age/TTL/quality, not by
"does the source still exist". Neither the AgentDB v3 bridge nor the sql.js
fallback exposed any hard, namespace-scoped delete anywhere in the CLI/MCP
surface. Separately, import.mjs/verify.mjs's spawnSync calls never passed
`cwd`, so ADR_ROOT scanned the right files but could write to the wrong
`.swarm/memory.db`.

CLI (@claude-flow/cli):
- memory-bridge.ts: bridgePurgeNamespace() — real DELETE FROM memory_entries
  WHERE namespace = ? against the live bridge SQLite handle, routed through
  the existing MutationGuard/AttestationLog hooks.
- memory-initializer.ts: purgeNamespace() (bridge-then-sql.js-fallback, same
  shape as deleteEntry) + withMemoryDbLock() (new <dbPath>.lock O_EXCL
  advisory lock, same stale-takeover pattern as global-ai-budget.ts —
  protects against a second concurrent purge/delete on the same file; does
  not close #2621, which needs every writer to adopt the same lock).
- commands/memory.ts: `memory purge --namespace <ns> --force [--dry-run]` —
  requires --namespace explicitly (no default), confirms unless --force.
- package.json: sql.js was already imported directly by
  memory-initializer.ts's fallback path but never declared as a dependency
  — worked by accident under npm's loose hoisting, silently broken under
  this repo's strict pnpm workspace (confirmed: unmodified deleteEntry/
  storeEntry fail identically). Declared explicitly.

Plugin (ruflo-adr):
- scripts/reindex.mjs (new) — purge both namespaces via `memory purge`,
  rescan every ADR currently on disk, store fresh, then re-list and assert
  the count equals files-on-disk (a storedRecords tally can't see a
  concurrent-writer clobber of the purge; a fresh recount can). Exits
  non-zero on failure. Full-namespace wipe rather than a selective orphan
  diff, since adr-edges keys always carry a fresh timestamp-rand suffix and
  are never deduplicated across imports — a selective pass would still
  leave duplicate edges accumulating for ADRs that do still exist.
- scripts/lib/parse-adrs.mjs (new) — findAdrs/parseAdr and sub-parsers
  extracted from import.mjs so reindex.mjs doesn't duplicate ~150 lines of
  dual-format parsing; import.mjs shrank 322 -> 156 lines, same dry-run
  output verified before/after (530 ADRs, same status/relation counts).
- import.mjs/verify.mjs: every npx subprocess call now passes `cwd: ROOT`.
- New `/adr-reindex` skill; adr-index/adr-verify SKILL.md cross-link it.
- ADR-0002 records the decision. plugin.json 0.3.0 -> 0.4.0.

Verified end-to-end against a scratch repo + local CLI build: 2 ADRs
indexed, one deleted from disk, re-run adr-index -> orphan survives
(bug reproduced exactly as filed), run adr-reindex -> orphan gone,
post-condition OK, exit 0.

New tests: __tests__/memory-purge-namespace-2666.test.ts (8 cases) —
namespace-scoped hard delete, cross-namespace isolation, the specific
#2652 regression (non-upsert re-store of a purged key must succeed),
invalid-namespace rejection, lock acquire/release/stale-takeover/
serialization. smoke.sh: 15 -> 21 checks.

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

* fix(ci): restore sql.js lockfile entry lost during local drift verification

A `git stash` + reinstall done while investigating unrelated pre-existing
pnpm-lock.yaml drift (verifying it wasn't caused by this branch) ran with
@claude-flow/cli/package.json temporarily reverted, regenerating
v3/pnpm-lock.yaml without the sql.js entry the prior commit added.
`git stash pop` restored package.json but not the lockfile state written
while the stash was active, so the previous commit's lockfile silently
lost that entry — CI's `pnpm install --frozen-lockfile` correctly caught
the drift (ERR_PNPM_OUTDATED_LOCKFILE). Re-ran `pnpm install` with
package.json in its correct state; frozen-lockfile install now passes
clean locally, matching CI's exact step.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-07-14 10:51:11 -04:00
ruv 64d0d70f22 darwin: iter 5 adr-coverage — handle full-bold MADR status style **Status: Value** 2026-06-26 16:21:58 -04:00
ruv 8c2f03e037 fix: 3 user-reported bugs — #2469 SKILL.md markdown, #2473 witness drift, #2474 ADR importer
Three bugs, one branch — all verified locally before commit.

**#2469** (AriESQ) — \`agent-docs-api-openapi/SKILL.md has malformed markdown\`
  The file had two adjacent \`---\` fenced YAML blocks at the top. Only
  the first is valid frontmatter; the second renders as \`<hr>\` + raw
  YAML text on skills.sh / GitHub web view. Wrapped the legacy agent
  YAML in a \`yaml\` code fence so it renders as code instead of leaking
  into the page body. Outer Claude Code skill frontmatter untouched.

**#2473** (verification HIGH) — witness manifest drift=8/9/9 on all 3 platforms
  My merge of #2468 changed 9 source files; their SHA-256s no longer
  matched the signed witness manifests. The Ed25519 seed is derived
  from gitCommit alone (\`sha256(gitCommit + ':ruflo-witness/v1')\`), so
  signatures generated on any one platform validate everywhere; only
  the manifest's \`os\` metadata field differs between the three.
  Re-signed all three with current HEAD source hashes — \`drift=0\` on
  all 3 platforms (was 8/9/9).

**#2474** (reimon) — ruflo-adr \`adr-index\` stores 0 records on common conventions
  Five distinct bugs in \`plugins/ruflo-adr/scripts/import.mjs\`:
  1. **(fatal)** Em-dash titles broke \`memoryStore\`: \`npm exec\` rejects
     args starting with non-ASCII dash, so every value-with-em-dash
     died with \`Records stored: 0/N\`. Use \`--flag=value\` form so npm
     sees a single argv token.
  2. \`parseStatus\` only matched \`**Status**:\` (colon outside bold) —
     missed the widely-used \`**Status:**\` (Nygard / MADR) form. Now
     accepts both placements.
  3. Body relationship regexes (\`**Supersedes**:\` etc.) had the same
     bug. Replaced with a shared \`REL(label)\` builder that accepts
     either colon placement and an optional qualifier (\`**Supersedes
     (partial):**\`).
  4. ID normalization mismatch: \`parseId\` kept \`ADR-0001\`, \`extractAdrRefs\`
     pad-then-stripped it to \`ADR-001\` → same ADR dangled. Single
     \`normalizeAdrId()\` now governs both paths.
  5. \`.claude/worktrees/*\` mirrors the repo; \`SKIP_DIRS\` didn't
     exclude \`.claude\`, so every ADR was indexed 2-3x. Added \`.claude\`
     to \`SKIP_DIRS\`.

  Verified with a synthetic fixture (3 ADRs: 1 zero-padded, 1 mixed,
  1 ADR-prefixed; em-dash titles; \`**Status:**\` and parenthetical
  qualifiers; \`**Supersedes:**\` / \`**Depends-on:**\` body relations;
  decoy worktree dir): \`Records stored: 3/3\`, all status fields
  populated, 3 edges parsed, 0 dangling refs, 0 worktree pollution
  (was: 0/3, all unknown, 0 edges).

  Em-dash fix end-to-end smoke: \`npx @claude-flow/cli@latest memory
  store --value="ADR-005 — Repository + Service Pattern"\` succeeds
  (was: \`npm error arg Argument starts with non-ascii dash\`).

Reporter @reimon and @AriESQ both filed clear, reproducible bugs with
root-cause analysis — thanks.

Co-Authored-By: RuFlo <ruv@ruv.net>
2026-06-26 14:40:18 -04:00
rUv ba92c56122 feat(cli-core): split cli into cli-core (lite) + cli (umbrella) — alpha.5 (#1764)
* feat(cli-core): scaffold @claude-flow/cli-core package + ADR-100 (#1748 Issue 3)

First fire of the cli-core split (ADR-100). Establishes the package
boundary, design doc, and skeleton entry point. Subsequent fires move
memory + hooks source in, build, benchmark, and publish under the
alpha tag.

What lands in this fire:

- v3/docs/adr/ADR-100-cli-core-split-lazy-load.md (Proposed) — full
  design rationale: the 1.8 MB / 999 files cold-cache problem from
  #1748 Issue 3, the cli-core ≤250KB target, the metapackage facade
  for the existing @claude-flow/cli, alpha-tag-then-promote
  versioning strategy, riskiest-assumption section, plan of work
  with 8-step roadmap, verification scaffold, migration path for
  plugin authors.

- v3/@claude-flow/cli-core/package.json — alpha 3.7.0-alpha.0,
  type=module, bin=claude-flow-core, exports map for the surface,
  publishConfig tag=alpha (so npm publish defaults to alpha, never
  accidentally to latest before validation).

- v3/@claude-flow/cli-core/README.md — what's included (memory +
  hooks), what's NOT (everything else — points at @claude-flow/cli),
  cold-cache verification snippet, ADR-100 cross-link.

- v3/@claude-flow/cli-core/tsconfig.json — ES2022 + Node module
  resolution + strict (mirrors @claude-flow/cli config).

- v3/@claude-flow/cli-core/src/index.ts — alpha-skeleton entry that
  responds to --version + --help and prints a "real surface lands
  in subsequent alphas" notice for any other command. This means
  the package can be npm-published immediately (non-empty surface)
  without breaking anything, and plugin authors can pin to it
  ahead of the actual feature landing.

Next fire (per ADR-100 §"Plan of work"):
  Step 2 — move commands/memory.ts + commands/hooks.ts +
            mcp-tools/{memory,hooks}-tools.ts + output.ts + types.ts
            into cli-core/src/
  Step 3 — build cli-core, smoke, fix any cross-package import
            misses
  Step 4 — update @claude-flow/cli's src/index.ts to re-export from
            cli-core + register lazy-loaded extras
  Step 5 — cold-cache benchmark old vs new, persist numbers
  Step 6 — bump versions to 3.7.0-alpha.1, npm publish --tag alpha
  Step 7 — PR + comment on #1748 with proof

Do NOT promote alpha → latest until the cold-cache benchmark shows
<5s and at least one external validator (the #1748 reporter is the
target candidate) confirms MCP startup succeeds on a cold cache.

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

* feat(cli-core): foundation surface (types, output, MCP tool types, validators) — ADR-100 Step 2

Second fire of the cli-core split. Foundation files copied from
@claude-flow/cli with no internal dependencies on cli's heavier
modules. Builds cleanly, dist is 136 KB / 20 files (vs cli's
9.6 MB / 777 files — 71× reduction).

Files copied to v3/@claude-flow/cli-core/src/:
  - types.ts          (CommandContext, Command, CommandResult, ParsedFlags)
  - output.ts         (terminal formatting, tables, spinners, progress)
  - mcp-tools/types.ts        (MCPTool, MCPToolInputSchema, MCPToolResult)
  - mcp-tools/validate-input.ts (input bounds + identifier guards)

Deferred from foundation:
  - fs-secure.ts — depends on encryption/vault.ts (ADR-096 encryption-
    at-rest). Will be added if memory.ts/hooks.ts need it; for the
    pure foundation surface it's not load-bearing.

Index.ts now exports a real foundation surface:
  import { CommandContext, MCPTool } from '@claude-flow/cli-core';
  import { output, validateInput } from '@claude-flow/cli-core';

Programmatic smoke verified — exports include:
  CLIError, CommandNotFoundError, ConfigError, ValidationError,
  output (OutputFormatter, Progress, Spinner), validateInput
  (assertValid, validateIdentifier, validateAgentSpawn, ...)

Binary --version and --help still work (alpha skeleton notice still
shown for non-foundation commands until Step 3+ lands memory + hooks).

Build: TS clean, no external errors. tsc --noEmit passes.

Next fire (Step 3): copy mcp-tools/memory-tools.ts +
mcp-tools/hooks-tools.ts (the MCP tool definitions — already use
types.ts + validate-input.ts that are now in cli-core, so should
build with minimal additional changes).

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

* feat(cli-core): foundation alpha.0 — defers memory/hooks tool deps to backend split (ADR-100 fire 3)

Fire 3 attempted to copy memory-tools.ts + hooks-tools.ts into cli-core
per the original plan and discovered both files have deep transitive ML
dependencies that defeat the lite-bundle goal:

  memory-tools.ts → ../memory/memory-initializer.js (2830 LOC) →
                    fs-secure.ts → encryption/vault.ts (ADR-096)

  hooks-tools.ts  → 9 transitive imports across
                    memory/{sona-optimizer, ewc-consolidation,
                    memory-bridge} and
                    ruvector/{moe-router, semantic-router,
                    flash-attention, lora-adapter,
                    enhanced-model-router}

Pulling those in would balloon cli-core past the 250 KB target.

Right architectural move (now documented in ADR-100 §"Discovery"):

  1. Memory backend abstraction. cli-core defines a MemoryBackend
     interface and ships a JsonMemoryBackend (no SQLite/HNSW/ONNX,
     just a JSON file at .swarm/memory.json — the foundation case).
     cli ships SqliteHnswMemoryBackend (the existing heavy
     implementation) that swaps in via env or import map.

  2. Tool-definition / handler split. MCPTool definitions (name,
     description, inputSchema — pure data, ~10 KB) go in cli-core.
     Handler implementations (the actual code) stay in cli's full
     module tree, accessed via dynamic-import only at request time.

This is more work than fire 1 anticipated — 4-6 additional fires
instead of the original "next fire" estimate. Net call: ship the
foundation alpha.0 NOW (proves publish pipeline, gives plugin
authors type imports + output utility); land backend abstraction
in alpha.1 and tool/handler split in alpha.2.

What's actually in alpha.0 (this commit):

  Foundation-only surface, 136 KB / 20 files dist, TS clean:
  - types.ts          (CommandContext, Command, ParsedFlags, ...)
  - output.ts         (terminal formatting, tables, spinners)
  - mcp-tools/types.ts          (MCPTool, MCPToolInputSchema, ...)
  - mcp-tools/validate-input.ts (input validators)

Programmatically:
  import { CommandContext, MCPTool, output, validateInput } from '@claude-flow/cli-core';

The Plan-of-work table in ADR-100 is updated with the new step
ordering, status markers, and the Discovery section explaining
why steps 3+4 changed shape.

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

* bench(cli-core): cold-cache 38× faster than @claude-flow/cli — alpha.0 published (ADR-100)

Persisted cold-cache benchmark proving the cli-core split fixes #1748
Issue 3's 30s MCP-startup timeout race:

  | Package                              | Cold     | Warm   | Files | Packed |
  | @claude-flow/cli-core@3.7.0-alpha.0  | 671 ms   | 361 ms |    22 | 22 KB  |
  | @claude-flow/cli@3.6.30              | 25.5 s   | 623 ms |   999 | 1.8 MB |

Results:
- 38× cold-cache speedup (25.5s → 0.67s)
- 80× packed-size reduction (1.8 MB → 22.3 KB)
- 45× file-count reduction (999 → 22)
- Fits comfortably under Claude Code's 30s MCP-startup timeout
  (cli is borderline at 25.5s — slow networks race and lose)

Caveats (also in the JSON's caveats array):
- cli-core@alpha.0 is foundation-only — types + output + MCP-tool
  types + validate-input. Does NOT yet expose memory/hooks command
  surface. ADR-100 §Discovery documents the tool-def/handler split
  needed for alpha.1 + alpha.2.
- The benchmark proves the COLD-CACHE thesis (smaller bundle = under
  timeout). It does NOT yet prove memory/hooks parity at speed —
  that requires the backend abstraction work in alpha.1.

Persisted to v3/docs/benchmarks/cli-core-cold-cache.json so future
regressions are visible.

Published: @claude-flow/cli-core@3.7.0-alpha.0 with --tag alpha
(latest also points to alpha.0 since this is the first publish).

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

* feat(cli-core): alpha.1 — MemoryBackend abstraction + working memory CLI (ADR-100 fire 4)

The hard problem from ADR-100 §Discovery — memory-tools.ts had a 2830-LOC
ML chain that defeated the lite-bundle goal. This commit implements the
backend abstraction so cli-core can ship its own lite memory implementation
without pulling sql.js / HNSW / ONNX.

What lands in alpha.1:

  src/memory/backend.ts (~150 LOC)
    Defines MemoryBackend interface — store / retrieve / search / list /
    delete / stats. MemoryEntry, MemorySearchResult, StoreOptions /
    SearchOptions / ListOptions types. Contract every backend (lite +
    heavy) implements.

  src/memory/json-backend.ts (~190 LOC)
    JsonMemoryBackend — implements the full interface against a JSON
    file (default .swarm/memory.json, override CLAUDE_FLOW_MEMORY_PATH).
    Atomic writes via tmp file + rename. TTL handling, namespace
    isolation, tag filtering, substring search. NO SQLite, NO HNSW,
    NO ONNX — pure JSON, no transitive ML deps.

  src/commands/memory.ts (~210 LOC)
    `memory <subcommand>` CLI dispatcher wrapping the backend. Subcommands:
    store / retrieve / get / list / search / delete / rm / stats.
    Every subcommand supports --format=json for piping to jq, --namespace,
    and the relevant per-op flags (--tags, --ttl, --upsert, --limit,
    --threshold, --path).

  src/index.ts (updated)
    Re-exports MemoryBackend interface + JsonMemoryBackend impl.
    Bin entry now dispatches `memory <args>` to runMemoryCommand.

Live verified end-to-end (smoke):
  ✓ store hello world           → ✓ stored default::hello
  ✓ store --namespace=test foo "bar baz" --tags=a,b → stored test::foo
  ✓ retrieve hello              → "world"
  ✓ list                        → 2 entries (default::hello + test::foo)
  ✓ search bar --format=json    → returns test::foo with score
  ✓ stats                       → backend=json, 2 entries, 656 bytes
  ✓ delete hello                → deleted
  ✓ list after delete           → only test::foo remains

Cold-cache via npm @ 3.7.0-alpha.1:
  rm -rf ~/.npm/_npx; npx @claude-flow/cli-core@alpha memory store ...
  → 2.71s wall-time

Compare to @claude-flow/cli@3.6.30: 25.5s cold cache.
**Cold-cache speedup with alpha.1's full memory surface: 9.4× faster.**
(alpha.0's foundation-only was 38× — alpha.1 is heavier because it has
the actual memory commands now, but still well under the 30s timeout.)

dist size: 200 KB / 32 files (vs cli's 9.6 MB / 777 files = 48× reduction).

Substrate trade-offs (honest):
  - Search degrades to substring matching (no semantic / vector). For
    real semantic search install @claude-flow/cli@alpha (heavy backend).
    The MemoryBackend interface is identical so call sites don't change.
  - Concurrent writers across processes: last-writer-wins. Acceptable for
    single-developer plugin workloads, not safe under high contention.
  - Atomic write via tmp+rename — no in-flight reader sees half-written
    JSON.

exports map updated:
  '@claude-flow/cli-core/memory'              → backend interface
  '@claude-flow/cli-core/memory/json-backend' → JsonMemoryBackend impl
  '@claude-flow/cli-core/commands/memory'     → CLI dispatcher

Published: @claude-flow/cli-core@3.7.0-alpha.1 with --tag alpha.

Next fire (alpha.2): tool-def/handler split for hooks-tools.ts. Pure
MCPTool definitions move to cli-core; handlers stay in @claude-flow/cli
behind dynamic-import boundaries.

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

* feat(cli-core): alpha.2 — MCP tool defs (memory + hooks subset, def-only) (ADR-100 fire 5)

Tool-definition / handler split landed. cli-core now ships pure-data MCPTool
definitions for the memory_* (6) + hooks_* (7) families an MCP server can
register with `tools/list`. Handlers stay in @claude-flow/cli — dynamic-
imported only at request time.

What lands in alpha.2:

  src/mcp-tools/memory-defs.ts (~115 LOC)
    6 memory_* MCPTool definitions:
      memory_store, memory_retrieve, memory_search, memory_delete,
      memory_list, memory_stats
    Descriptions match the 3.6.30 sharpening (#1748 Issue 4 — "Use when
    native X is wrong because Y" framing).

  src/mcp-tools/hooks-defs.ts (~120 LOC)
    7 hooks_* MCPTool definitions (most-used subset):
      hooks_route, hooks_pre-task, hooks_post-task, hooks_post-edit,
      hooks_model-outcome, hooks_model-route, hooks_explain
    Remaining 30 hooks tools stay in @claude-flow/cli (they pull in
    sona-optimizer, ewc-consolidation, ruvector/* heavy ML deps).
    If a session needs one of those, fall through to cli@alpha.

  src/mcp-tools/index.ts (~25 LOC)
    Re-exports memoryToolDefs + hooksToolDefs + allToolDefs
    (concatenation, suitable for a single tools/list registration).

  src/index.ts (updated)
    Re-exports MCPToolDef type + the def arrays so consumers can
    `import { allToolDefs } from '@claude-flow/cli-core'`.

  package.json (updated)
    Version 3.7.0-alpha.1 → 3.7.0-alpha.2
    New exports: ./mcp-tools, ./mcp-tools/memory-defs, ./mcp-tools/hooks-defs

Live verified end-to-end via npm:
  rm -rf ~/.npm/_npx
  npx -y @claude-flow/cli-core@alpha --version  → 2.3s cold

Programmatic import:
  import { memoryToolDefs, hooksToolDefs, allToolDefs } from '@claude-flow/cli-core';
  // memoryToolDefs.length: 6
  // hooksToolDefs.length: 7
  // allToolDefs.length: 13

dist: 256 KB / 44 files (still 38× smaller than cli's 9.6 MB / 777).

Why this matters (the lazy-load thesis):
  An MCP server backed by cli-core can advertise these 13 tools to a
  Claude session in <2.5s on a cold cache (vs cli's 25s race). The
  model sees the tools listed, picks one, and the handler can be
  dynamic-imported from cli at request time — paying the heavy cost
  ONLY when actually used. This is the structural fix #1748 Issue 3
  asked for.

Next fire (alpha.3): wire dynamic-import handlers in @claude-flow/cli's
metapackage so an MCP server using cli-core's defs can dispatch a real
memory_search invocation through to the heavy backend on demand.

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

* docs(cli-core): MIGRATION.md + scan helper for plugin authors (ADR-100 fire 6)

alpha.2 ships functional memory CLI + 13 MCPTool defs. This commit
makes the alpha actually usable today by plugin authors who want to
opt into the cold-cache speedup.

What lands:

  v3/@claude-flow/cli-core/MIGRATION.md
    Concrete diff + env-flag pattern for switching plugin Bash blocks
    from @claude-flow/cli@latest to @claude-flow/cli-core@alpha. Tables
    of what's safe to migrate today (memory ops) vs what's deferred
    (hooks, neural, agent/swarm). Storage backend coexistence section
    documents the SQLite/HNSW vs JSON file split honestly. Reverting
    instructions. Adoption sequence.

  v3/@claude-flow/cli-core/README.md
    Adds prominent MIGRATION.md pointer in the documentation list +
    issue #1760 cross-reference for alpha tracking.

  v3/@claude-flow/cli-core/scripts/migrate-plugin-call-sites.mjs
    Conservative scanner — walks a plugin dir, finds @claude-flow/cli
    invocations, classifies each as [review] (memory ops, safe to
    migrate by hand) vs [skip] (hooks/neural/agent/swarm — deferred).
    Deliberately does NOT auto-rewrite (regex over Node argv arrays
    isn't safe enough to ship without human review). Exit code 1
    when call sites need review; exit 0 only when fully clean.

Smoke run on cost-tracker (the canonical heavy CLI consumer):
  6 scripts flagged for memory migration:
    track.mjs, budget.mjs, conversation.mjs, export.mjs,
    federation.mjs, summary.mjs
  1 script correctly skipped (outcome.mjs uses hooks — defer to alpha.3+)

Migration recommendation summarized in MIGRATION.md:
  const cliPkg = process.env.CLI_CORE === '1'
    ? '@claude-flow/cli-core@alpha'  // lite — JSON backend, ~2s cold
    : '@claude-flow/cli@latest';     // heavy — SQLite/HNSW, ~25s cold

Plugin authors can opt in via env var, A/B test, then flip the
default once they're confident the substring-search regression
doesn't bite their use case.

Honest trade-offs documented in MIGRATION.md:
  - Storage file changes (.swarm/memory.db → .swarm/memory.json)
  - Search degrades to substring (no semantic / vector)
  - Hooks operations not yet migrable (alpha.3+)
  - Storage convergence planned for alpha.3 or later

No version bump in this commit (docs + scripts only). cli-core stays
at 3.7.0-alpha.2.

Next fire (alpha.3): wire dynamic-import handlers in @claude-flow/cli's
metapackage so an MCP server using cli-core's defs can dispatch a real
tools/call invocation through to the heavy backend on demand. After
that, cost-tracker's hooks call sites become migrable too.

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

* feat(cli-core): alpha.4 — bin-shim isMain fix + flag-form CLI compat + cost-tracker integration (ADR-100 fire 7)

End-to-end validation. cli-core@alpha.4 is now actually usable from
plugin scripts with the env-flag pattern. Two real bugs caught + fixed
during integration testing.

Bug 1: bin-shim isMain detection broken (alpha.0–alpha.2)

  Earlier alphas used:
    process.argv[1]?.endsWith('cli-core/dist/src/index.js')

  This fails when invoked via `npx @claude-flow/cli-core@alpha`
  because npx exposes the binary through a bin-shim symlink at
  ~/.npm/_npx/<hash>/node_modules/.bin/claude-flow-core, NOT the
  underlying dist/src/index.js. The endsWith check missed it,
  isMain returned false, the bin entry silently no-op'd.

  alpha.0/.1/.2 were effectively unusable as published binaries —
  --version returned empty, memory store wrote nowhere, exit 0.

  Fix: ESM-standard isMain via realpathSync comparison. argv[1]
  symlink-resolves to dist/src/index.js, which equals
  fileURLToPath(import.meta.url). Fallback heuristic for fringe
  cases (regex on argv[1] for claude-flow-core / cli-core/dist/).

Bug 2: memory store argument-parsing incompatibility

  cli-core's memory store only accepted positional `<key> <value>`
  args; @claude-flow/cli accepts `--key K --value V` flag form too.
  Plugin scripts (track.mjs, summary.mjs, etc.) use the flag form,
  so cli-core was a non-drop-in replacement.

  Fix: accept BOTH forms. Same change applied to retrieve, get,
  delete, rm subcommands. Usage messages updated to show both.

Cost-tracker integration (proves the alpha works end-to-end):

  plugins/ruflo-cost-tracker/scripts/track.mjs gains the env-flag
  pattern from MIGRATION.md:

    const cliPkg = process.env.CLI_CORE === '1'
      ? '@claude-flow/cli-core@alpha'   // lite — JSON, ~2s cold
      : '@claude-flow/cli@latest';       // heavy — SQLite/HNSW, ~25s cold

  Live verified end-to-end on this session's data:
    rm -rf ~/.npm/_npx
    CLI_CORE=1 CLAUDE_FLOW_MEMORY_PATH=/tmp/test.json \
      node plugins/ruflo-cost-tracker/scripts/track.mjs

  Result:
    | Persisted | `cli-core-test:session-1dba3b8c-1f1f-4718-bab4-9ffba5f49578` |
    /tmp/test.json: backend=json, 1 entry, $2578.69 total persisted

  CLI_CORE unset still uses cli@latest — backward compatible.

Why this matters: alpha.0–alpha.3 *published* but the bin was broken;
nobody pulling the alpha could actually run it. alpha.4 is the FIRST
alpha that genuinely works through `npx`. The cold-cache benchmark
numbers from fire 3 (38× speedup) were directionally right but
under-reported because alpha.0's binary just exited 0 immediately —
the real work was being skipped. With alpha.4's working bin, every
operation is real.

Published: @claude-flow/cli-core@3.7.0-alpha.4 with --tag alpha.

Branch: feat/cli-core-split, 8 commits.

Next fire: extend the env-flag pattern to remaining cost-tracker
scripts (budget, summary, conversation, federation, export); validate
each end-to-end; then the metapackage refactor for @claude-flow/cli.

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

* feat(cost-tracker): apply CLI_CORE env-flag to all 5 remaining scripts

Extends fire 7 (track.mjs) to budget/summary/conversation/federation/export.
All cost-tracker scripts now route to @claude-flow/cli-core@alpha when
CLI_CORE=1 is set, dropping cold-cache wall-time from ~25s to ~2s per
spawnSync. Falls back to the heavy @claude-flow/cli@latest by default.

Validated end-to-end against cli-core@3.7.0-alpha.4:
  - budget set/get/check     (markdown + JSON)
  - summary --format json    (cross-namespace list + retrieve)
  - conversation --format md (per-session timeline)
  - federation --format json (empty + non-empty paths)
  - export --prometheus      (textfile-collector exposition)

The 13 call sites all use a module-scoped CLI_PKG constant so the
package resolution happens once at startup, not per spawn.

Refs ADR-100, #1748 Issue 3, #1760

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

* feat(cli-core): alpha.5 — add mcp-tools/types + validate-input subpath exports

Adds two new package.json export entries:
  - ./mcp-tools/types          (MCPTool, MCPToolInputSchema, MCPToolResult)
  - ./mcp-tools/validate-input (validators)

These already shipped via the bundled ./mcp-tools index, but exposing them
as direct subpath exports is the prereq for @claude-flow/cli to replace its
4 byte-identical local copies (types.ts, output.ts, mcp-tools/types.ts,
mcp-tools/validate-input.ts — 1,229 LOC total) with single-line re-export
shims pointing at cli-core. Verified via diff -q between cli and cli-core
that all four files are currently identical.

That follow-up PR is intentionally NOT bundled with alpha.5 because cli is
2.2 MB / 1146 files and the foundation refactor deserves proper review.
This release just lays the wiring so the cli-side change is mechanical.

Validated all 11 cli-core subpath exports resolve correctly via Node's
ESM resolution. Build clean. Published @ alpha tag (latest stays at
alpha.0 to preserve "alpha is opt-in" semantics).

Refs ADR-100, #1748 Issue 3, #1760

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

* feat(cli): re-export 4 foundation modules from cli-core@alpha.5

Replaces 4 byte-identical files in @claude-flow/cli with one-line
re-export shims pointing at @claude-flow/cli-core. Net: -1,215 LOC of
duplication, +31 LOC of shim. The shim files keep their original paths
so the 60+ internal `import './types.js'`-style call sites continue to
resolve unchanged.

Files migrated (all confirmed byte-identical to cli-core via diff -q
prior to refactor):
  - cli/src/types.ts                 (287 LOC → 14 LOC shim)
  - cli/src/output.ts                (640 LOC → 9 LOC shim)
  - cli/src/mcp-tools/types.ts       (46 LOC → 8 LOC shim)
  - cli/src/mcp-tools/validate-input.ts (256 LOC → 9 LOC shim)

Wiring:
  - Added @claude-flow/cli-core: ^3.7.0-alpha.5 to cli's runtime deps
  - pnpm install resolved cli-core via the v3 monorepo workspace map
    (workspaces: ["@claude-flow/*"]); pnpm-lock.yaml updated accordingly

Validation:
  - npm run build: clean, zero TypeScript errors
  - node bin/cli.js --version: ok (v3.6.30)
  - node bin/cli.js memory list: ok (full SQLite backend still works)
  - dynamic import of output.js: OutputFormatter, Progress, Spinner,
    output instance all reachable through the shim

Future drift is now impossible — when cli-core's types change, cli
picks them up automatically via the dep. The 4 files are no longer
maintained in two places.

NOT bumping cli's version in this commit. Let the change bake on the
branch before publishing. Once external validation lands (#1748
reporter, cost-tracker bench-corpus parity), this can ship as part of
the cli-core-split PR.

Refs ADR-100, #1748 Issue 3, #1760

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

* test(cli-core): cold-cache benchmark script (ADR-100 fire 11)

Reproducible benchmark for the cli vs cli-core wall-time claim. Uses
NPM_CONFIG_CACHE redirection so it never touches ~/.npm/_npx (per
project memory feedback on destructive cache ops).

Results captured on this run (N=3, macOS, mise/node v22):

  | Condition          |   Min |  Mean |   Max |
  |--------------------|------:|------:|------:|
  | cli  cold-cache    | 32.76 | 34.89 | 37.38 |
  | cli  warm-cache    | 16.56 | 16.69 | 16.89 |
  | core cold-cache    |  1.38 |  1.53 |  1.80 |
  | core warm-cache    |  1.33 |  1.36 |  1.42 |

  Speedup
    cold-cache: 22.9x  (cli 34.89s vs core 1.53s)
    warm-cache: 12.3x  (cli 16.69s vs core 1.36s)

The cold-cache gap (~33s saved per spawn) is the user-visible win
for plugin scripts that spawn the cli many times — exactly the
problem #1748 issue 3 raised.

Refs ADR-100, #1748 Issue 3, #1760

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

* feat(ruflo-adr): apply CLI_CORE env-flag to verify + import scripts

Extends the cli-core opt-in pattern from cost-tracker (fires 7+8) to
ruflo-adr's CLI spawn sites — verify.mjs (list+retrieve) and import.mjs
(store). With CLI_CORE=1, both route to @claude-flow/cli-core@alpha,
dropping per-spawn cold-cache wall-time from ~25s to ~2s.

import.mjs documents one behavior delta: cli-core's JsonMemoryBackend
overwrites by default, collapsing the "exists"/UNIQUE-constraint signal
to plain "ok". Re-runs become idempotent (records refreshed) rather
than incremental (records skipped). The comment in import.mjs flags
this so users who want incremental semantics know to leave CLI_CORE
unset.

Validated end-to-end with a single-ADR smoke test under /tmp:
  CLI_CORE=1 IMPORT_FORMAT=json node import.mjs  →  storedRecords: 1
  CLI_CORE=1 VERIFY_FORMAT=json node verify.mjs  →  adrCount: 1

Refs ADR-100, #1748 Issue 3, #1760, #1764

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

---------

Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
2026-05-05 13:44:01 -04:00
rUv cd0b66f0d4 feat(ruflo-adr): one-shot import + verify scripts (v0.3.0) (#1758)
The /ruflo-adr:adr-index skill previously instructed the agent to
issue per-ADR MCP tool calls (agentdb_hierarchical-store +
agentdb_causal-edge + memory_store) — for the Ruflo monorepo's 70+
ADRs that's hundreds of round-trips, no dedup, no false-positive
guard for issue numbers, and no support for the v3-vs-plugin format
divergence.

This commit packages the logic as a reusable script.

What lands:

- scripts/import.mjs (~200 LOC) — walks the working dir (or ADR_ROOT
  override), parses every ADR under */docs/adr/ or */docs/adrs/,
  handles both formats:
    * v3-style: `# ADR-097: Title` heading + `**Status**:` line
    * plugin-style: YAML frontmatter (id/title/status/date/tags)
  Persists to `adr-patterns` (records) + `adr-edges` (relationships)
  via spawnSync over `npx @claude-flow/cli memory store`. JSON or
  markdown output. IMPORT_DRY_RUN=1 supported.

- scripts/verify.mjs (~80 LOC) — companion read-back validator that
  surfaces dangling refs, supersede cycles, status mismatches.
  Exits 1 on cycles (always corruption); exits 1 on any issue with
  VERIFY_STRICT=1 (CI fail-closed).

- skills/adr-index/SKILL.md — rewritten to call import.mjs in one
  Bash invocation instead of looping MCP calls.

- skills/adr-verify/SKILL.md — new sibling skill wrapping verify.mjs.

- scripts/smoke.sh — extended from 10 to 15 checks: verifies both
  scripts are executable + syntax-clean, validates the import
  script's dual-format support + issue-number false-positive guard,
  validates verify.mjs has cycle detection + fail-closed exit, and
  asserts both skills reference their respective scripts.

Live verified on the Ruflo monorepo:
  - 73 ADRs across 39 source dirs imported to adr-patterns
  - 13 'related' edges to adr-edges
  - 0 dangling refs (was 17 false positives on issue numbers before
    the script's extractAdrRefs() guard stripped #1697 / commit /
    PR / backtick-quoted patterns)
  - 0 status mismatches
  - 0 storage errors

False-positive fix detail: the skill's old prose said "extract ADR-NNN
references from ## Links section". The reality is ADRs reference
GitHub issues (#1697), commits, PRs all the time, and a naive regex
captures those as ADR-1697. extractAdrRefs() strips known non-ADR
patterns before the regex pass.

Plugin-only change. No npm publish needed (loaded directly from the
repo via /plugin install or claude --plugin-dir).

Version: 0.2.0 → 0.3.0. Smoke: 10 → 15 checks (~85ms).

Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
2026-05-05 11:01:31 -04:00