@cerefox/memory 0.5.2 → 0.5.4
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 +8 -0
- package/dist/bin/cerefox.js +346 -77
- package/docs/guides/connect-agents.md +10 -7
- package/docs/guides/migration-v0.4.md +20 -3
- package/docs/guides/migration-v0.5.md +136 -1
- package/docs/guides/quickstart.md +63 -4
- package/docs/guides/upgrading.md +45 -2
- package/package.json +1 -1
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
|
```
|
package/dist/bin/cerefox.js
CHANGED
|
@@ -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.
|
|
7182
|
+
var PKG_VERSION = "0.5.4";
|
|
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
|
|
22513
|
+
return userState;
|
|
22507
22514
|
}
|
|
22508
22515
|
function resolveEnvFile(opts = {}) {
|
|
22509
22516
|
return join(resolveConfigDir(opts), ".env");
|
|
22510
22517
|
}
|
|
22511
|
-
function userStateDir() {
|
|
22512
|
-
return
|
|
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
|
}
|
|
@@ -24728,7 +24737,7 @@ __export(exports_sync_self_docs, {
|
|
|
24728
24737
|
runSyncSelfDocs: () => runSyncSelfDocs,
|
|
24729
24738
|
registerSyncSelfDocs: () => registerSyncSelfDocs
|
|
24730
24739
|
});
|
|
24731
|
-
import { readFileSync as
|
|
24740
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
24732
24741
|
import { basename as basename3, extname as extname3 } from "node:path";
|
|
24733
24742
|
async function runSyncSelfDocs(options = {}) {
|
|
24734
24743
|
const project = options.project ?? "_cerefox-self-docs";
|
|
@@ -24755,7 +24764,7 @@ async function runSyncSelfDocs(options = {}) {
|
|
|
24755
24764
|
const authorType = resolveAuthorType("agent");
|
|
24756
24765
|
const outcomes = [];
|
|
24757
24766
|
for (const doc of docs) {
|
|
24758
|
-
const content =
|
|
24767
|
+
const content = readFileSync7(doc.path, "utf8");
|
|
24759
24768
|
const m = content.match(/^#\s+(.+)$/m);
|
|
24760
24769
|
const title = m ? m[1].trim() : basename3(doc.path, extname3(doc.path));
|
|
24761
24770
|
try {
|
|
@@ -38135,17 +38144,24 @@ function registerConfigSet(program2) {
|
|
|
38135
38144
|
init_cli_core();
|
|
38136
38145
|
|
|
38137
38146
|
// src/cli/util/mcp-config-writers.ts
|
|
38138
|
-
import {
|
|
38147
|
+
import {
|
|
38148
|
+
copyFileSync,
|
|
38149
|
+
existsSync as existsSync3,
|
|
38150
|
+
mkdirSync as mkdirSync2,
|
|
38151
|
+
readFileSync as readFileSync2,
|
|
38152
|
+
writeFileSync as writeFileSync2
|
|
38153
|
+
} from "node:fs";
|
|
38139
38154
|
import { homedir as homedir3, platform } from "node:os";
|
|
38140
38155
|
import { dirname, join as join3 } from "node:path";
|
|
38156
|
+
import { spawnSync } from "node:child_process";
|
|
38141
38157
|
function defaultCerefoxEntry() {
|
|
38142
38158
|
return {
|
|
38143
38159
|
command: "npx",
|
|
38144
38160
|
args: ["-y", "--package=@cerefox/memory", "cerefox", "mcp"]
|
|
38145
38161
|
};
|
|
38146
38162
|
}
|
|
38147
|
-
function
|
|
38148
|
-
return join3(homedir3(), ".claude
|
|
38163
|
+
function claudeCodeUserConfigPath() {
|
|
38164
|
+
return join3(homedir3(), ".claude.json");
|
|
38149
38165
|
}
|
|
38150
38166
|
function claudeDesktopConfigPath() {
|
|
38151
38167
|
const home = homedir3();
|
|
@@ -38157,22 +38173,40 @@ function claudeDesktopConfigPath() {
|
|
|
38157
38173
|
}
|
|
38158
38174
|
return join3(home, ".config", "Claude", "claude_desktop_config.json");
|
|
38159
38175
|
}
|
|
38176
|
+
function claudeCodeDelegated() {
|
|
38177
|
+
const entry = defaultCerefoxEntry();
|
|
38178
|
+
return {
|
|
38179
|
+
cmd: "claude",
|
|
38180
|
+
args: ["mcp", "add", "cerefox", "--scope", "user", "--", entry.command, ...entry.args]
|
|
38181
|
+
};
|
|
38182
|
+
}
|
|
38160
38183
|
var WRITERS = {
|
|
38161
38184
|
"claude-code": {
|
|
38162
38185
|
id: "claude-code",
|
|
38163
38186
|
label: "Claude Code",
|
|
38164
|
-
|
|
38165
|
-
|
|
38187
|
+
kind: "delegated",
|
|
38188
|
+
configPath: claudeCodeUserConfigPath(),
|
|
38189
|
+
buildServerEntry: defaultCerefoxEntry,
|
|
38190
|
+
delegated: claudeCodeDelegated
|
|
38166
38191
|
},
|
|
38167
38192
|
"claude-desktop": {
|
|
38168
38193
|
id: "claude-desktop",
|
|
38169
38194
|
label: "Claude Desktop",
|
|
38195
|
+
kind: "direct-write",
|
|
38170
38196
|
configPath: claudeDesktopConfigPath(),
|
|
38171
38197
|
buildServerEntry: defaultCerefoxEntry
|
|
38172
38198
|
}
|
|
38173
38199
|
};
|
|
38174
38200
|
function writeMcpConfig(writer, opts = {}) {
|
|
38175
|
-
|
|
38201
|
+
if (opts.customPath) {
|
|
38202
|
+
return directWrite({ ...writer, kind: "direct-write" }, opts.customPath, opts);
|
|
38203
|
+
}
|
|
38204
|
+
if (writer.kind === "delegated") {
|
|
38205
|
+
return delegatedWrite(writer, opts);
|
|
38206
|
+
}
|
|
38207
|
+
return directWrite(writer, writer.configPath, opts);
|
|
38208
|
+
}
|
|
38209
|
+
function directWrite(writer, configPath, opts) {
|
|
38176
38210
|
const entry = writer.buildServerEntry();
|
|
38177
38211
|
if (!opts.dryRun)
|
|
38178
38212
|
mkdirSync2(dirname(configPath), { recursive: true });
|
|
@@ -38200,6 +38234,46 @@ function writeMcpConfig(writer, opts = {}) {
|
|
|
38200
38234
|
}
|
|
38201
38235
|
return { configPath, backupPath, action: action5, serverEntry: entry };
|
|
38202
38236
|
}
|
|
38237
|
+
function delegatedWrite(writer, opts) {
|
|
38238
|
+
if (!writer.delegated) {
|
|
38239
|
+
throw new Error(`${writer.label}: kind=delegated but no delegated() factory`);
|
|
38240
|
+
}
|
|
38241
|
+
const { cmd, args } = writer.delegated();
|
|
38242
|
+
const entry = writer.buildServerEntry();
|
|
38243
|
+
const delegatedCommand = `${cmd} ${args.join(" ")}`;
|
|
38244
|
+
if (opts.dryRun) {
|
|
38245
|
+
return {
|
|
38246
|
+
configPath: writer.configPath,
|
|
38247
|
+
backupPath: null,
|
|
38248
|
+
action: "delegated",
|
|
38249
|
+
serverEntry: entry,
|
|
38250
|
+
delegatedCommand
|
|
38251
|
+
};
|
|
38252
|
+
}
|
|
38253
|
+
let backupPath = null;
|
|
38254
|
+
if (!opts.noBackup && existsSync3(writer.configPath)) {
|
|
38255
|
+
backupPath = writer.configPath + ".pre-cerefox.bak";
|
|
38256
|
+
copyFileSync(writer.configPath, backupPath);
|
|
38257
|
+
}
|
|
38258
|
+
const result = spawnSync(cmd, args, { stdio: "inherit" });
|
|
38259
|
+
if (result.error) {
|
|
38260
|
+
const err = result.error;
|
|
38261
|
+
if (err.code === "ENOENT") {
|
|
38262
|
+
throw new Error(`${writer.label}: \`${cmd}\` not found on PATH. ` + `Install ${writer.label} (https://docs.claude.com/en/docs/claude-code) ` + `and re-run \`cerefox configure-agent --tool ${writer.id}\`.`);
|
|
38263
|
+
}
|
|
38264
|
+
throw new Error(`${writer.label}: failed to spawn \`${cmd}\`: ${err.message}`);
|
|
38265
|
+
}
|
|
38266
|
+
if (result.status !== 0) {
|
|
38267
|
+
throw new Error(`${writer.label}: \`${delegatedCommand}\` exited with status ${result.status ?? "unknown"}. ` + `Check the output above; you may need to update or re-authenticate ${writer.label}.`);
|
|
38268
|
+
}
|
|
38269
|
+
return {
|
|
38270
|
+
configPath: writer.configPath,
|
|
38271
|
+
backupPath,
|
|
38272
|
+
action: "delegated",
|
|
38273
|
+
serverEntry: entry,
|
|
38274
|
+
delegatedCommand
|
|
38275
|
+
};
|
|
38276
|
+
}
|
|
38203
38277
|
|
|
38204
38278
|
// src/cli/commands/configure-agent.ts
|
|
38205
38279
|
function action5(options) {
|
|
@@ -38226,12 +38300,15 @@ function action5(options) {
|
|
|
38226
38300
|
println(c.dim(` backup: ${result.backupPath}`));
|
|
38227
38301
|
}
|
|
38228
38302
|
println(c.dim(` action: ${result.action}`));
|
|
38303
|
+
if (result.delegatedCommand) {
|
|
38304
|
+
println(c.dim(` invoked: ${result.delegatedCommand}`));
|
|
38305
|
+
}
|
|
38229
38306
|
println("");
|
|
38230
38307
|
println(c.bold("Server entry written:"));
|
|
38231
38308
|
println(JSON.stringify(result.serverEntry, null, 2));
|
|
38232
38309
|
if (!options.dryRun) {
|
|
38233
38310
|
println("");
|
|
38234
|
-
println(c.dim(writer.id === "claude-desktop" ? "Restart Claude Desktop fully (Cmd+Q on macOS) to pick up the new server." : "
|
|
38311
|
+
println(c.dim(writer.id === "claude-desktop" ? "Restart Claude Desktop fully (Cmd+Q on macOS) to pick up the new server." : "Start a new Claude Code session to pick up the new server " + "(running sessions cache MCP server lists at startup)."));
|
|
38235
38312
|
}
|
|
38236
38313
|
}
|
|
38237
38314
|
function registerConfigureAgent(program2) {
|
|
@@ -38286,10 +38363,10 @@ function registerDeleteDoc(program2) {
|
|
|
38286
38363
|
// src/cli/commands/docs.ts
|
|
38287
38364
|
init_cli_core();
|
|
38288
38365
|
init_bundled_docs();
|
|
38289
|
-
import { spawnSync } from "node:child_process";
|
|
38366
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
38290
38367
|
function openInBrowser(path) {
|
|
38291
38368
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
38292
|
-
const result =
|
|
38369
|
+
const result = spawnSync2(cmd, [path], { stdio: "ignore" });
|
|
38293
38370
|
if (result.status !== 0) {
|
|
38294
38371
|
println(c.dim(`(could not auto-open; the file is at: ${path})`));
|
|
38295
38372
|
}
|
|
@@ -38333,7 +38410,7 @@ init_cli_core();
|
|
|
38333
38410
|
init_meta();
|
|
38334
38411
|
init_config();
|
|
38335
38412
|
init_config();
|
|
38336
|
-
import { existsSync as existsSync5, statSync as statSync2 } from "node:fs";
|
|
38413
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync, statSync as statSync2 } from "node:fs";
|
|
38337
38414
|
import { homedir as homedir4 } from "node:os";
|
|
38338
38415
|
import { join as join5 } from "node:path";
|
|
38339
38416
|
function checkBinary() {
|
|
@@ -38552,29 +38629,58 @@ async function checkSchemaVersion() {
|
|
|
38552
38629
|
};
|
|
38553
38630
|
}
|
|
38554
38631
|
}
|
|
38632
|
+
function hasCerefoxInJsonFile(path) {
|
|
38633
|
+
if (!existsSync5(path))
|
|
38634
|
+
return false;
|
|
38635
|
+
try {
|
|
38636
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
38637
|
+
const mcpServers = parsed.mcpServers;
|
|
38638
|
+
return Boolean(mcpServers && typeof mcpServers === "object" && "cerefox" in mcpServers);
|
|
38639
|
+
} catch {
|
|
38640
|
+
return false;
|
|
38641
|
+
}
|
|
38642
|
+
}
|
|
38555
38643
|
function checkMcpConfigs() {
|
|
38556
38644
|
const home = homedir4();
|
|
38557
|
-
const
|
|
38558
|
-
|
|
38559
|
-
|
|
38560
|
-
|
|
38561
|
-
|
|
38562
|
-
|
|
38563
|
-
|
|
38564
|
-
|
|
38565
|
-
|
|
38645
|
+
const claudeCodeUser = join5(home, ".claude.json");
|
|
38646
|
+
const claudeCodeProj = join5(process.cwd(), ".mcp.json");
|
|
38647
|
+
const claudeDesktop = process.platform === "darwin" ? join5(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : process.platform === "win32" ? join5(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json") : join5(home, ".config", "Claude", "claude_desktop_config.json");
|
|
38648
|
+
const found = [];
|
|
38649
|
+
if (hasCerefoxInJsonFile(claudeCodeUser))
|
|
38650
|
+
found.push("Claude Code (user)");
|
|
38651
|
+
if (hasCerefoxInJsonFile(claudeCodeProj))
|
|
38652
|
+
found.push("Claude Code (proj)");
|
|
38653
|
+
if (hasCerefoxInJsonFile(claudeDesktop))
|
|
38654
|
+
found.push("Claude Desktop");
|
|
38566
38655
|
if (found.length === 0) {
|
|
38567
38656
|
return {
|
|
38568
38657
|
name: "mcp clients",
|
|
38569
38658
|
status: "warn",
|
|
38570
|
-
detail: "No MCP client configs
|
|
38571
|
-
hint: "Run `cerefox configure-agent --tool claude-code` to wire up
|
|
38659
|
+
detail: "No MCP client configs reference Cerefox.",
|
|
38660
|
+
hint: "Run `cerefox configure-agent --tool claude-code` (or `--tool claude-desktop`) to wire up a client."
|
|
38572
38661
|
};
|
|
38573
38662
|
}
|
|
38574
38663
|
return {
|
|
38575
38664
|
name: "mcp clients",
|
|
38576
38665
|
status: "ok",
|
|
38577
|
-
detail: found.
|
|
38666
|
+
detail: found.join(", ")
|
|
38667
|
+
};
|
|
38668
|
+
}
|
|
38669
|
+
function checkLegacyShadowEnv() {
|
|
38670
|
+
const home = homedir4();
|
|
38671
|
+
const homeEnv = join5(home, USER_STATE_DIR_NAME, ".env");
|
|
38672
|
+
const cwdEnv = join5(process.cwd(), ".env");
|
|
38673
|
+
if (!existsSync5(homeEnv) || !existsSync5(cwdEnv))
|
|
38674
|
+
return null;
|
|
38675
|
+
try {
|
|
38676
|
+
if (realpathSync(homeEnv) === realpathSync(cwdEnv))
|
|
38677
|
+
return null;
|
|
38678
|
+
} catch {}
|
|
38679
|
+
return {
|
|
38680
|
+
name: "legacy env",
|
|
38681
|
+
status: "skipped",
|
|
38682
|
+
detail: `${cwdEnv} (shadowed by ~/.cerefox/.env)`,
|
|
38683
|
+
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+)."
|
|
38578
38684
|
};
|
|
38579
38685
|
}
|
|
38580
38686
|
function checkPostgres() {
|
|
@@ -38593,11 +38699,13 @@ function checkPostgres() {
|
|
|
38593
38699
|
};
|
|
38594
38700
|
}
|
|
38595
38701
|
async function runAllChecks() {
|
|
38702
|
+
const legacy = checkLegacyShadowEnv();
|
|
38596
38703
|
return [
|
|
38597
38704
|
checkBinary(),
|
|
38598
38705
|
checkRuntime(),
|
|
38599
38706
|
checkVersion(),
|
|
38600
38707
|
checkConfig(),
|
|
38708
|
+
...legacy ? [legacy] : [],
|
|
38601
38709
|
await checkSupabase(),
|
|
38602
38710
|
await checkOpenAI(),
|
|
38603
38711
|
await checkSchemaVersion(),
|
|
@@ -38738,7 +38846,7 @@ init_cli_core();
|
|
|
38738
38846
|
init_mcp_tools();
|
|
38739
38847
|
init_config();
|
|
38740
38848
|
init_client();
|
|
38741
|
-
import { readFileSync as
|
|
38849
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
38742
38850
|
import { basename, extname } from "node:path";
|
|
38743
38851
|
async function readContent(path, paste) {
|
|
38744
38852
|
if (paste) {
|
|
@@ -38760,7 +38868,7 @@ async function readContent(path, paste) {
|
|
|
38760
38868
|
}
|
|
38761
38869
|
let content;
|
|
38762
38870
|
try {
|
|
38763
|
-
content =
|
|
38871
|
+
content = readFileSync5(path, "utf8");
|
|
38764
38872
|
} catch (err) {
|
|
38765
38873
|
const msg = err instanceof Error ? err.message : String(err);
|
|
38766
38874
|
throw userError(`Cannot read ${path}: ${msg}`);
|
|
@@ -38825,7 +38933,7 @@ init_mcp_tools();
|
|
|
38825
38933
|
init_config();
|
|
38826
38934
|
init_client();
|
|
38827
38935
|
var import_cli_progress = __toESM(require_cli_progress(), 1);
|
|
38828
|
-
import { readFileSync as
|
|
38936
|
+
import { readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
|
|
38829
38937
|
import { basename as basename2, extname as extname2, join as join6 } from "node:path";
|
|
38830
38938
|
function walk(dir, extensions) {
|
|
38831
38939
|
let entries;
|
|
@@ -38881,7 +38989,7 @@ async function action12(dir, options) {
|
|
|
38881
38989
|
bar?.update({ file: basename2(file) });
|
|
38882
38990
|
let content;
|
|
38883
38991
|
try {
|
|
38884
|
-
content =
|
|
38992
|
+
content = readFileSync6(file, "utf8");
|
|
38885
38993
|
} catch (err) {
|
|
38886
38994
|
outcomes.push({ file, status: "error", detail: `read failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
38887
38995
|
bar?.increment();
|
|
@@ -38938,19 +39046,21 @@ init_cli_core();
|
|
|
38938
39046
|
init_config();
|
|
38939
39047
|
import {
|
|
38940
39048
|
chmodSync,
|
|
39049
|
+
copyFileSync as copyFileSync2,
|
|
38941
39050
|
existsSync as existsSync6,
|
|
38942
39051
|
mkdirSync as mkdirSync3,
|
|
38943
|
-
readFileSync as
|
|
39052
|
+
readFileSync as readFileSync8,
|
|
38944
39053
|
writeFileSync as writeFileSync3
|
|
38945
39054
|
} from "node:fs";
|
|
38946
|
-
import {
|
|
39055
|
+
import { homedir as homedir5 } from "node:os";
|
|
39056
|
+
import { dirname as dirname3, join as join7 } from "node:path";
|
|
38947
39057
|
async function readConfigFile(path) {
|
|
38948
39058
|
if (!existsSync6(path)) {
|
|
38949
39059
|
throw userError(`--config file not found: ${path}`);
|
|
38950
39060
|
}
|
|
38951
39061
|
let parsed;
|
|
38952
39062
|
try {
|
|
38953
|
-
parsed = JSON.parse(
|
|
39063
|
+
parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
38954
39064
|
} catch (err) {
|
|
38955
39065
|
throw userError(`--config: invalid JSON in ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
38956
39066
|
}
|
|
@@ -38973,6 +39083,41 @@ async function readConfigFile(path) {
|
|
|
38973
39083
|
CEREFOX_AUTHOR_TYPE: typeof obj.CEREFOX_AUTHOR_TYPE === "string" ? obj.CEREFOX_AUTHOR_TYPE : undefined
|
|
38974
39084
|
};
|
|
38975
39085
|
}
|
|
39086
|
+
function parseDotEnvFile(content) {
|
|
39087
|
+
const map = {};
|
|
39088
|
+
for (const rawLine of content.split(/\r?\n/)) {
|
|
39089
|
+
const line = rawLine.trim();
|
|
39090
|
+
if (!line || line.startsWith("#"))
|
|
39091
|
+
continue;
|
|
39092
|
+
const eqIdx = line.indexOf("=");
|
|
39093
|
+
if (eqIdx === -1)
|
|
39094
|
+
continue;
|
|
39095
|
+
const key = line.slice(0, eqIdx).trim();
|
|
39096
|
+
let value = line.slice(eqIdx + 1).trim();
|
|
39097
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
39098
|
+
value = value.slice(1, -1);
|
|
39099
|
+
}
|
|
39100
|
+
map[key] = value;
|
|
39101
|
+
}
|
|
39102
|
+
return map;
|
|
39103
|
+
}
|
|
39104
|
+
function answersFromEnvFile(path) {
|
|
39105
|
+
const parsed = parseDotEnvFile(readFileSync8(path, "utf8"));
|
|
39106
|
+
const required = ["CEREFOX_SUPABASE_URL", "CEREFOX_SUPABASE_KEY", "OPENAI_API_KEY"];
|
|
39107
|
+
for (const key of required) {
|
|
39108
|
+
if (!parsed[key] || parsed[key].trim() === "") {
|
|
39109
|
+
throw userError(`Existing .env at ${path} is missing required key "${key}".`, `Fix it manually or run \`cerefox init --force\` to start fresh.`);
|
|
39110
|
+
}
|
|
39111
|
+
}
|
|
39112
|
+
return {
|
|
39113
|
+
CEREFOX_SUPABASE_URL: parsed.CEREFOX_SUPABASE_URL,
|
|
39114
|
+
CEREFOX_SUPABASE_KEY: parsed.CEREFOX_SUPABASE_KEY,
|
|
39115
|
+
OPENAI_API_KEY: parsed.OPENAI_API_KEY,
|
|
39116
|
+
CEREFOX_DATABASE_URL: parsed.CEREFOX_DATABASE_URL,
|
|
39117
|
+
CEREFOX_AUTHOR_NAME: parsed.CEREFOX_AUTHOR_NAME,
|
|
39118
|
+
CEREFOX_AUTHOR_TYPE: parsed.CEREFOX_AUTHOR_TYPE
|
|
39119
|
+
};
|
|
39120
|
+
}
|
|
38976
39121
|
async function promptForAnswers() {
|
|
38977
39122
|
println(c.bold("Cerefox first-run setup."));
|
|
38978
39123
|
println(c.dim(`This will write configuration to ~/.cerefox/.env (or CEREFOX_CONFIG_DIR if set).
|
|
@@ -39087,35 +39232,38 @@ async function validateOpenAI(key) {
|
|
|
39087
39232
|
throw userError(`OpenAI key validation failed: ${resp.status} ${body.slice(0, 100)}`, "Verify the key on https://platform.openai.com/api-keys.");
|
|
39088
39233
|
}
|
|
39089
39234
|
}
|
|
39090
|
-
|
|
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();
|
|
39235
|
+
function printMigrationMenu(cwdEnv, homeEnv) {
|
|
39101
39236
|
println("");
|
|
39102
|
-
println(c.
|
|
39103
|
-
|
|
39104
|
-
println(
|
|
39105
|
-
|
|
39106
|
-
println(
|
|
39107
|
-
|
|
39108
|
-
|
|
39109
|
-
|
|
39110
|
-
|
|
39111
|
-
|
|
39112
|
-
|
|
39113
|
-
|
|
39114
|
-
|
|
39115
|
-
}
|
|
39237
|
+
println(c.yellow(`⚠ Found existing config at ${cwdEnv}.`));
|
|
39238
|
+
println("");
|
|
39239
|
+
println("This may be from a previous Python install. The TS CLI can use the");
|
|
39240
|
+
println("same .env — env-var names are identical, no rewrite needed.");
|
|
39241
|
+
println("");
|
|
39242
|
+
println(" " + c.bold("[c]") + " Copy to " + homeEnv + " " + c.green("(recommended)"));
|
|
39243
|
+
println(c.dim(" • TS reads the new home from now on"));
|
|
39244
|
+
println(c.dim(" • Python keeps reading " + cwdEnv + " (backward compat)"));
|
|
39245
|
+
println(c.dim(" • Edit ~/.cerefox/.env going forward; the repo .env is legacy"));
|
|
39246
|
+
println("");
|
|
39247
|
+
println(" " + c.bold("[u]") + " Use " + cwdEnv + " as-is, skip writing anything");
|
|
39248
|
+
println(c.dim(" • Both TS and Python keep reading the existing file"));
|
|
39249
|
+
println(c.dim(" • Defer the migration"));
|
|
39116
39250
|
println("");
|
|
39117
|
-
println(c.
|
|
39251
|
+
println(" " + c.bold("[f]") + " Fresh start — interactive prompts, write to " + homeEnv);
|
|
39252
|
+
println(c.dim(" • Use if the existing file is stale or wrong"));
|
|
39118
39253
|
println("");
|
|
39254
|
+
}
|
|
39255
|
+
async function promptMigrationChoice() {
|
|
39256
|
+
const choice = await ask({
|
|
39257
|
+
type: "text",
|
|
39258
|
+
name: "choice",
|
|
39259
|
+
message: "Choice (c/u/f) [c]",
|
|
39260
|
+
initial: "c",
|
|
39261
|
+
validate: (v) => /^[cuf]?$/i.test(v.trim()) || "Expected c, u, or f."
|
|
39262
|
+
});
|
|
39263
|
+
const ch = choice.trim().toLowerCase() || "c";
|
|
39264
|
+
return ch;
|
|
39265
|
+
}
|
|
39266
|
+
async function postWriteLifecycle(envPath, options) {
|
|
39119
39267
|
if (!options.skipSchema) {
|
|
39120
39268
|
println(c.bold("Schema deploy"));
|
|
39121
39269
|
println(c.dim(` v0.5 doesn't yet bundle the schema-deploy path (it needs the direct
|
|
@@ -39159,6 +39307,127 @@ async function action14(options) {
|
|
|
39159
39307
|
println(c.dim(" cerefox doctor # verify everything"));
|
|
39160
39308
|
println(c.dim(' cerefox search "…" # search the KB'));
|
|
39161
39309
|
println(c.dim(" cerefox ingest <file> # add a doc"));
|
|
39310
|
+
println("");
|
|
39311
|
+
println(c.dim(` Config in effect: ${envPath}`));
|
|
39312
|
+
}
|
|
39313
|
+
function writeAnswersTo(target, answers) {
|
|
39314
|
+
mkdirSync3(dirname3(target), { recursive: true });
|
|
39315
|
+
writeFileSync3(target, buildEnvFile(answers), "utf8");
|
|
39316
|
+
if (process.platform !== "win32") {
|
|
39317
|
+
try {
|
|
39318
|
+
chmodSync(target, 384);
|
|
39319
|
+
} catch {
|
|
39320
|
+
warn(`Could not chmod 0600 ${target}.`);
|
|
39321
|
+
}
|
|
39322
|
+
}
|
|
39323
|
+
}
|
|
39324
|
+
async function action14(options) {
|
|
39325
|
+
const homeEnv = join7(homedir5(), USER_STATE_DIR_NAME, ".env");
|
|
39326
|
+
const cwdEnv = join7(process.cwd(), ".env");
|
|
39327
|
+
const explicitDir = (process.env.CEREFOX_CONFIG_DIR ?? "").trim();
|
|
39328
|
+
if (explicitDir) {
|
|
39329
|
+
const target2 = resolveEnvFile();
|
|
39330
|
+
if (existsSync6(target2) && !options.force) {
|
|
39331
|
+
println(c.yellow(`⚠ Config already exists at ${target2}.`));
|
|
39332
|
+
const ok2 = await confirm("Overwrite?", true);
|
|
39333
|
+
if (!ok2) {
|
|
39334
|
+
println(c.dim("Aborted. Use `--force` to skip this prompt next time."));
|
|
39335
|
+
return;
|
|
39336
|
+
}
|
|
39337
|
+
}
|
|
39338
|
+
const answers2 = options.config ? await readConfigFile(options.config) : await promptForAnswers();
|
|
39339
|
+
println("");
|
|
39340
|
+
println(c.bold("Validating credentials…"));
|
|
39341
|
+
await validateSupabase(answers2.CEREFOX_SUPABASE_URL, answers2.CEREFOX_SUPABASE_KEY);
|
|
39342
|
+
println(c.green(" ✓ Supabase reachable"));
|
|
39343
|
+
await validateOpenAI(answers2.OPENAI_API_KEY);
|
|
39344
|
+
println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
|
|
39345
|
+
writeAnswersTo(target2, answers2);
|
|
39346
|
+
println("");
|
|
39347
|
+
println(c.green(`✓ Wrote ${target2}`));
|
|
39348
|
+
println("");
|
|
39349
|
+
await postWriteLifecycle(target2, options);
|
|
39350
|
+
return;
|
|
39351
|
+
}
|
|
39352
|
+
if (existsSync6(homeEnv) && !options.force) {
|
|
39353
|
+
println(c.yellow(`⚠ Config already exists at ${homeEnv}.`));
|
|
39354
|
+
const ok2 = await confirm("Overwrite?", true);
|
|
39355
|
+
if (!ok2) {
|
|
39356
|
+
println(c.dim("Aborted. Use `--force` to skip this prompt next time."));
|
|
39357
|
+
return;
|
|
39358
|
+
}
|
|
39359
|
+
const answers2 = options.config ? await readConfigFile(options.config) : await promptForAnswers();
|
|
39360
|
+
println("");
|
|
39361
|
+
println(c.bold("Validating credentials…"));
|
|
39362
|
+
await validateSupabase(answers2.CEREFOX_SUPABASE_URL, answers2.CEREFOX_SUPABASE_KEY);
|
|
39363
|
+
println(c.green(" ✓ Supabase reachable"));
|
|
39364
|
+
await validateOpenAI(answers2.OPENAI_API_KEY);
|
|
39365
|
+
println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
|
|
39366
|
+
writeAnswersTo(homeEnv, answers2);
|
|
39367
|
+
println("");
|
|
39368
|
+
println(c.green(`✓ Wrote ${homeEnv}`));
|
|
39369
|
+
println("");
|
|
39370
|
+
await postWriteLifecycle(homeEnv, options);
|
|
39371
|
+
return;
|
|
39372
|
+
}
|
|
39373
|
+
if (existsSync6(cwdEnv) && !options.force && !options.config) {
|
|
39374
|
+
printMigrationMenu(cwdEnv, homeEnv);
|
|
39375
|
+
const ch = await promptMigrationChoice();
|
|
39376
|
+
println("");
|
|
39377
|
+
if (ch === "c") {
|
|
39378
|
+
mkdirSync3(dirname3(homeEnv), { recursive: true });
|
|
39379
|
+
copyFileSync2(cwdEnv, homeEnv);
|
|
39380
|
+
if (process.platform !== "win32") {
|
|
39381
|
+
try {
|
|
39382
|
+
chmodSync(homeEnv, 384);
|
|
39383
|
+
} catch {
|
|
39384
|
+
warn(`Could not chmod 0600 ${homeEnv}.`);
|
|
39385
|
+
}
|
|
39386
|
+
}
|
|
39387
|
+
println(c.green(`✓ Copied ${cwdEnv} → ${homeEnv}`));
|
|
39388
|
+
println(c.dim(` Repo file unchanged — Python still reads it during migration.`));
|
|
39389
|
+
println("");
|
|
39390
|
+
const answers2 = answersFromEnvFile(homeEnv);
|
|
39391
|
+
println(c.bold("Validating credentials…"));
|
|
39392
|
+
await validateSupabase(answers2.CEREFOX_SUPABASE_URL, answers2.CEREFOX_SUPABASE_KEY);
|
|
39393
|
+
println(c.green(" ✓ Supabase reachable"));
|
|
39394
|
+
await validateOpenAI(answers2.OPENAI_API_KEY);
|
|
39395
|
+
println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
|
|
39396
|
+
println("");
|
|
39397
|
+
await postWriteLifecycle(homeEnv, options);
|
|
39398
|
+
return;
|
|
39399
|
+
}
|
|
39400
|
+
if (ch === "u") {
|
|
39401
|
+
const answers2 = answersFromEnvFile(cwdEnv);
|
|
39402
|
+
println(c.bold("Validating existing config…"));
|
|
39403
|
+
await validateSupabase(answers2.CEREFOX_SUPABASE_URL, answers2.CEREFOX_SUPABASE_KEY);
|
|
39404
|
+
println(c.green(" ✓ Supabase reachable"));
|
|
39405
|
+
await validateOpenAI(answers2.OPENAI_API_KEY);
|
|
39406
|
+
println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
|
|
39407
|
+
println("");
|
|
39408
|
+
println(c.green(`✓ Using existing config at ${cwdEnv}`));
|
|
39409
|
+
println(c.dim(` TS reads it via the legacy dev-mode fallback (~/.cerefox/.env not present).`));
|
|
39410
|
+
println(c.dim(` Run \`cerefox init\` again later to migrate to the new home.`));
|
|
39411
|
+
println("");
|
|
39412
|
+
await postWriteLifecycle(cwdEnv, options);
|
|
39413
|
+
return;
|
|
39414
|
+
}
|
|
39415
|
+
println(c.dim(`Fresh start. Ignoring ${cwdEnv}; writing a new config to ${homeEnv}.`));
|
|
39416
|
+
println("");
|
|
39417
|
+
}
|
|
39418
|
+
const target = homeEnv;
|
|
39419
|
+
const answers = options.config ? await readConfigFile(options.config) : await promptForAnswers();
|
|
39420
|
+
println("");
|
|
39421
|
+
println(c.bold("Validating credentials…"));
|
|
39422
|
+
await validateSupabase(answers.CEREFOX_SUPABASE_URL, answers.CEREFOX_SUPABASE_KEY);
|
|
39423
|
+
println(c.green(" ✓ Supabase reachable"));
|
|
39424
|
+
await validateOpenAI(answers.OPENAI_API_KEY);
|
|
39425
|
+
println(c.green(" ✓ OpenAI key valid (test embedding succeeded)"));
|
|
39426
|
+
writeAnswersTo(target, answers);
|
|
39427
|
+
println("");
|
|
39428
|
+
println(c.green(`✓ Wrote ${target}`));
|
|
39429
|
+
println("");
|
|
39430
|
+
await postWriteLifecycle(target, options);
|
|
39162
39431
|
}
|
|
39163
39432
|
function registerInit(program2) {
|
|
39164
39433
|
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);
|
|
@@ -39435,14 +39704,14 @@ function registerReindex(program2) {
|
|
|
39435
39704
|
// src/cli/commands/restore.ts
|
|
39436
39705
|
init_cli_core();
|
|
39437
39706
|
init_client();
|
|
39438
|
-
import { existsSync as existsSync7, readFileSync as
|
|
39439
|
-
import { homedir as
|
|
39440
|
-
import { join as
|
|
39707
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
39708
|
+
import { homedir as homedir6 } from "node:os";
|
|
39709
|
+
import { join as join8, resolve as resolve3 } from "node:path";
|
|
39441
39710
|
function expandHome2(path) {
|
|
39442
39711
|
if (path === "~")
|
|
39443
|
-
return
|
|
39712
|
+
return homedir6();
|
|
39444
39713
|
if (path.startsWith("~/"))
|
|
39445
|
-
return
|
|
39714
|
+
return join8(homedir6(), path.slice(2));
|
|
39446
39715
|
return path;
|
|
39447
39716
|
}
|
|
39448
39717
|
function resolveBackupFile(target) {
|
|
@@ -39453,17 +39722,17 @@ function resolveBackupFile(target) {
|
|
|
39453
39722
|
const stat = statSync4(path);
|
|
39454
39723
|
if (stat.isFile())
|
|
39455
39724
|
return path;
|
|
39456
|
-
const candidates = readdirSync3(path).filter((n) => n.endsWith(".json") && n.startsWith("cerefox-")).map((n) => ({ name: n, mtime: statSync4(
|
|
39725
|
+
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
39726
|
if (candidates.length === 0) {
|
|
39458
39727
|
throw userError(`No cerefox-*.json files in ${path}`);
|
|
39459
39728
|
}
|
|
39460
|
-
return
|
|
39729
|
+
return join8(path, candidates[0].name);
|
|
39461
39730
|
}
|
|
39462
39731
|
async function action20(target, options) {
|
|
39463
39732
|
const file = resolveBackupFile(target);
|
|
39464
39733
|
let payload;
|
|
39465
39734
|
try {
|
|
39466
|
-
payload = JSON.parse(
|
|
39735
|
+
payload = JSON.parse(readFileSync9(file, "utf8"));
|
|
39467
39736
|
} catch (err) {
|
|
39468
39737
|
throw userError(`Could not parse backup file ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
39469
39738
|
}
|
|
@@ -39680,7 +39949,7 @@ function registerSearch(program2) {
|
|
|
39680
39949
|
// src/cli/commands/self-update.ts
|
|
39681
39950
|
init_cli_core();
|
|
39682
39951
|
init_meta();
|
|
39683
|
-
import { spawnSync as
|
|
39952
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
39684
39953
|
function detectRuntime() {
|
|
39685
39954
|
const bin = (process.argv[1] ?? "").toLowerCase();
|
|
39686
39955
|
if (bin.includes(".bun") || bin.includes("/bun/")) {
|
|
@@ -39753,7 +40022,7 @@ async function action22(options) {
|
|
|
39753
40022
|
return;
|
|
39754
40023
|
}
|
|
39755
40024
|
}
|
|
39756
|
-
const result =
|
|
40025
|
+
const result = spawnSync3(runtime.command, runtime.args(target), {
|
|
39757
40026
|
stdio: "inherit"
|
|
39758
40027
|
});
|
|
39759
40028
|
if (result.status !== 0) {
|
|
@@ -39820,18 +40089,18 @@ init_config();
|
|
|
39820
40089
|
init_client();
|
|
39821
40090
|
import {
|
|
39822
40091
|
existsSync as existsSync8,
|
|
39823
|
-
readFileSync as
|
|
40092
|
+
readFileSync as readFileSync10,
|
|
39824
40093
|
readdirSync as readdirSync4,
|
|
39825
40094
|
statSync as statSync5
|
|
39826
40095
|
} from "node:fs";
|
|
39827
|
-
import { basename as basename4, extname as extname4, join as
|
|
40096
|
+
import { basename as basename4, extname as extname4, join as join9, relative } from "node:path";
|
|
39828
40097
|
var ROOT_LEVEL_DOCS = ["README.md", "AGENT_GUIDE.md", "AGENT_QUICK_REFERENCE.md"];
|
|
39829
40098
|
function walkMarkdown(dir) {
|
|
39830
40099
|
const out = [];
|
|
39831
40100
|
if (!existsSync8(dir))
|
|
39832
40101
|
return out;
|
|
39833
40102
|
for (const name of readdirSync4(dir)) {
|
|
39834
|
-
const full =
|
|
40103
|
+
const full = join9(dir, name);
|
|
39835
40104
|
let stat;
|
|
39836
40105
|
try {
|
|
39837
40106
|
stat = statSync5(full);
|
|
@@ -39851,11 +40120,11 @@ async function action24(options) {
|
|
|
39851
40120
|
const project = options.project ?? "cerefox";
|
|
39852
40121
|
const targets = [];
|
|
39853
40122
|
for (const rel of ROOT_LEVEL_DOCS) {
|
|
39854
|
-
const abs =
|
|
40123
|
+
const abs = join9(cwd, rel);
|
|
39855
40124
|
if (existsSync8(abs))
|
|
39856
40125
|
targets.push({ abs, rel });
|
|
39857
40126
|
}
|
|
39858
|
-
for (const abs of walkMarkdown(
|
|
40127
|
+
for (const abs of walkMarkdown(join9(cwd, "docs"))) {
|
|
39859
40128
|
targets.push({ abs, rel: relative(cwd, abs) });
|
|
39860
40129
|
}
|
|
39861
40130
|
if (targets.length === 0) {
|
|
@@ -39879,7 +40148,7 @@ async function action24(options) {
|
|
|
39879
40148
|
const authorType = resolveAuthorType("agent");
|
|
39880
40149
|
const outcomes = [];
|
|
39881
40150
|
for (const t of targets) {
|
|
39882
|
-
const content =
|
|
40151
|
+
const content = readFileSync10(t.abs, "utf8");
|
|
39883
40152
|
const title = basename4(t.abs, extname4(t.abs));
|
|
39884
40153
|
try {
|
|
39885
40154
|
const message = await ingestTool.handler(client.raw, {
|
|
@@ -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
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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.
|
|
127
|
-
|
|
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
|
-
|
|
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,23 @@
|
|
|
1
|
-
# Migrating to Cerefox v0.5.
|
|
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`" + "v0.5.4 fixed configure-agent claude-code" |
|
|
15
|
+
| `@cerefox/memory` v0.5.2 (npm) | "v0.5.3 migrated `.env`" + "v0.5.4 fixed configure-agent claude-code" |
|
|
16
|
+
| `@cerefox/memory` v0.5.3 (npm) | **"v0.5.4 fixed configure-agent claude-code"** — re-run configure-agent |
|
|
17
|
+
|
|
18
|
+
> Looking for `migration-v0.4.md`? It's been demoted to a historical
|
|
19
|
+
> record (the bin names it documents no longer exist). Everything you
|
|
20
|
+
> need to know about the v0.4 → v0.5 transition lives in this file.
|
|
2
21
|
|
|
3
22
|
**TL;DR:** the Cerefox CLI is now a TypeScript binary published to npm.
|
|
4
23
|
You can keep using the Python CLI through v0.7.x (it just prints a
|
|
@@ -196,6 +215,122 @@ explicit instead of "magic delegation".
|
|
|
196
215
|
|
|
197
216
|
---
|
|
198
217
|
|
|
218
|
+
## v0.5.3 migrated `.env` from `<repo>/.env` to `~/.cerefox/.env`
|
|
219
|
+
|
|
220
|
+
If you've been using the Python `cerefox` CLI, your `.env` lives in your
|
|
221
|
+
repo root (`/path/to/cerefox/.env`). The TS CLI v0.5.2 also read that
|
|
222
|
+
file, via a "CWD `.env` wins" precedence inherited from Python. v0.5.3
|
|
223
|
+
flips that precedence: **once `~/.cerefox/.env` exists, the TS CLI reads
|
|
224
|
+
from there**; the repo file becomes a legacy fallback for Python's
|
|
225
|
+
`uv run cerefox …` workflows.
|
|
226
|
+
|
|
227
|
+
**You see zero behavior change until you run `cerefox init`.** If your
|
|
228
|
+
home dir doesn't have `~/.cerefox/.env`, the TS CLI keeps reading your
|
|
229
|
+
existing repo `.env` (legacy dev-mode precedence). No action required.
|
|
230
|
+
|
|
231
|
+
When you do run `cerefox init` with a repo `.env` already in place, the
|
|
232
|
+
TS CLI offers a three-choice menu:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
⚠ Found existing config at /path/to/cerefox/.env.
|
|
236
|
+
|
|
237
|
+
[c] Copy to /Users/you/.cerefox/.env (recommended)
|
|
238
|
+
• TS reads the new home from now on
|
|
239
|
+
• Python keeps reading /path/to/cerefox/.env (backward compat)
|
|
240
|
+
• Edit ~/.cerefox/.env going forward; the repo .env is legacy
|
|
241
|
+
|
|
242
|
+
[u] Use /path/to/cerefox/.env as-is, skip writing anything
|
|
243
|
+
• Both TS and Python keep reading the existing file
|
|
244
|
+
• Defer the migration
|
|
245
|
+
|
|
246
|
+
[f] Fresh start — interactive prompts, write to /Users/you/.cerefox/.env
|
|
247
|
+
• Use if the existing file is stale or wrong
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Pick **[c]** for the typical Python → TS upgrade. The TS CLI starts
|
|
251
|
+
reading `~/.cerefox/.env`; your remaining Python `uv run cerefox …`
|
|
252
|
+
commands keep reading the unchanged repo file. The two files diverge
|
|
253
|
+
only if you start editing one of them — keep them in sync (or just edit
|
|
254
|
+
`~/.cerefox/.env` and accept that Python uses a frozen snapshot until
|
|
255
|
+
v0.9).
|
|
256
|
+
|
|
257
|
+
After v0.9 (Python CLI removed), `cerefox doctor` will say "ok" if you
|
|
258
|
+
delete the repo file. Until then, `doctor` reports it as
|
|
259
|
+
`legacy env … (shadowed by ~/.cerefox/.env)` so you know it's harmless.
|
|
260
|
+
|
|
261
|
+
### Python paths.py precedence (unchanged)
|
|
262
|
+
|
|
263
|
+
`src/cerefox/paths.py` keeps the v0.5.2 precedence (CWD `.env` wins).
|
|
264
|
+
Your existing `uv run cerefox …` invocations from inside the repo
|
|
265
|
+
continue to read the repo file regardless of what's in `~/.cerefox/`.
|
|
266
|
+
When this module goes away in v0.9+, the divergence resolves naturally.
|
|
267
|
+
|
|
268
|
+
### `CEREFOX_CONFIG_DIR` is unchanged
|
|
269
|
+
|
|
270
|
+
If you have `CEREFOX_CONFIG_DIR` set (e.g. for a non-standard install),
|
|
271
|
+
it still wins over both home and repo `.env` files. Init writes there
|
|
272
|
+
and skips the migration prompt.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## v0.5.4 fixed `cerefox configure-agent --tool claude-code`
|
|
277
|
+
|
|
278
|
+
**If you ran `cerefox configure-agent --tool claude-code` on any version
|
|
279
|
+
from v0.5.0 through v0.5.3, Claude Code did not actually pick up the
|
|
280
|
+
config.** The writer wrote to `~/.claude/mcp.json` — a path Claude Code
|
|
281
|
+
doesn't read. Claude Code's user-scope MCP servers live in
|
|
282
|
+
**`~/.claude.json`** (a dot-file in `$HOME`) under the `.mcpServers` key.
|
|
283
|
+
|
|
284
|
+
The bug went unnoticed because `cerefox doctor` was scanning the same
|
|
285
|
+
wrong path — both surfaces were consistently lying.
|
|
286
|
+
|
|
287
|
+
### What v0.5.4 changed
|
|
288
|
+
|
|
289
|
+
- **`configure-agent --tool claude-code`** now shells out to Claude Code's
|
|
290
|
+
own `claude mcp add --scope user` CLI. Claude Code manages its own
|
|
291
|
+
config schema; delegating is future-proof. Requires the `claude` CLI
|
|
292
|
+
on PATH (fair assumption — you're configuring it).
|
|
293
|
+
- Before invoking the delegated CLI, the writer takes a defensive backup
|
|
294
|
+
of `~/.claude.json` to `~/.claude.json.pre-cerefox.bak`.
|
|
295
|
+
- **`cerefox doctor`** now scans `~/.claude.json` for a `mcpServers.cerefox`
|
|
296
|
+
entry (not the orphaned `~/.claude/mcp.json` from the bug window).
|
|
297
|
+
- **`--tool claude-desktop` is unchanged** — Claude Desktop has no CLI
|
|
298
|
+
helper, so its writer remains direct-file-write.
|
|
299
|
+
|
|
300
|
+
### What you need to do
|
|
301
|
+
|
|
302
|
+
Anyone who ran `configure-agent --tool claude-code` on v0.5.0–v0.5.3:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
# 1. Upgrade
|
|
306
|
+
bun update -g @cerefox/memory # or: npm update -g @cerefox/memory
|
|
307
|
+
|
|
308
|
+
# 2. (Optional) Remove the orphaned file the buggy versions wrote.
|
|
309
|
+
# It does nothing — Claude Code never read it. Safe to delete.
|
|
310
|
+
rm -f ~/.claude/mcp.json
|
|
311
|
+
|
|
312
|
+
# 3. Re-run configure-agent to write the config at the correct path.
|
|
313
|
+
cerefox configure-agent --tool claude-code
|
|
314
|
+
|
|
315
|
+
# 4. Verify
|
|
316
|
+
claude mcp list # should now show 'cerefox'
|
|
317
|
+
cerefox doctor # 'mcp clients' should list 'Claude Code (user)'
|
|
318
|
+
|
|
319
|
+
# 5. Start a fresh Claude Code session — the cerefox tools appear.
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Running sessions cache the MCP server list at startup, so an
|
|
323
|
+
**already-open Claude Code session won't pick up the new server**.
|
|
324
|
+
Open a new session.
|
|
325
|
+
|
|
326
|
+
### `--config-path FILE` override (advanced)
|
|
327
|
+
|
|
328
|
+
If you pass `--config-path FILE` explicitly, configure-agent does a
|
|
329
|
+
direct-write to FILE instead of shelling out — preserves the v0.5.0–v0.5.3
|
|
330
|
+
test path and works for power users who want a specific file location.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
199
334
|
## Known gotchas
|
|
200
335
|
|
|
201
336
|
### `npx` from inside an npm workspace
|
|
@@ -4,9 +4,70 @@ Get Cerefox running locally and ingest your first document.
|
|
|
4
4
|
|
|
5
5
|
> **Upgrading from a previous version?** See the [Upgrading Guide](upgrading.md) for migration steps instead.
|
|
6
6
|
|
|
7
|
+
## Two install paths
|
|
8
|
+
|
|
9
|
+
| You want | Read this section | Why |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| **Use Cerefox as an MCP server / CLI** | "Path A — npm install" below (fastest) | One install, callable from any directory. No Python needed. Recommended for end users since v0.5. |
|
|
12
|
+
| **Contribute to Cerefox, run the web UI, deploy schema** | "Path B — source checkout" below | Full source: schema deploy, web UI, ingestion pipeline. Required for contributors and for the web UI (until v0.6). |
|
|
13
|
+
|
|
14
|
+
You'll need a Supabase project (free tier works) and an OpenAI API key for either
|
|
15
|
+
path — those go into `.env`. The npm install asks for them interactively via
|
|
16
|
+
`cerefox init`; the source checkout has you write them into a `.env` file
|
|
17
|
+
yourself.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Path A — npm install (fastest, 5 min total)
|
|
22
|
+
|
|
23
|
+
For end users who just want the Cerefox CLI + MCP server on their machine.
|
|
24
|
+
|
|
25
|
+
### A.1 Prerequisites
|
|
26
|
+
- Node.js 20+ (`node --version`) or Bun 1.0+ (`bun --version`)
|
|
27
|
+
- A Supabase account -- [supabase.com](https://supabase.com) (free tier works)
|
|
28
|
+
- An OpenAI API key -- [platform.openai.com/api-keys](https://platform.openai.com/api-keys)
|
|
29
|
+
- **Schema must be deployed** to your Supabase. Until v0.6 ports the schema
|
|
30
|
+
deploy to TypeScript, this still requires the source checkout (Path B) once,
|
|
31
|
+
or someone else who has the source checkout to deploy it for you.
|
|
32
|
+
|
|
33
|
+
### A.2 Install
|
|
34
|
+
```bash
|
|
35
|
+
# One-line install (detects Bun or installs it, falls back to npm):
|
|
36
|
+
curl -fsSL https://github.com/fstamatelopoulos/cerefox/releases/latest/download/install.sh | sh
|
|
37
|
+
|
|
38
|
+
# Or direct:
|
|
39
|
+
bun add -g @cerefox/memory # preferred
|
|
40
|
+
# npm install -g @cerefox/memory # alternative
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### A.3 First-run setup
|
|
44
|
+
```bash
|
|
45
|
+
cerefox init # 5-step interactive setup
|
|
46
|
+
cerefox doctor # verify the install
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### A.4 Wire up your MCP client
|
|
50
|
+
```bash
|
|
51
|
+
# Run the ones that apply:
|
|
52
|
+
cerefox configure-agent --tool claude-code
|
|
53
|
+
cerefox configure-agent --tool claude-desktop
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`--tool claude-code` shells out to Claude Code's own `claude mcp add --scope user`
|
|
57
|
+
to register the server (Claude Code knows where to store the config).
|
|
58
|
+
`--tool claude-desktop` writes the JSON config file directly.
|
|
59
|
+
|
|
60
|
+
Then restart your MCP client. **Path A users skip ahead to "[Connect an AI agent](#8-connect-an-ai-agent-optional-5-min)" (step 8) for the verification prompt.** Steps 3–7
|
|
61
|
+
below are Path B-only (setting up `.env` by hand, deploying the schema, the web UI).
|
|
62
|
+
|
|
7
63
|
---
|
|
8
64
|
|
|
9
|
-
##
|
|
65
|
+
## Path B — source checkout (contributors, schema deploy, web UI)
|
|
66
|
+
|
|
67
|
+
For anyone hacking on Cerefox itself, deploying the schema for the first time,
|
|
68
|
+
or running the web UI.
|
|
69
|
+
|
|
70
|
+
### B.1 Prerequisites
|
|
10
71
|
|
|
11
72
|
- Python 3.11+ (`python3 --version`)
|
|
12
73
|
- Node.js 18+ and npm (`node --version`)
|
|
@@ -14,9 +75,7 @@ Get Cerefox running locally and ingest your first document.
|
|
|
14
75
|
- A Supabase account -- [supabase.com](https://supabase.com) (free tier works)
|
|
15
76
|
- An OpenAI API key -- [platform.openai.com/api-keys](https://platform.openai.com/api-keys)
|
|
16
77
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## 2. Install Cerefox (2 min)
|
|
78
|
+
### B.2 Install Cerefox (2 min)
|
|
20
79
|
|
|
21
80
|
```bash
|
|
22
81
|
git clone https://github.com/fstamatelopoulos/cerefox.git
|
package/docs/guides/upgrading.md
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
# Upgrading Cerefox
|
|
2
2
|
|
|
3
|
-
This guide covers upgrading an existing Cerefox installation
|
|
3
|
+
This guide covers upgrading an existing Cerefox installation. All steps are idempotent and safe to re-run.
|
|
4
4
|
|
|
5
|
-
##
|
|
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.
|
|
3
|
+
"version": "0.5.4",
|
|
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",
|