Repository navigation
Description
Summary
init, status, doctor and import all accept --db <path>, but the skill, reflexion, causal, recall, learner, db, sync, query, store-pattern, train and optimize-memory commands do not. They choose their database only from AGENTDB_PATH, falling back to ./agentdb.db in the working directory. Their handlers read arguments by position, so a --db <path> pair is not rejected. It becomes record data instead. reflexion store … --db X saves an episode whose input is --db and whose output is X. skill create n d --db X saves a skill whose code is --db. Both land in ./agentdb.db, and X is never touched. stats takes only a positional path, so stats --db X opens ./--db instead of X. With native better-sqlite3 that creates files literally named --db, --db-shm and --db-wal in the working directory. None of these cases produces a warning.
Environment
| Component | Version | Source of the value |
|---|---|---|
| macOS | 27.0 (build 26A428), kernel Darwin 27.0.0 | sw_vers, uname -r |
| Architecture | arm64 | uname -m |
| Node.js | v26.4.0 (mise-managed) | node --version |
| npm | 11.17.0 | npm --version |
| pnpm | 11.17.0 (mise-managed standalone binary) | pnpm --version |
| ruflo | 3.45.0 (npm latest 3.45.0) |
<npm-root-g>/ruflo/package.json; ruflo --version → ruflo v3.45.0 |
| @claude-flow/cli (bundled in ruflo) | 3.45.0 (npm latest 3.45.0) |
<npm-root-g>/ruflo/node_modules/@claude-flow/cli/package.json (not installed globally on its own) |
| @claude-flow/memory (bundled in ruflo) | 3.0.0-alpha.25 (npm latest 3.0.0-alpha.26) |
<npm-root-g>/ruflo/node_modules/@claude-flow/memory/package.json; nested copy under @claude-flow/cli is also 3.0.0-alpha.25 |
| agentdb (bundled in ruflo) | 3.0.0-alpha.20 (npm latest 3.0.0-alpha.20) |
<npm-root-g>/ruflo/node_modules/agentdb/package.json; nested copies under @claude-flow/cli and @claude-flow/memory are also 3.0.0-alpha.20 |
| agentdb (global standalone) | 3.0.0-alpha.17 | <npm-root-g>/agentdb/package.json; agentdb --version → agentdb v3.0.0-alpha.17 |
| ruvector (bundled in ruflo) | 0.2.41 | <npm-root-g>/ruflo/node_modules/ruvector/package.json (same version nested under @claude-flow/cli, @claude-flow/memory, agentdb) |
| ruvector (global) | 0.3.3 (npm latest 0.3.3) |
<npm-root-g>/ruvector/package.json; ruvector --version → 0.3.3 |
| agentic-qe | 3.14.3 (npm latest 3.14.3) |
<npm-root-g>/agentic-qe/package.json; aqe --version → 3.14.3 |
| ruvnet-brain plugin (Claude Code) | 4.3.28 (active, user scope, commit e89ea1ba167d) | ~/.claude/plugins/installed_plugins.json; ~/.claude/plugins/cache/ruvnet-brain/ruvnet-brain/4.3.28/.claude-plugin/plugin.json (older cache dirs 4.3.21 and 4.3.26 also present) |
| ruvnet-brain KB bundle | brainVersion 4.3.28 (releaseTag v4.3.28, builtUtc 2026-09-21T03:14:37.838Z) | ~/.cache/ruvnet-brain/kb/SOURCE.json |
| Claude Code | 2.1.283 (native installer) | claude --version; ~/.local/bin/claude → ~/.local/share/claude/versions/2.1.283 |
| codex-cli | 0.157.0 | codex --version; <npm-root-g>/@openai/codex/package.json |
| agentic-kit (global) | 4.0.0-alpha.55 | <npm-root-g>/@pacphi/agentic-kit/package.json; ak --version |
| agentic-kit (repo HEAD) | 9fcc06c34d9e (branch fix/audit-237-238-remediation; package.json 4.0.0-alpha.55) | git rev-parse HEAD |
<npm-root-g> = ~/.local/share/mise/installs/node/26.4.0/lib/node_modules (npm root -g). npm latest dist-tags read with npm view <pkg> dist-tags. Captured 2026-09-26.
The copy tested is the agentdb 3.0.0-alpha.20 bundled in ruflo 3.45.0, whose CLI file is byte-identical to the published tarball's. It loads native better-sqlite3 12.11.1 from its nested node_modules. That copy was placed there by a local native-dependency repair step in agentic-kit, not by ruflo or npm. The skill and reflexion misrouting does not depend on the backend: the standalone 3.0.0-alpha.17 behaves the same way. It runs on sql.js because its tree has no better-sqlite3: the dependency is optional, and require.resolve('better-sqlite3') from its directory fails with MODULE_NOT_FOUND.
Steps to reproduce
The commands run in a fresh mktemp -d directory, with HOME pointed at a temporary directory and XDG_* and AGENTDB_PATH unset. AGENTDB_DISABLE_TRANSFORMERS=1 only prevents a model download and does not affect the defect. These are the exact commands run on 2026-09-26:
CLI="$(npm root -g)/ruflo/node_modules/agentdb/dist/src/cli/agentdb-cli.js" # agentdb 3.0.0-alpha.20
T=$(mktemp -d); mkdir -p "$T/home" "$T/work"; cd "$T/work"
ad() { env -u XDG_CONFIG_HOME -u XDG_DATA_HOME -u XDG_CACHE_HOME -u XDG_STATE_HOME -u AGENTDB_PATH \
HOME="$T/home" AGENTDB_DISABLE_TRANSFORMERS=1 node "$CLI" "$@"; }
ad init --db "$T/target.db" >/dev/null 2>&1; echo "init exit=$?"
ad reflexion store s1 task-a 0.9 true "worked" --db "$T/target.db" >/dev/null 2>&1; echo "reflexion store exit=$?"
ad skill create jwt_auth "Generate JWT" --db "$T/target.db" >/dev/null 2>&1; echo "skill create exit=$?"
ad skill search jwt 5 --db "$T/target.db" >/dev/null 2>&1; echo "skill search exit=$?"
ad skill consolidate --db "$T/target.db" 2>&1 | grep -m1 "Min Attempts"
ad stats --db "$T/target.db" 2>&1 | grep -m1 "Getting statistics"
ls -1A "$T/work"
sqlite3 "$T/target.db" "select 'target.db episodes', count(*) from episodes; select 'target.db skills', count(*) from skills;"
sqlite3 "$T/work/agentdb.db" "select 'agentdb.db episode', session_id, task, input, output from episodes; select 'agentdb.db skill', name, code from skills;"
# control: the environment variable is honored
env -u XDG_CONFIG_HOME -u XDG_DATA_HOME -u XDG_CACHE_HOME -u XDG_STATE_HOME HOME="$T/home" \
AGENTDB_DISABLE_TRANSFORMERS=1 AGENTDB_PATH="$T/target.db" \
node "$CLI" reflexion store s2 task-b 0.9 true "worked" >/dev/null 2>&1; echo "AGENTDB_PATH reflexion store exit=$?"
sqlite3 "$T/target.db" "select 'target.db episodes after AGENTDB_PATH', count(*) from episodes;"Output (temp path shown as $T, ANSI colours stripped):
init exit=0
reflexion store exit=1
skill create exit=1
skill search exit=0
ℹ Min Attempts: 3
ℹ Getting statistics for: --db
--db
--db-shm
--db-wal
agentdb.db
agentdb.db-shm
agentdb.db-wal
target.db episodes|0
target.db skills|0
agentdb.db episode|s1|task-a|--db|$T/target.db
agentdb.db skill|jwt_auth|--db
AGENTDB_PATH reflexion store exit=1
target.db episodes after AGENTDB_PATH|1
The exit=1 results come from a separate defect, reported in the companion issue #26 ("CLI write commands exit 1 after committing: this.db.save is not a function"). Write commands throw this.db.save is not a function on native better-sqlite3 after the row has been committed. The misrouting shown here does not depend on that defect. skill search exits 0 and silently searches ./agentdb.db. skill consolidate --db X reads --db as the positional min-attempts: parseInt('--db') gives NaN, which falls back to 3, as the Min Attempts: 3 line shows. It then runs against ./agentdb.db.
Against the global standalone agentdb 3.0.0-alpha.17 (sql.js), reflexion store and skill create exit 0 and misroute the same way. These are the exact commands run, in the same sandbox and without init:
CLI="$(npm root -g)/agentdb/dist/src/cli/agentdb-cli.js" # agentdb 3.0.0-alpha.17
T=$(mktemp -d); mkdir -p "$T/home" "$T/work"; cd "$T/work"
ad() { env -u XDG_CONFIG_HOME -u XDG_DATA_HOME -u XDG_CACHE_HOME -u XDG_STATE_HOME -u AGENTDB_PATH \
HOME="$T/home" AGENTDB_DISABLE_TRANSFORMERS=1 node "$CLI" "$@"; }
ad reflexion store s1 task-a 0.9 true "worked" --db "$T/target.db" 2>&1 | grep -E "native|sql.js|WASM|Stored|❌" | head -3; echo "reflexion store exit=${PIPESTATUS[0]}"
ad skill create jwt_auth "Generate JWT" --db "$T/target.db" 2>&1 | grep -E "Created|❌"; echo "skill create exit=${PIPESTATUS[0]}"
ls -1A "$T/work"; ls "$T/target.db" 2>&1 | sed "s#$T#\$T#"
sqlite3 "$T/work/agentdb.db" "select 'agentdb.db episode', session_id, task, input, output from episodes; select 'agentdb.db skill', name, code from skills;" | sed "s#$T#\$T#"Output (ANSI colours stripped):
✅ Using sql.js (WASM SQLite, no build tools required)
✅ Stored episode #1
reflexion store exit=0
✅ Created skill #1
skill create exit=0
agentdb.db
ls: $T/target.db: No such file or directory
agentdb.db episode|s1|task-a|--db|$T/target.db
agentdb.db skill|jwt_auth|--db
On sql.js (alpha.17, or alpha.20 with AGENTDB_FORCE_SQLJS=1), stats --db X still prints Getting statistics for: --db and reports on that path instead of X, but it leaves no --db* files in the working directory.
Expected vs actual
Expected: Every command that opens a database honors --db <path>, as init, status, doctor and import already do. A command that does not support the flag rejects it and exits non-zero rather than treating it as data. stats --db X reports on X.
Actual:
| Command | What happens |
|---|---|
reflexion store … --db X |
Writes to ./agentdb.db; --db and X become the episode's input and output |
skill create … --db X |
Writes to ./agentdb.db; --db becomes the skill's code |
skill search … --db X |
Searches ./agentdb.db and exits 0 |
skill consolidate --db X |
Runs against ./agentdb.db with --db parsed as min-attempts |
stats --db X |
Reports on ./--db instead of X; with native better-sqlite3 it also creates ./--db, ./--db-shm and ./--db-wal |
X itself |
Stays empty (0 episodes, 0 skills); in the alpha.17 run, which skips init, it is never created |
Evidence
Installed package: agentdb 3.0.0-alpha.20 (bundled in ruflo 3.45.0), <npm-root-g>/ruflo/node_modules/agentdb/dist/src/cli/agentdb-cli.js:
:1592:const dbPath = process.env.AGENTDB_PATH || './agentdb.db';is the only path source for everything dispatched at:1596-1628(causal,recall,learner,reflexion,skill,db,sync,query,store-pattern,train,optimize-memory).--dbis parsed only byinit(:1389-1391),status(:1404-1406),doctor(:1466-1468) andimport(handleImportCommand,:2611-2612).:2683(handleStatsCommand):const dbPath = args[0] || './agentdb.db';takes the first token, so--dbbecomes the path.- Positional handlers with no flag rejection:
reflexion store:1863-1874, whereinput: args[5]andoutput: args[6]skill create:1955-1960, wherecode: args[2]skill search:1962-1966skill consolidate:1968-1974, whereminAttempts: args[0] ? parseInt(args[0]) : undefined
- The help documents
--dbforinit,status,doctorandimport(:2754,:2757,:2761,:2826), a positional path forstats(:2830,agentdb stats [db-path]), andAGENTDB_PATHunder ENVIRONMENT (:3027). No text saysskillandreflexionaccept only the environment variable. - The standalone
agentdb3.0.0-alpha.17 has the same lines (:1592,:2683).
Upstream source, ruvnet/agentdb main at 6cdefe4 (release 3.0.0-alpha.20, still HEAD on 2026-09-26), src/cli/agentdb-cli.ts:
:1948: theAGENTDB_PATHfallback:1742,:1757,:1815,:2966:--dbininit,status,doctorandimport:3058:statsargs[0]:2226:reflexion storeinput: args[5]:2307:skill createcode: args[2]:2316:skill consolidateminAttempts: args[0]:3410: the help's ENVIRONMENT block
A RuvNet Brain search (search_ruvnet) returned only agentdb and agentic-flow documentation for this query, not the CLI source, so the upstream paths above come from the GitHub tree.
Impact
- Silent misrouting. Episodes and skills go to a
./agentdb.dbthat depends on the caller's working directory, while the database the caller named stays empty. A wrapper that passes--dbto every subcommand, as the help suggests forinitandstatus, splits its data across stores without noticing. - Data corruption. Flag tokens and file paths are persisted into
input,outputandcodefields, and later retrievals and searches return them. The episode'soutput(here, the path) is also part of the textReflexionMemory.buildEpisodeText()embeds. - Stray files. With native better-sqlite3,
stats --db Xleaves--db*files in the working directory, which must be removed withrm -- --db*.
Suggested direction
- Parse
--db <path>(and--db=<path>) once inmain()before dispatch. Apply the precedence--db>AGENTDB_PATH>./agentdb.dband remove the pair from the arguments passed to handlers. - Let
stats(andexport) accept--dbas well as the positional path. - Make the positional handlers reject unknown
--options with a non-zero exit instead of storing them. - Document the precedence in
--help.
Related
- CLI write commands exit 1 after committing: this.db.save is not a function #26, companion issue (same repro session): "CLI write commands exit 1 after committing: this.db.save is not a function". It explains the
exit=1results above. - doctor command: db.get() is not a valid better-sqlite3 API, and ESM require() misreports core modules as "not available" #23 (open):
doctorcalls a non-better-sqlite3 API (db.get()). A different command in the same CLI. - silent-lost-update: When better-sqlite3 cannot be loaded, db-fallback silently falls back to sql.js, which holds the whole database in memory and, on shutdown, overwrites the entire file with its in-memory image #12 (closed) and PR fix: prevent sql.js lost updates and MCP stdout pollution #18 (merged):
AGENTDB_PATHsharing with sql.js. Related to the path contract but not this defect. - AgentDB persistence problem agentic-flow#48 (closed): MCP server persistence with
AGENTDB_PATH. Not the same defect. - Duplicate search (open and closed issues and PRs, 2026-09-26): all 16 issues and 9 PRs in ruvnet/agentdb read by title, plus
gh search issuesin ruvnet/agentdb, ruvnet/ruflo and ruvnet/agentic-flow forAGENTDB_PATH --db,reflexion store,skill consolidate,save is not a functionanddb.save, andgh search prsfor--db,AGENTDB_PATHandsave. No existing report.
Activity