@cerefox/memory 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -79,11 +79,19 @@ already provisioned (see "Before you install").
79
79
  > the deploy logic is ported to the TS CLI. For now, the setup-supabase
80
80
  > guide walks through it.
81
81
 
82
+ > **Upgrading from the Python `cerefox` CLI?** If you have a working
83
+ > `.env` in your repo clone, init detects it and offers to **copy** it to
84
+ > `~/.cerefox/.env` so the TS CLI uses the new home while Python keeps
85
+ > reading the repo file unchanged. See the migration-v0.5 guide for the
86
+ > three-choice prompt. Existing users with no `~/.cerefox/.env` see zero
87
+ > behavior change until they opt in.
88
+
82
89
  ---
83
90
 
84
91
  ## Connect an AI agent
85
92
 
86
93
  ```bash
94
+ # Run the configure-agent commands that apply to your setup:
87
95
  cerefox configure-agent --tool claude-code # writes ~/.claude/mcp.json
88
96
  cerefox configure-agent --tool claude-desktop # writes Claude Desktop config
89
97
  ```
@@ -7179,7 +7179,7 @@ var exports_meta = {};
7179
7179
  __export(exports_meta, {
7180
7180
  PKG_VERSION: () => PKG_VERSION
7181
7181
  });
7182
- var PKG_VERSION = "0.5.2";
7182
+ var PKG_VERSION = "0.5.3";
7183
7183
  var init_meta = () => {};
7184
7184
 
7185
7185
  // ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
@@ -22494,26 +22494,35 @@ function expandTilde(p) {
22494
22494
  }
22495
22495
  return p;
22496
22496
  }
22497
+ function userStateDirAbs(opts) {
22498
+ return resolvePath(join(opts.home ?? homedir(), USER_STATE_DIR_NAME));
22499
+ }
22497
22500
  function resolveConfigDir(opts = {}) {
22498
22501
  const override = (env.CEREFOX_CONFIG_DIR ?? "").trim();
22499
22502
  if (override) {
22500
22503
  return resolvePath(expandTilde(override));
22501
22504
  }
22505
+ const userState = userStateDirAbs(opts);
22506
+ if (existsSync(join(userState, ".env"))) {
22507
+ return userState;
22508
+ }
22502
22509
  const here = opts.cwd ?? processCwd();
22503
22510
  if (existsSync(join(here, ".env"))) {
22504
22511
  return resolvePath(here);
22505
22512
  }
22506
- return resolvePath(join(homedir(), USER_STATE_DIR_NAME));
22513
+ return userState;
22507
22514
  }
22508
22515
  function resolveEnvFile(opts = {}) {
22509
22516
  return join(resolveConfigDir(opts), ".env");
22510
22517
  }
22511
- function userStateDir() {
22512
- return resolvePath(join(homedir(), USER_STATE_DIR_NAME));
22518
+ function userStateDir(opts = {}) {
22519
+ return userStateDirAbs(opts);
22513
22520
  }
22514
22521
  function isDevMode(opts = {}) {
22515
22522
  if ((env.CEREFOX_CONFIG_DIR ?? "").trim())
22516
22523
  return false;
22524
+ if (existsSync(join(userStateDirAbs(opts), ".env")))
22525
+ return false;
22517
22526
  const here = opts.cwd ?? processCwd();
22518
22527
  return existsSync(join(here, ".env"));
22519
22528
  }
@@ -38333,7 +38342,7 @@ init_cli_core();
38333
38342
  init_meta();
38334
38343
  init_config();
38335
38344
  init_config();
38336
- import { existsSync as existsSync5, statSync as statSync2 } from "node:fs";
38345
+ import { existsSync as existsSync5, realpathSync, statSync as statSync2 } from "node:fs";
38337
38346
  import { homedir as homedir4 } from "node:os";
38338
38347
  import { join as join5 } from "node:path";
38339
38348
  function checkBinary() {
@@ -38577,6 +38586,23 @@ function checkMcpConfigs() {
38577
38586
  detail: found.map((f) => f.label).join(", ")
38578
38587
  };
38579
38588
  }
38589
+ function checkLegacyShadowEnv() {
38590
+ const home = homedir4();
38591
+ const homeEnv = join5(home, USER_STATE_DIR_NAME, ".env");
38592
+ const cwdEnv = join5(process.cwd(), ".env");
38593
+ if (!existsSync5(homeEnv) || !existsSync5(cwdEnv))
38594
+ return null;
38595
+ try {
38596
+ if (realpathSync(homeEnv) === realpathSync(cwdEnv))
38597
+ return null;
38598
+ } catch {}
38599
+ return {
38600
+ name: "legacy env",
38601
+ status: "skipped",
38602
+ detail: `${cwdEnv} (shadowed by ~/.cerefox/.env)`,
38603
+ hint: "Python `uv run cerefox …` still reads this file during the v0.5–v0.7 migration window. " + "Safe to delete once Python support is removed (v0.9+)."
38604
+ };
38605
+ }
38580
38606
  function checkPostgres() {
38581
38607
  if (process.env.CEREFOX_DATABASE_URL) {
38582
38608
  return {
@@ -38593,11 +38619,13 @@ function checkPostgres() {
38593
38619
  };
38594
38620
  }
38595
38621
  async function runAllChecks() {
38622
+ const legacy = checkLegacyShadowEnv();
38596
38623
  return [
38597
38624
  checkBinary(),
38598
38625
  checkRuntime(),
38599
38626
  checkVersion(),
38600
38627
  checkConfig(),
38628
+ ...legacy ? [legacy] : [],
38601
38629
  await checkSupabase(),
38602
38630
  await checkOpenAI(),
38603
38631
  await checkSchemaVersion(),
@@ -38938,12 +38966,14 @@ init_cli_core();
38938
38966
  init_config();
38939
38967
  import {
38940
38968
  chmodSync,
38969
+ copyFileSync as copyFileSync2,
38941
38970
  existsSync as existsSync6,
38942
38971
  mkdirSync as mkdirSync3,
38943
38972
  readFileSync as readFileSync7,
38944
38973
  writeFileSync as writeFileSync3
38945
38974
  } from "node:fs";
38946
- import { dirname as dirname3 } from "node:path";
38975
+ import { homedir as homedir5 } from "node:os";
38976
+ import { dirname as dirname3, join as join7 } from "node:path";
38947
38977
  async function readConfigFile(path) {
38948
38978
  if (!existsSync6(path)) {
38949
38979
  throw userError(`--config file not found: ${path}`);
@@ -38973,6 +39003,41 @@ async function readConfigFile(path) {
38973
39003
  CEREFOX_AUTHOR_TYPE: typeof obj.CEREFOX_AUTHOR_TYPE === "string" ? obj.CEREFOX_AUTHOR_TYPE : undefined
38974
39004
  };
38975
39005
  }
39006
+ function parseDotEnvFile(content) {
39007
+ const map = {};
39008
+ for (const rawLine of content.split(/\r?\n/)) {
39009
+ const line = rawLine.trim();
39010
+ if (!line || line.startsWith("#"))
39011
+ continue;
39012
+ const eqIdx = line.indexOf("=");
39013
+ if (eqIdx === -1)
39014
+ continue;
39015
+ const key = line.slice(0, eqIdx).trim();
39016
+ let value = line.slice(eqIdx + 1).trim();
39017
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
39018
+ value = value.slice(1, -1);
39019
+ }
39020
+ map[key] = value;
39021
+ }
39022
+ return map;
39023
+ }
39024
+ function answersFromEnvFile(path) {
39025
+ const parsed = parseDotEnvFile(readFileSync7(path, "utf8"));
39026
+ const required = ["CEREFOX_SUPABASE_URL", "CEREFOX_SUPABASE_KEY", "OPENAI_API_KEY"];
39027
+ for (const key of required) {
39028
+ if (!parsed[key] || parsed[key].trim() === "") {
39029
+ throw userError(`Existing .env at ${path} is missing required key "${key}".`, `Fix it manually or run \`cerefox init --force\` to start fresh.`);
39030
+ }
39031
+ }
39032
+ return {
39033
+ CEREFOX_SUPABASE_URL: parsed.CEREFOX_SUPABASE_URL,
39034
+ CEREFOX_SUPABASE_KEY: parsed.CEREFOX_SUPABASE_KEY,
39035
+ OPENAI_API_KEY: parsed.OPENAI_API_KEY,
39036
+ CEREFOX_DATABASE_URL: parsed.CEREFOX_DATABASE_URL,
39037
+ CEREFOX_AUTHOR_NAME: parsed.CEREFOX_AUTHOR_NAME,
39038
+ CEREFOX_AUTHOR_TYPE: parsed.CEREFOX_AUTHOR_TYPE
39039
+ };
39040
+ }
38976
39041
  async function promptForAnswers() {
38977
39042
  println(c.bold("Cerefox first-run setup."));
38978
39043
  println(c.dim(`This will write configuration to ~/.cerefox/.env (or CEREFOX_CONFIG_DIR if set).
@@ -39087,35 +39152,38 @@ async function validateOpenAI(key) {
39087
39152
  throw userError(`OpenAI key validation failed: ${resp.status} ${body.slice(0, 100)}`, "Verify the key on https://platform.openai.com/api-keys.");
39088
39153
  }
39089
39154
  }
39090
- async function action14(options) {
39091
- const envPath = resolveEnvFile();
39092
- if (existsSync6(envPath) && !options.force) {
39093
- println(c.yellow(`⚠ Config already exists at ${envPath}.`));
39094
- const ok2 = await confirm("Overwrite?", true);
39095
- if (!ok2) {
39096
- println(c.dim("Aborted. Use `--force` to skip this prompt next time."));
39097
- return;
39098
- }
39099
- }
39100
- const answers = options.config ? await readConfigFile(options.config) : await promptForAnswers();
39155
+ function printMigrationMenu(cwdEnv, homeEnv) {
39101
39156
  println("");
39102
- println(c.bold("Validating credentials…"));
39103
- await validateSupabase(answers.CEREFOX_SUPABASE_URL, answers.CEREFOX_SUPABASE_KEY);
39104
- println(c.green(" Supabase reachable"));
39105
- await validateOpenAI(answers.OPENAI_API_KEY);
39106
- println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
39107
- mkdirSync3(dirname3(envPath), { recursive: true });
39108
- writeFileSync3(envPath, buildEnvFile(answers), "utf8");
39109
- if (process.platform !== "win32") {
39110
- try {
39111
- chmodSync(envPath, 384);
39112
- } catch {
39113
- warn(`Could not chmod 0600 ${envPath}.`);
39114
- }
39115
- }
39157
+ println(c.yellow(`⚠ Found existing config at ${cwdEnv}.`));
39158
+ println("");
39159
+ println("This may be from a previous Python install. The TS CLI can use the");
39160
+ println("same .env — env-var names are identical, no rewrite needed.");
39116
39161
  println("");
39117
- println(c.green(`✓ Wrote ${envPath}`));
39162
+ println(" " + c.bold("[c]") + " Copy to " + homeEnv + " " + c.green("(recommended)"));
39163
+ println(c.dim(" • TS reads the new home from now on"));
39164
+ println(c.dim(" • Python keeps reading " + cwdEnv + " (backward compat)"));
39165
+ println(c.dim(" • Edit ~/.cerefox/.env going forward; the repo .env is legacy"));
39118
39166
  println("");
39167
+ println(" " + c.bold("[u]") + " Use " + cwdEnv + " as-is, skip writing anything");
39168
+ println(c.dim(" • Both TS and Python keep reading the existing file"));
39169
+ println(c.dim(" • Defer the migration"));
39170
+ println("");
39171
+ println(" " + c.bold("[f]") + " Fresh start — interactive prompts, write to " + homeEnv);
39172
+ println(c.dim(" • Use if the existing file is stale or wrong"));
39173
+ println("");
39174
+ }
39175
+ async function promptMigrationChoice() {
39176
+ const choice = await ask({
39177
+ type: "text",
39178
+ name: "choice",
39179
+ message: "Choice (c/u/f) [c]",
39180
+ initial: "c",
39181
+ validate: (v) => /^[cuf]?$/i.test(v.trim()) || "Expected c, u, or f."
39182
+ });
39183
+ const ch = choice.trim().toLowerCase() || "c";
39184
+ return ch;
39185
+ }
39186
+ async function postWriteLifecycle(envPath, options) {
39119
39187
  if (!options.skipSchema) {
39120
39188
  println(c.bold("Schema deploy"));
39121
39189
  println(c.dim(` v0.5 doesn't yet bundle the schema-deploy path (it needs the direct
@@ -39159,6 +39227,127 @@ async function action14(options) {
39159
39227
  println(c.dim(" cerefox doctor # verify everything"));
39160
39228
  println(c.dim(' cerefox search "…" # search the KB'));
39161
39229
  println(c.dim(" cerefox ingest <file> # add a doc"));
39230
+ println("");
39231
+ println(c.dim(` Config in effect: ${envPath}`));
39232
+ }
39233
+ function writeAnswersTo(target, answers) {
39234
+ mkdirSync3(dirname3(target), { recursive: true });
39235
+ writeFileSync3(target, buildEnvFile(answers), "utf8");
39236
+ if (process.platform !== "win32") {
39237
+ try {
39238
+ chmodSync(target, 384);
39239
+ } catch {
39240
+ warn(`Could not chmod 0600 ${target}.`);
39241
+ }
39242
+ }
39243
+ }
39244
+ async function action14(options) {
39245
+ const homeEnv = join7(homedir5(), USER_STATE_DIR_NAME, ".env");
39246
+ const cwdEnv = join7(process.cwd(), ".env");
39247
+ const explicitDir = (process.env.CEREFOX_CONFIG_DIR ?? "").trim();
39248
+ if (explicitDir) {
39249
+ const target2 = resolveEnvFile();
39250
+ if (existsSync6(target2) && !options.force) {
39251
+ println(c.yellow(`⚠ Config already exists at ${target2}.`));
39252
+ const ok2 = await confirm("Overwrite?", true);
39253
+ if (!ok2) {
39254
+ println(c.dim("Aborted. Use `--force` to skip this prompt next time."));
39255
+ return;
39256
+ }
39257
+ }
39258
+ const answers2 = options.config ? await readConfigFile(options.config) : await promptForAnswers();
39259
+ println("");
39260
+ println(c.bold("Validating credentials…"));
39261
+ await validateSupabase(answers2.CEREFOX_SUPABASE_URL, answers2.CEREFOX_SUPABASE_KEY);
39262
+ println(c.green(" ✓ Supabase reachable"));
39263
+ await validateOpenAI(answers2.OPENAI_API_KEY);
39264
+ println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
39265
+ writeAnswersTo(target2, answers2);
39266
+ println("");
39267
+ println(c.green(`✓ Wrote ${target2}`));
39268
+ println("");
39269
+ await postWriteLifecycle(target2, options);
39270
+ return;
39271
+ }
39272
+ if (existsSync6(homeEnv) && !options.force) {
39273
+ println(c.yellow(`⚠ Config already exists at ${homeEnv}.`));
39274
+ const ok2 = await confirm("Overwrite?", true);
39275
+ if (!ok2) {
39276
+ println(c.dim("Aborted. Use `--force` to skip this prompt next time."));
39277
+ return;
39278
+ }
39279
+ const answers2 = options.config ? await readConfigFile(options.config) : await promptForAnswers();
39280
+ println("");
39281
+ println(c.bold("Validating credentials…"));
39282
+ await validateSupabase(answers2.CEREFOX_SUPABASE_URL, answers2.CEREFOX_SUPABASE_KEY);
39283
+ println(c.green(" ✓ Supabase reachable"));
39284
+ await validateOpenAI(answers2.OPENAI_API_KEY);
39285
+ println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
39286
+ writeAnswersTo(homeEnv, answers2);
39287
+ println("");
39288
+ println(c.green(`✓ Wrote ${homeEnv}`));
39289
+ println("");
39290
+ await postWriteLifecycle(homeEnv, options);
39291
+ return;
39292
+ }
39293
+ if (existsSync6(cwdEnv) && !options.force && !options.config) {
39294
+ printMigrationMenu(cwdEnv, homeEnv);
39295
+ const ch = await promptMigrationChoice();
39296
+ println("");
39297
+ if (ch === "c") {
39298
+ mkdirSync3(dirname3(homeEnv), { recursive: true });
39299
+ copyFileSync2(cwdEnv, homeEnv);
39300
+ if (process.platform !== "win32") {
39301
+ try {
39302
+ chmodSync(homeEnv, 384);
39303
+ } catch {
39304
+ warn(`Could not chmod 0600 ${homeEnv}.`);
39305
+ }
39306
+ }
39307
+ println(c.green(`✓ Copied ${cwdEnv} → ${homeEnv}`));
39308
+ println(c.dim(` Repo file unchanged — Python still reads it during migration.`));
39309
+ println("");
39310
+ const answers2 = answersFromEnvFile(homeEnv);
39311
+ println(c.bold("Validating credentials…"));
39312
+ await validateSupabase(answers2.CEREFOX_SUPABASE_URL, answers2.CEREFOX_SUPABASE_KEY);
39313
+ println(c.green(" ✓ Supabase reachable"));
39314
+ await validateOpenAI(answers2.OPENAI_API_KEY);
39315
+ println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
39316
+ println("");
39317
+ await postWriteLifecycle(homeEnv, options);
39318
+ return;
39319
+ }
39320
+ if (ch === "u") {
39321
+ const answers2 = answersFromEnvFile(cwdEnv);
39322
+ println(c.bold("Validating existing config…"));
39323
+ await validateSupabase(answers2.CEREFOX_SUPABASE_URL, answers2.CEREFOX_SUPABASE_KEY);
39324
+ println(c.green(" ✓ Supabase reachable"));
39325
+ await validateOpenAI(answers2.OPENAI_API_KEY);
39326
+ println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
39327
+ println("");
39328
+ println(c.green(`✓ Using existing config at ${cwdEnv}`));
39329
+ println(c.dim(` TS reads it via the legacy dev-mode fallback (~/.cerefox/.env not present).`));
39330
+ println(c.dim(` Run \`cerefox init\` again later to migrate to the new home.`));
39331
+ println("");
39332
+ await postWriteLifecycle(cwdEnv, options);
39333
+ return;
39334
+ }
39335
+ println(c.dim(`Fresh start. Ignoring ${cwdEnv}; writing a new config to ${homeEnv}.`));
39336
+ println("");
39337
+ }
39338
+ const target = homeEnv;
39339
+ const answers = options.config ? await readConfigFile(options.config) : await promptForAnswers();
39340
+ println("");
39341
+ println(c.bold("Validating credentials…"));
39342
+ await validateSupabase(answers.CEREFOX_SUPABASE_URL, answers.CEREFOX_SUPABASE_KEY);
39343
+ println(c.green(" ✓ Supabase reachable"));
39344
+ await validateOpenAI(answers.OPENAI_API_KEY);
39345
+ println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
39346
+ writeAnswersTo(target, answers);
39347
+ println("");
39348
+ println(c.green(`✓ Wrote ${target}`));
39349
+ println("");
39350
+ await postWriteLifecycle(target, options);
39162
39351
  }
39163
39352
  function registerInit(program2) {
39164
39353
  program2.command("init").description("Interactive first-run setup (config, schema deploy stub, optional MCP wiring).").option("-c, --config <file>", "Non-interactive mode: read answers from a JSON file.").option("--force", "Overwrite existing configuration without prompting.").option("--skip-schema", "Skip the schema deploy step.").option("--skip-self-docs", "Skip the bundled self-doc ingest.").option("--skip-agent-config", "Skip the optional MCP agent wiring.").action(action14);
@@ -39436,13 +39625,13 @@ function registerReindex(program2) {
39436
39625
  init_cli_core();
39437
39626
  init_client();
39438
39627
  import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
39439
- import { homedir as homedir5 } from "node:os";
39440
- import { join as join7, resolve as resolve3 } from "node:path";
39628
+ import { homedir as homedir6 } from "node:os";
39629
+ import { join as join8, resolve as resolve3 } from "node:path";
39441
39630
  function expandHome2(path) {
39442
39631
  if (path === "~")
39443
- return homedir5();
39632
+ return homedir6();
39444
39633
  if (path.startsWith("~/"))
39445
- return join7(homedir5(), path.slice(2));
39634
+ return join8(homedir6(), path.slice(2));
39446
39635
  return path;
39447
39636
  }
39448
39637
  function resolveBackupFile(target) {
@@ -39453,11 +39642,11 @@ function resolveBackupFile(target) {
39453
39642
  const stat = statSync4(path);
39454
39643
  if (stat.isFile())
39455
39644
  return path;
39456
- const candidates = readdirSync3(path).filter((n) => n.endsWith(".json") && n.startsWith("cerefox-")).map((n) => ({ name: n, mtime: statSync4(join7(path, n)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
39645
+ const candidates = readdirSync3(path).filter((n) => n.endsWith(".json") && n.startsWith("cerefox-")).map((n) => ({ name: n, mtime: statSync4(join8(path, n)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
39457
39646
  if (candidates.length === 0) {
39458
39647
  throw userError(`No cerefox-*.json files in ${path}`);
39459
39648
  }
39460
- return join7(path, candidates[0].name);
39649
+ return join8(path, candidates[0].name);
39461
39650
  }
39462
39651
  async function action20(target, options) {
39463
39652
  const file = resolveBackupFile(target);
@@ -39824,14 +40013,14 @@ import {
39824
40013
  readdirSync as readdirSync4,
39825
40014
  statSync as statSync5
39826
40015
  } from "node:fs";
39827
- import { basename as basename4, extname as extname4, join as join8, relative } from "node:path";
40016
+ import { basename as basename4, extname as extname4, join as join9, relative } from "node:path";
39828
40017
  var ROOT_LEVEL_DOCS = ["README.md", "AGENT_GUIDE.md", "AGENT_QUICK_REFERENCE.md"];
39829
40018
  function walkMarkdown(dir) {
39830
40019
  const out = [];
39831
40020
  if (!existsSync8(dir))
39832
40021
  return out;
39833
40022
  for (const name of readdirSync4(dir)) {
39834
- const full = join8(dir, name);
40023
+ const full = join9(dir, name);
39835
40024
  let stat;
39836
40025
  try {
39837
40026
  stat = statSync5(full);
@@ -39851,11 +40040,11 @@ async function action24(options) {
39851
40040
  const project = options.project ?? "cerefox";
39852
40041
  const targets = [];
39853
40042
  for (const rel of ROOT_LEVEL_DOCS) {
39854
- const abs = join8(cwd, rel);
40043
+ const abs = join9(cwd, rel);
39855
40044
  if (existsSync8(abs))
39856
40045
  targets.push({ abs, rel });
39857
40046
  }
39858
- for (const abs of walkMarkdown(join8(cwd, "docs"))) {
40047
+ for (const abs of walkMarkdown(join9(cwd, "docs"))) {
39859
40048
  targets.push({ abs, rel: relative(cwd, abs) });
39860
40049
  }
39861
40050
  if (targets.length === 0) {
@@ -112,19 +112,22 @@ The local Cerefox MCP server runs on your machine and exposes the same 10 tools
112
112
  Edge Function, communicating with clients over stdio.
113
113
 
114
114
  As of **v0.4.0** the local server ships as an npm package — **[`@cerefox/memory`](https://www.npmjs.com/package/@cerefox/memory)** — built with the official `@modelcontextprotocol/sdk`.
115
- The bin entry is `cerefox` (run as `cerefox mcp`). The recommended client config is `npx -y --package=@cerefox/memory cerefox mcp`.
115
+ The bin entry is `cerefox` (run as `cerefox mcp`). The recommended client config is `npx -y --package=@cerefox/memory cerefox mcp`, or if you've installed the package globally, just `cerefox mcp`.
116
116
 
117
- The legacy `uv run cerefox mcp` invocation **still works** and is preserved as a soft
118
- wrapper: it tries `npx --no-install @cerefox/memory cerefox mcp` first and falls back to the
119
- Python MCP server if npm is unavailable or `@cerefox/memory` isn't installed. New users
120
- should prefer the npm-native config; existing users don't have to change anything.
117
+ The Python `uv run cerefox mcp` invocation **still works** and remains the right choice if
118
+ you've installed Cerefox from a source checkout. v0.4 through v0.5.1 advertised a
119
+ "soft wrapper" that tried to auto-delegate to the npm package, but the probe was unreliable
120
+ under MCP-client launch environments v0.5.2 removed it. The two paths (Python via
121
+ `uv run`, TS via `cerefox mcp` on PATH or via `npx`) are now **fully independent**.
122
+ Pick one explicitly in your MCP client config.
121
123
 
122
124
  - Embeddings are computed locally using your `.env` key (no extra credentials)
123
125
  - Works offline except for the OpenAI embedding API call per query
124
126
  - One setup, all compatible local clients (Claude Desktop, Cursor, Claude Code, Codex CLI, …)
125
127
 
126
- See [`docs/guides/migration-v0.4.md`](migration-v0.4.md) for before/after config snippets
127
- per client.
128
+ See [`docs/guides/migration-v0.5.md`](migration-v0.5.md) for the per-client config
129
+ snippets, the v0.5.2 soft-wrapper removal explainer, and the v0.5.3 `.env` location
130
+ change.
128
131
 
129
132
  > **Why not `mcp-server-fetch`?** The generic fetch MCP only supports GET requests and cannot
130
133
  > make authenticated POST calls to the Edge Functions. The built-in local server is
@@ -1,6 +1,23 @@
1
- # Migrating to Cerefox v0.4.0
2
-
3
- **TL;DR**: nothing urgent. Your existing `cerefox mcp` configs keep
1
+ # Migrating to Cerefox v0.4.0 (historical)
2
+
3
+ > ## Historical document do not use the snippets in this file
4
+ >
5
+ > This guide documents the **v0.4.0 → v0.4.3 migration window** (May 2026).
6
+ > The `cerefox-mcp` bin name referenced throughout was dropped in **v0.5.1**;
7
+ > the soft-wrapper described in some sections was removed in **v0.5.2**.
8
+ > The per-client config snippets below **will not work on @cerefox/memory v0.5+**.
9
+ >
10
+ > **If you're upgrading today, use the current guide instead:**
11
+ > → [`migration-v0.5.md`](migration-v0.5.md) — covers Python `cerefox` → v0.5.x
12
+ > AND v0.4.x → v0.5.x in a single document, with the v0.5.0/v0.5.1/v0.5.2/v0.5.3
13
+ > transitions all explained.
14
+ >
15
+ > This file is preserved so historical CHANGELOG entries that reference it
16
+ > still resolve. It's not maintained.
17
+
18
+ ---
19
+
20
+ **Original TL;DR (preserved verbatim)**: nothing urgent. Your existing `cerefox mcp` configs keep
4
21
  working unchanged. The Python `cerefox mcp` command is now a soft
5
22
  wrapper that transparently uses the new TypeScript MCP server if it's
6
23
  installed, falling back to the legacy Python implementation otherwise.
@@ -1,4 +1,22 @@
1
- # Migrating to Cerefox v0.5.0
1
+ # Migrating to Cerefox v0.5.x
2
+
3
+ **This is the canonical upgrade guide for any user landing on Cerefox v0.5+.**
4
+ It covers the v0.4 → v0.5 transition, the v0.5.x patch trail (v0.5.1, v0.5.2,
5
+ v0.5.3), and the Python `cerefox` → TS `cerefox` migration path.
6
+
7
+ ## Where to start
8
+
9
+ | Coming from | Read |
10
+ |---|---|
11
+ | Never used Cerefox before | [`quickstart.md`](quickstart.md) first, then come back here only if you hit a `.env` / config question |
12
+ | Python `cerefox` (any version through v0.5.x) | "What changed" → "Install paths" → "v0.5.3 migrated `.env`" sections below |
13
+ | `@cerefox/memory` v0.4.x (npm) | "Upgrading an existing MCP client config" → "v0.5.2 fixed the soft wrapper" → "v0.5.3 migrated `.env`" |
14
+ | `@cerefox/memory` v0.5.0 or v0.5.1 (npm) | "v0.5.2 fixed the soft wrapper" + "v0.5.3 migrated `.env`" |
15
+ | `@cerefox/memory` v0.5.2 (npm) | "v0.5.3 migrated `.env`" — the rest is unchanged |
16
+
17
+ > Looking for `migration-v0.4.md`? It's been demoted to a historical
18
+ > record (the bin names it documents no longer exist). Everything you
19
+ > need to know about the v0.4 → v0.5 transition lives in this file.
2
20
 
3
21
  **TL;DR:** the Cerefox CLI is now a TypeScript binary published to npm.
4
22
  You can keep using the Python CLI through v0.7.x (it just prints a
@@ -196,6 +214,64 @@ explicit instead of "magic delegation".
196
214
 
197
215
  ---
198
216
 
217
+ ## v0.5.3 migrated `.env` from `<repo>/.env` to `~/.cerefox/.env`
218
+
219
+ If you've been using the Python `cerefox` CLI, your `.env` lives in your
220
+ repo root (`/path/to/cerefox/.env`). The TS CLI v0.5.2 also read that
221
+ file, via a "CWD `.env` wins" precedence inherited from Python. v0.5.3
222
+ flips that precedence: **once `~/.cerefox/.env` exists, the TS CLI reads
223
+ from there**; the repo file becomes a legacy fallback for Python's
224
+ `uv run cerefox …` workflows.
225
+
226
+ **You see zero behavior change until you run `cerefox init`.** If your
227
+ home dir doesn't have `~/.cerefox/.env`, the TS CLI keeps reading your
228
+ existing repo `.env` (legacy dev-mode precedence). No action required.
229
+
230
+ When you do run `cerefox init` with a repo `.env` already in place, the
231
+ TS CLI offers a three-choice menu:
232
+
233
+ ```
234
+ ⚠ Found existing config at /path/to/cerefox/.env.
235
+
236
+ [c] Copy to /Users/you/.cerefox/.env (recommended)
237
+ • TS reads the new home from now on
238
+ • Python keeps reading /path/to/cerefox/.env (backward compat)
239
+ • Edit ~/.cerefox/.env going forward; the repo .env is legacy
240
+
241
+ [u] Use /path/to/cerefox/.env as-is, skip writing anything
242
+ • Both TS and Python keep reading the existing file
243
+ • Defer the migration
244
+
245
+ [f] Fresh start — interactive prompts, write to /Users/you/.cerefox/.env
246
+ • Use if the existing file is stale or wrong
247
+ ```
248
+
249
+ Pick **[c]** for the typical Python → TS upgrade. The TS CLI starts
250
+ reading `~/.cerefox/.env`; your remaining Python `uv run cerefox …`
251
+ commands keep reading the unchanged repo file. The two files diverge
252
+ only if you start editing one of them — keep them in sync (or just edit
253
+ `~/.cerefox/.env` and accept that Python uses a frozen snapshot until
254
+ v0.9).
255
+
256
+ After v0.9 (Python CLI removed), `cerefox doctor` will say "ok" if you
257
+ delete the repo file. Until then, `doctor` reports it as
258
+ `legacy env … (shadowed by ~/.cerefox/.env)` so you know it's harmless.
259
+
260
+ ### Python paths.py precedence (unchanged)
261
+
262
+ `src/cerefox/paths.py` keeps the v0.5.2 precedence (CWD `.env` wins).
263
+ Your existing `uv run cerefox …` invocations from inside the repo
264
+ continue to read the repo file regardless of what's in `~/.cerefox/`.
265
+ When this module goes away in v0.9+, the divergence resolves naturally.
266
+
267
+ ### `CEREFOX_CONFIG_DIR` is unchanged
268
+
269
+ If you have `CEREFOX_CONFIG_DIR` set (e.g. for a non-standard install),
270
+ it still wins over both home and repo `.env` files. Init writes there
271
+ and skips the migration prompt.
272
+
273
+ ---
274
+
199
275
  ## Known gotchas
200
276
 
201
277
  ### `npx` from inside an npm workspace
@@ -1,8 +1,27 @@
1
1
  # Upgrading Cerefox
2
2
 
3
- This guide covers upgrading an existing Cerefox installation to the latest version. All steps are idempotent and safe to re-run.
3
+ This guide covers upgrading an existing Cerefox installation. All steps are idempotent and safe to re-run.
4
4
 
5
- ## Standard Upgrade Checklist
5
+ ## Pick your path
6
+
7
+ Cerefox has two install paths since v0.4.0 (npm) and v0.5.0 (TS CLI). The right
8
+ upgrade procedure depends on how you installed Cerefox:
9
+
10
+ | You installed via | Upgrade with |
11
+ |---|---|
12
+ | **npm / Bun** (`@cerefox/memory` global) | `bun update -g @cerefox/memory` (or `npm update -g @cerefox/memory`), then read [`migration-v0.5.md`](migration-v0.5.md) for any breaking-change notes per version. **`cerefox doctor`** verifies the install. |
13
+ | **Source checkout** (`git clone` + `uv sync`) | The "Standard Upgrade Checklist" below — Python deps, schema migrations, Edge Functions, frontend build, the works. |
14
+ | **Both** (you contribute AND have the npm bin globally) | Both flows. The two paths share the same Supabase + `.env`; just keep them updated in lockstep. |
15
+
16
+ > If you're upgrading from Python `cerefox` to the npm-installed TS CLI for the first
17
+ > time, [`migration-v0.5.md`](migration-v0.5.md) is the canonical guide — it covers
18
+ > `cerefox init`'s coexistence flow (`[c]opy` your existing `.env` to `~/.cerefox/.env`),
19
+ > the v0.5.2 soft-wrapper removal, and the v0.5.3 paths precedence change.
20
+
21
+ The rest of this document covers the **source checkout** path (Python + frontend + Edge
22
+ Functions). If you're an npm-installed user, you've already got everything you need.
23
+
24
+ ## Standard Upgrade Checklist (source-checkout users)
6
25
 
7
26
  Run these steps every time you pull a new version:
8
27
 
@@ -61,6 +80,30 @@ open http://localhost:8000/app/
61
80
 
62
81
  Most upgrades require no special steps beyond the standard checklist above. Notes below only apply when upgrading across specific version boundaries.
63
82
 
83
+ ### Upgrading to v0.5.x (from any v0.4.x or earlier)
84
+
85
+ The v0.4 → v0.5 transition is a milestone — the CLI itself moved from Python
86
+ to TypeScript. The full migration guide is [`migration-v0.5.md`](migration-v0.5.md);
87
+ the short version for source-checkout users is:
88
+
89
+ - The Python `cerefox` CLI **still works** through v0.7.x — `uv sync` is enough
90
+ to pull it. It prints a one-line ⚠ deprecation banner on every invocation.
91
+ - The new npm CLI lives alongside: `bun install -g @cerefox/memory` (or
92
+ `npm install -g @cerefox/memory`). `cerefox doctor` from any directory will
93
+ verify it.
94
+ - **v0.5.2** stripped the Python `cerefox mcp` soft-wrapper. If your MCP
95
+ client config uses `uv run --directory /path/to/cerefox cerefox mcp`,
96
+ nothing changes — that path runs the Python MCP server directly. If you
97
+ want the TS server instead, point your client at `cerefox mcp`
98
+ (npm-installed) or `npx -y --package=@cerefox/memory cerefox mcp`.
99
+ - **v0.5.3** changed the TS CLI's `.env` precedence: `~/.cerefox/.env` now
100
+ wins over `<repo>/.env` when both exist. Your existing `<repo>/.env` keeps
101
+ working until you run `cerefox init` and pick the `[c]opy` migration
102
+ option. Python `paths.py` is unchanged.
103
+
104
+ No schema migration, no Edge Function redeploy, no chunk reindex required
105
+ for the v0.4 → v0.5.3 arc.
106
+
64
107
  ### Upgrading to v0.1.20 (from v0.1.19) -- Multi-Project Preservation Fix
65
108
 
66
109
  **Edge Function redeploy is required.** v0.1.20 fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerefox/memory",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Cerefox — user-owned shared memory for AI agents. The local TypeScript runtime: stdio MCP server in v0.4; CLI binary added in v0.5; in-process web server in v0.6; ingestion pipeline in v0.7.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/fstamatelopoulos/cerefox",