@cerefox/memory 0.8.0 → 0.8.1

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.
@@ -182,6 +182,10 @@ function printTable(rows, emptyMessage = "(no rows)") {
182
182
  println(line(headers.map((h) => String(row[h] ?? ""))));
183
183
  }
184
184
  }
185
+ function localTimestamp(d = new Date) {
186
+ const p = (n, w = 2) => String(n).padStart(w, "0");
187
+ return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ` + `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}.${p(d.getMilliseconds(), 3)}`;
188
+ }
185
189
  function eprintln(line = "") {
186
190
  process.stderr.write(line + `
187
191
  `);
@@ -5315,6 +5319,7 @@ __export(exports_cli_core, {
5315
5319
  parseFloat01: () => parseFloat01,
5316
5320
  ok: () => ok,
5317
5321
  notFound: () => notFound,
5322
+ localTimestamp: () => localTimestamp,
5318
5323
  info: () => info,
5319
5324
  errorln: () => errorln,
5320
5325
  eprintln: () => eprintln,
@@ -7179,7 +7184,7 @@ var exports_meta = {};
7179
7184
  __export(exports_meta, {
7180
7185
  PKG_VERSION: () => PKG_VERSION
7181
7186
  });
7182
- var PKG_VERSION = "0.8.0";
7187
+ var PKG_VERSION = "0.8.1";
7183
7188
  var init_meta = () => {};
7184
7189
 
7185
7190
  // ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
@@ -24624,12 +24629,12 @@ var init_src = __esm(() => {
24624
24629
 
24625
24630
  // src/cli/util/bundled-docs.ts
24626
24631
  import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync5, statSync } from "node:fs";
24627
- import { dirname as dirname3, join as join5, resolve as resolve2 } from "node:path";
24632
+ import { dirname as dirname3, join as join6, resolve as resolve2 } from "node:path";
24628
24633
  import { fileURLToPath as fileURLToPath2 } from "node:url";
24629
24634
  function findPackageRoot() {
24630
24635
  let dir = dirname3(fileURLToPath2(import.meta.url));
24631
24636
  for (let i = 0;i < 10; i++) {
24632
- const pkgJson = join5(dir, "package.json");
24637
+ const pkgJson = join6(dir, "package.json");
24633
24638
  if (existsSync7(pkgJson)) {
24634
24639
  try {
24635
24640
  const parsed = JSON.parse(readFileSync5(pkgJson, "utf8"));
@@ -24645,14 +24650,14 @@ function findPackageRoot() {
24645
24650
  return resolve2(dirname3(fileURLToPath2(import.meta.url)), "..", "..", "..");
24646
24651
  }
24647
24652
  function bundledDocsDir() {
24648
- const inPackage = join5(PACKAGE_ROOT, "docs");
24653
+ const inPackage = join6(PACKAGE_ROOT, "docs");
24649
24654
  if (existsSync7(inPackage))
24650
24655
  return inPackage;
24651
24656
  return resolve2(PACKAGE_ROOT, "..", "..", "docs");
24652
24657
  }
24653
24658
  function agentGuidePath() {
24654
24659
  const candidates = [
24655
- join5(PACKAGE_ROOT, "AGENT_GUIDE.md"),
24660
+ join6(PACKAGE_ROOT, "AGENT_GUIDE.md"),
24656
24661
  resolve2(PACKAGE_ROOT, "..", "..", "AGENT_GUIDE.md")
24657
24662
  ];
24658
24663
  for (const c2 of candidates)
@@ -24662,7 +24667,7 @@ function agentGuidePath() {
24662
24667
  }
24663
24668
  function agentQuickReferencePath() {
24664
24669
  const candidates = [
24665
- join5(PACKAGE_ROOT, "AGENT_QUICK_REFERENCE.md"),
24670
+ join6(PACKAGE_ROOT, "AGENT_QUICK_REFERENCE.md"),
24666
24671
  resolve2(PACKAGE_ROOT, "..", "..", "AGENT_QUICK_REFERENCE.md")
24667
24672
  ];
24668
24673
  for (const c2 of candidates)
@@ -24673,12 +24678,12 @@ function agentQuickReferencePath() {
24673
24678
  function listBundledDocs() {
24674
24679
  const entries = [];
24675
24680
  const docsDir = bundledDocsDir();
24676
- const guidesDir = join5(docsDir, "guides");
24681
+ const guidesDir = join6(docsDir, "guides");
24677
24682
  if (existsSync7(guidesDir) && statSync(guidesDir).isDirectory()) {
24678
24683
  for (const name of readdirSync3(guidesDir)) {
24679
24684
  if (!name.endsWith(".md"))
24680
24685
  continue;
24681
- const full = join5(guidesDir, name);
24686
+ const full = join6(guidesDir, name);
24682
24687
  entries.push({
24683
24688
  topic: name.replace(/\.md$/, ""),
24684
24689
  path: full,
@@ -26814,11 +26819,11 @@ async function runSyncSelfDocs(options = {}) {
26814
26819
  printTable(outcomes.filter((o) => o.status === "error").map((o) => ({ topic: o.topic, error: o.detail.slice(0, 100) })));
26815
26820
  }
26816
26821
  }
26817
- async function action14(options) {
26822
+ async function action15(options) {
26818
26823
  await runSyncSelfDocs(options);
26819
26824
  }
26820
26825
  function registerSyncSelfDocs(program2) {
26821
- program2.command("sync-self-docs").description("Ingest bundled Cerefox docs under the _cerefox-self-docs project.").option("--dry-run", "List what would be ingested without writing.").option("--project <name>", "Override the target project name.", "_cerefox-self-docs").action(action14);
26826
+ program2.command("sync-self-docs").description("Ingest bundled Cerefox docs under the _cerefox-self-docs project.").option("--dry-run", "List what would be ingested without writing.").option("--project <name>", "Override the target project name.", "_cerefox-self-docs").action(action15);
26822
26827
  }
26823
26828
  var init_sync_self_docs = __esm(() => {
26824
26829
  init_cli_core();
@@ -41528,6 +41533,49 @@ function registerDeleteDoc(program2) {
41528
41533
  program2.command("delete-doc").description("Soft-delete a document (recoverable via the web UI trash).").argument("<document-id>", "UUID of the document to delete.").option("--reason <text>", "Optional reason recorded in the audit log.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").option("--yes", "Skip the confirmation prompt.").action(action6);
41529
41534
  }
41530
41535
 
41536
+ // src/cli/commands/delete-project.ts
41537
+ init_cli_core();
41538
+ init_client();
41539
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
41540
+ async function action7(target, options) {
41541
+ const client = getClient();
41542
+ const isUuid = UUID_RE.test(target);
41543
+ const lookup = isUuid ? client.raw.from("cerefox_projects").select("id, name, description").eq("id", target).maybeSingle() : client.raw.from("cerefox_projects").select("id, name, description").eq("name", target).maybeSingle();
41544
+ const { data: project, error } = await lookup;
41545
+ if (error) {
41546
+ throw systemError(`Project lookup failed: ${error.message}`);
41547
+ }
41548
+ if (!project) {
41549
+ throw notFound(`Project "${target}" not found.`);
41550
+ }
41551
+ const { count, error: countErr } = await client.raw.from("cerefox_document_projects").select("*", { count: "exact", head: true }).eq("project_id", project.id);
41552
+ if (countErr) {
41553
+ throw systemError(`Could not count documents in project: ${countErr.message}`);
41554
+ }
41555
+ const docCount = count ?? 0;
41556
+ if (docCount > 0 && !options.force) {
41557
+ throw userError(`Project "${project.name}" still has ${docCount} document link(s).`, "Pass --force to delete the project anyway (documents remain; only the project row is removed).");
41558
+ }
41559
+ println(c.yellow("About to delete project:"));
41560
+ println(` ${project.name}`);
41561
+ println(c.dim(` ${project.id} · ${docCount} doc link(s)`));
41562
+ if (!options.yes) {
41563
+ const ok2 = await confirm("Continue?", true);
41564
+ if (!ok2) {
41565
+ println(c.dim("Aborted."));
41566
+ return;
41567
+ }
41568
+ }
41569
+ const { error: delErr } = await client.raw.from("cerefox_projects").delete().eq("id", project.id);
41570
+ if (delErr) {
41571
+ throw systemError(`Delete failed: ${delErr.message}`);
41572
+ }
41573
+ println(c.green(`✓ Deleted project "${project.name}" (id: ${project.id}).`));
41574
+ }
41575
+ function registerDeleteProject(program2) {
41576
+ program2.command("delete-project").description("Delete an empty project (use --force to remove a non-empty one).").argument("<name-or-id>", "Project name (exact match) or UUID.").option("--yes", "Skip the confirmation prompt.").option("--force", "Allow deletion when documents are still linked to the project.").action(action7);
41577
+ }
41578
+
41531
41579
  // src/cli/commands/deploy-server.ts
41532
41580
  init_cli_core();
41533
41581
  init_config();
@@ -41584,8 +41632,9 @@ function resolveServerAssets(opts = {}) {
41584
41632
  }
41585
41633
 
41586
41634
  // ../../_shared/db-deploy/index.ts
41587
- import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync } from "node:fs";
41588
41635
  init_src();
41636
+ import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync } from "node:fs";
41637
+ import { join as join5 } from "node:path";
41589
41638
  var RESET_SQL = `
41590
41639
  DROP TABLE IF EXISTS cerefox_chunks CASCADE;
41591
41640
  DROP TABLE IF EXISTS cerefox_documents CASCADE;
@@ -41651,6 +41700,95 @@ async function runDbDeploy(opts) {
41651
41700
  await sql.end({ timeout: 5 }).catch(() => {});
41652
41701
  }
41653
41702
  }
41703
+ var BOOTSTRAP_MIGRATIONS_SQL = `
41704
+ CREATE TABLE IF NOT EXISTS cerefox_migrations (
41705
+ id SERIAL PRIMARY KEY,
41706
+ filename TEXT NOT NULL UNIQUE,
41707
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
41708
+ );
41709
+ `;
41710
+ async function detectExistingSchema(dbUrl) {
41711
+ const sql = src_default(dbUrl, { prepare: false, onnotice: () => {} });
41712
+ try {
41713
+ const rows = await sql`SELECT to_regclass('public.cerefox_documents') AS t`;
41714
+ return rows[0]?.t != null;
41715
+ } finally {
41716
+ await sql.end({ timeout: 5 }).catch(() => {});
41717
+ }
41718
+ }
41719
+ async function migrationStatus(opts) {
41720
+ const sql = src_default(opts.dbUrl, { prepare: false, onnotice: () => {} });
41721
+ try {
41722
+ await sql.unsafe(BOOTSTRAP_MIGRATIONS_SQL);
41723
+ const all = listMigrationFiles(opts.assets.migrationsDir);
41724
+ const appliedRows = await sql`SELECT filename FROM cerefox_migrations ORDER BY filename`;
41725
+ const appliedSet = new Set(appliedRows.map((r) => r.filename));
41726
+ return {
41727
+ all,
41728
+ applied: all.filter((f) => appliedSet.has(f)),
41729
+ pending: all.filter((f) => !appliedSet.has(f))
41730
+ };
41731
+ } finally {
41732
+ await sql.end({ timeout: 5 }).catch(() => {});
41733
+ }
41734
+ }
41735
+ async function runDbMigrate(opts) {
41736
+ const log = opts.log ?? (() => {});
41737
+ const sql = src_default(opts.dbUrl, { prepare: false, onnotice: () => {} });
41738
+ try {
41739
+ await sql.unsafe(BOOTSTRAP_MIGRATIONS_SQL);
41740
+ const allFiles = listMigrationFiles(opts.assets.migrationsDir);
41741
+ const appliedRows = await sql`SELECT filename FROM cerefox_migrations ORDER BY filename`;
41742
+ const appliedSet = new Set(appliedRows.map((r) => r.filename));
41743
+ const pending = allFiles.filter((f) => !appliedSet.has(f));
41744
+ if (pending.length === 0)
41745
+ return { ok: true, applied: [], pending: [] };
41746
+ if (opts.dryRun) {
41747
+ for (const f of pending)
41748
+ log(`Would apply ${f}`);
41749
+ return { ok: true, applied: [], pending };
41750
+ }
41751
+ const applied = [];
41752
+ for (const f of pending) {
41753
+ const body = readFileSync4(join5(opts.assets.migrationsDir, f), "utf8");
41754
+ log(`Applying ${f}…`);
41755
+ try {
41756
+ await sql.begin(async (tx) => {
41757
+ await tx.unsafe(body);
41758
+ await tx`INSERT INTO cerefox_migrations (filename) VALUES (${f}) ON CONFLICT DO NOTHING`;
41759
+ });
41760
+ applied.push(f);
41761
+ } catch (err) {
41762
+ return {
41763
+ ok: false,
41764
+ applied,
41765
+ pending,
41766
+ failedFile: f,
41767
+ error: err instanceof Error ? err.message : String(err)
41768
+ };
41769
+ }
41770
+ }
41771
+ return { ok: true, applied, pending };
41772
+ } finally {
41773
+ await sql.end({ timeout: 5 }).catch(() => {});
41774
+ }
41775
+ }
41776
+ async function applyRpcs(opts) {
41777
+ const log = opts.log ?? (() => {});
41778
+ log("Refresh RPCs (rpcs.sql)…");
41779
+ if (opts.dryRun)
41780
+ return { ok: true };
41781
+ const rpcsSql = readFileSync4(opts.assets.rpcsFile, "utf8");
41782
+ const sql = src_default(opts.dbUrl, { prepare: false, onnotice: () => {} });
41783
+ try {
41784
+ await sql.unsafe(rpcsSql);
41785
+ return { ok: true };
41786
+ } catch (err) {
41787
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
41788
+ } finally {
41789
+ await sql.end({ timeout: 5 }).catch(() => {});
41790
+ }
41791
+ }
41654
41792
 
41655
41793
  // src/cli/commands/deploy-server.ts
41656
41794
  function commandSucceeds(cmd, args) {
@@ -41666,7 +41804,7 @@ function listEdgeFunctions(functionsDir) {
41666
41804
  return [];
41667
41805
  return readdirSync2(functionsDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("cerefox-")).map((e) => e.name).sort();
41668
41806
  }
41669
- async function action7(options) {
41807
+ async function action8(options) {
41670
41808
  const settings = loadSettings();
41671
41809
  const assets = resolveServerAssets();
41672
41810
  const doSchema = !options.functionsOnly;
@@ -41724,9 +41862,35 @@ async function action7(options) {
41724
41862
  }
41725
41863
  println(c.green(`
41726
41864
  ✓ All prerequisites satisfied.`));
41865
+ let schemaMode = "unknown";
41866
+ let pending = [];
41867
+ if (doSchema) {
41868
+ try {
41869
+ const exists = await detectExistingSchema(settings.databaseUrl);
41870
+ schemaMode = exists ? "existing" : "fresh";
41871
+ if (exists) {
41872
+ pending = (await migrationStatus({ dbUrl: settings.databaseUrl, assets })).pending;
41873
+ }
41874
+ } catch (err) {
41875
+ if (!options.dryRun) {
41876
+ eprintln(c.red(`
41877
+ ✗ Could not connect to the database: ${err instanceof Error ? err.message : String(err)}`));
41878
+ eprintln(c.dim(" Verify CEREFOX_DATABASE_URL (Session Pooler, port 5432)."));
41879
+ process.exit(1);
41880
+ }
41881
+ }
41882
+ }
41727
41883
  const planLines = [];
41728
41884
  if (doSchema) {
41729
- planLines.push(` • ${options.reset ? "RESET + redeploy" : "Deploy"} schema + RPCs to ${settings.supabaseUrl || "your Supabase database"}`);
41885
+ if (schemaMode === "fresh") {
41886
+ planLines.push(` • Deploy fresh schema + RPCs to ${settings.supabaseUrl || "your Supabase database"}`);
41887
+ } else if (schemaMode === "existing") {
41888
+ planLines.push(` • Update existing schema: apply ${pending.length} pending migration(s) + refresh RPCs`);
41889
+ for (const f of pending)
41890
+ planLines.push(c.dim(` – ${f}`));
41891
+ } else {
41892
+ planLines.push(` • Schema + RPCs (fresh or update — couldn't probe the DB)`);
41893
+ }
41730
41894
  }
41731
41895
  if (doFunctions) {
41732
41896
  planLines.push(` • Deploy ${efNames.length} Edge Function(s): ${efNames.join(", ")}`);
@@ -41741,26 +41905,52 @@ Plan:`));
41741
41905
  process.exit(0);
41742
41906
  }
41743
41907
  const proceed = await confirm(`
41744
- Proceed with deployment to Supabase${options.reset ? " (--reset DROPS existing data!)" : ""}?`, true);
41908
+ Proceed with deployment to Supabase?`, true);
41745
41909
  if (!proceed) {
41746
41910
  println(c.dim("Aborted."));
41747
41911
  process.exit(0);
41748
41912
  }
41749
41913
  if (doSchema) {
41750
- println(c.bold(`
41751
- ▶ Deploying schema + RPCs…`));
41752
- const result = await runDbDeploy({
41753
- dbUrl: settings.databaseUrl,
41754
- assets,
41755
- reset: options.reset,
41756
- log: (line) => println(c.dim(` ${line}`))
41757
- });
41758
- if (!result.ok) {
41759
- eprintln(c.red(`
41914
+ if (schemaMode === "fresh") {
41915
+ println(c.bold(`
41916
+ ▶ Deploying fresh schema + RPCs…`));
41917
+ const result = await runDbDeploy({
41918
+ dbUrl: settings.databaseUrl,
41919
+ assets,
41920
+ log: (line) => println(c.dim(` ${line}`))
41921
+ });
41922
+ if (!result.ok) {
41923
+ eprintln(c.red(`
41760
41924
  ✗ Schema deploy failed at "${result.failedStep}": ${result.error}`));
41761
- process.exit(1);
41925
+ process.exit(1);
41926
+ }
41927
+ println(c.green(` ✓ Fresh schema deployed (${result.stepsRun} steps).`));
41928
+ } else {
41929
+ println(c.bold(`
41930
+ ▶ Updating existing schema (migrations + RPC refresh)…`));
41931
+ const mig = await runDbMigrate({
41932
+ dbUrl: settings.databaseUrl,
41933
+ assets,
41934
+ log: (line) => println(c.dim(` ${line}`))
41935
+ });
41936
+ if (!mig.ok) {
41937
+ eprintln(c.red(`
41938
+ ✗ Migration "${mig.failedFile}" failed: ${mig.error}`));
41939
+ eprintln(c.dim(" Earlier migrations in this run were committed. Fix + re-run."));
41940
+ process.exit(1);
41941
+ }
41942
+ const rpcs = await applyRpcs({
41943
+ dbUrl: settings.databaseUrl,
41944
+ assets,
41945
+ log: (line) => println(c.dim(` ${line}`))
41946
+ });
41947
+ if (!rpcs.ok) {
41948
+ eprintln(c.red(`
41949
+ ✗ RPC refresh failed: ${rpcs.error}`));
41950
+ process.exit(1);
41951
+ }
41952
+ println(c.green(` ✓ Applied ${mig.applied.length} migration(s); RPCs refreshed.`));
41762
41953
  }
41763
- println(c.green(` ✓ Schema deployed (${result.stepsRun} steps).`));
41764
41954
  }
41765
41955
  if (doFunctions) {
41766
41956
  println(c.bold(`
@@ -41794,7 +41984,7 @@ Proceed with deployment to Supabase${options.reset ? " (--reset DROPS existing d
41794
41984
  println(c.dim("Verify with: cerefox doctor"));
41795
41985
  }
41796
41986
  function registerDeployServer(program2) {
41797
- program2.command("deploy-server").description("Deploy the Cerefox server side (schema + RPCs + Edge Functions) to Supabase.").option("--dry-run", "Print the plan + pre-flight without deploying.").option("--reset", "DROP existing Cerefox tables before redeploying the schema (DESTRUCTIVE).").option("--schema-only", "Deploy only the schema + RPCs (skip Edge Functions).").option("--functions-only", "Deploy only the Edge Functions (skip the schema).").action(action7);
41987
+ program2.command("deploy-server").description("Deploy/update the Cerefox server side (schema + RPCs + Edge Functions) on Supabase.").option("--dry-run", "Print the plan + pre-flight without deploying.").option("--schema-only", "Deploy/update only the schema + RPCs (skip Edge Functions).").option("--functions-only", "Deploy only the Edge Functions (skip the schema/RPCs).").action(action8);
41798
41988
  }
41799
41989
 
41800
41990
  // src/cli/commands/docs.ts
@@ -41808,7 +41998,7 @@ function openInBrowser(path) {
41808
41998
  println(c.dim(`(could not auto-open; the file is at: ${path})`));
41809
41999
  }
41810
42000
  }
41811
- function action8(topic, options) {
42001
+ function action9(topic, options) {
41812
42002
  const docs = listBundledDocs();
41813
42003
  if (options.list || !topic && !options.print) {
41814
42004
  if (docs.length === 0) {
@@ -41837,7 +42027,7 @@ function action8(topic, options) {
41837
42027
  openInBrowser(doc.path);
41838
42028
  }
41839
42029
  function registerDocs(program2) {
41840
- program2.command("docs").description("Open bundled Cerefox docs in your browser (or print to stdout).").argument("[topic]", "Doc topic (e.g. quickstart, connect-agents). Omit for the index.").option("--print", "Print to stdout instead of opening a browser.").option("--list", "List available topics.").action(action8);
42030
+ program2.command("docs").description("Open bundled Cerefox docs in your browser (or print to stdout).").argument("[topic]", "Doc topic (e.g. quickstart, connect-agents). Omit for the index.").option("--print", "Print to stdout instead of opening a browser.").option("--list", "List available topics.").action(action9);
41841
42031
  }
41842
42032
 
41843
42033
  // ../../node_modules/.bun/ora@9.4.0/node_modules/ora/index.js
@@ -45231,7 +45421,7 @@ init_config();
45231
45421
  init_config();
45232
45422
  import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync, statSync as statSync2 } from "node:fs";
45233
45423
  import { homedir as homedir4 } from "node:os";
45234
- import { join as join6 } from "node:path";
45424
+ import { join as join7 } from "node:path";
45235
45425
 
45236
45426
  // ../../_shared/compatibility/index.ts
45237
45427
  var COMPATIBILITY = {
@@ -45502,11 +45692,24 @@ async function checkOpenAI() {
45502
45692
  };
45503
45693
  }
45504
45694
  }
45695
+ var SCHEMA_VERSION_RE = /^--\s*@version:\s*(\S+)/m;
45696
+ function readBundledSchemaVersion() {
45697
+ try {
45698
+ const assets = resolveServerAssets();
45699
+ if (!existsSync8(assets.schemaFile))
45700
+ return null;
45701
+ const m = readFileSync6(assets.schemaFile, "utf8").match(SCHEMA_VERSION_RE);
45702
+ return m ? m[1] : null;
45703
+ } catch {
45704
+ return null;
45705
+ }
45706
+ }
45707
+ var SCHEMA_CHECK_NAME = "schema + RPCs";
45505
45708
  async function checkSchemaVersion() {
45506
45709
  const settings = loadSettings();
45507
45710
  if (!settings.supabaseUrl || !settings.supabaseKey) {
45508
45711
  return {
45509
- name: "schema",
45712
+ name: SCHEMA_CHECK_NAME,
45510
45713
  status: "skipped",
45511
45714
  detail: "Supabase config missing; skipped."
45512
45715
  };
@@ -45524,21 +45727,40 @@ async function checkSchemaVersion() {
45524
45727
  });
45525
45728
  if (!resp.ok) {
45526
45729
  return {
45527
- name: "schema",
45730
+ name: SCHEMA_CHECK_NAME,
45528
45731
  status: "error",
45529
- detail: `cerefox_schema_version returned ${resp.status}`,
45530
- hint: "Deploy the schema: `uv run python scripts/db_deploy.py`"
45732
+ detail: `cerefox_schema_version returned ${resp.status} — schema not deployed.`,
45733
+ hint: "Deploy the schema + RPCs (see remediation below)."
45531
45734
  };
45532
45735
  }
45533
45736
  const deployed = await resp.json();
45534
- return {
45535
- name: "schema",
45536
- status: "ok",
45537
- detail: `cerefox_schema_version() → "${deployed}"`
45538
- };
45737
+ const bundled = readBundledSchemaVersion();
45738
+ const level = classifyCompat(deployed, COMPATIBILITY.minSchema, bundled);
45739
+ switch (level) {
45740
+ case "below-min":
45741
+ return {
45742
+ name: SCHEMA_CHECK_NAME,
45743
+ status: "error",
45744
+ detail: `Deployed schema v${deployed} is below the required minimum v${COMPATIBILITY.minSchema}.`,
45745
+ hint: "Update the schema + RPCs (see remediation below)."
45746
+ };
45747
+ case "above-min-but-old":
45748
+ return {
45749
+ name: SCHEMA_CHECK_NAME,
45750
+ status: "warn",
45751
+ detail: `Deployed schema v${deployed} works but is older than this client's bundled v${bundled}.`,
45752
+ hint: "Update the schema + RPCs (see remediation below)."
45753
+ };
45754
+ default:
45755
+ return {
45756
+ name: SCHEMA_CHECK_NAME,
45757
+ status: "ok",
45758
+ detail: `cerefox_schema_version() → "${deployed}"${bundled ? ` (bundled v${bundled})` : ""}`
45759
+ };
45760
+ }
45539
45761
  } catch (err) {
45540
45762
  return {
45541
- name: "schema",
45763
+ name: SCHEMA_CHECK_NAME,
45542
45764
  status: "error",
45543
45765
  detail: `Schema version probe failed: ${err instanceof Error ? err.message : String(err)}`
45544
45766
  };
@@ -45557,9 +45779,9 @@ function hasCerefoxInJsonFile(path) {
45557
45779
  }
45558
45780
  function checkMcpConfigs() {
45559
45781
  const home = homedir4();
45560
- const claudeCodeUser = join6(home, ".claude.json");
45561
- const claudeCodeProj = join6(process.cwd(), ".mcp.json");
45562
- const claudeDesktop = process.platform === "darwin" ? join6(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : process.platform === "win32" ? join6(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json") : join6(home, ".config", "Claude", "claude_desktop_config.json");
45782
+ const claudeCodeUser = join7(home, ".claude.json");
45783
+ const claudeCodeProj = join7(process.cwd(), ".mcp.json");
45784
+ const claudeDesktop = process.platform === "darwin" ? join7(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : process.platform === "win32" ? join7(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json") : join7(home, ".config", "Claude", "claude_desktop_config.json");
45563
45785
  const found = [];
45564
45786
  if (hasCerefoxInJsonFile(claudeCodeUser))
45565
45787
  found.push("Claude Code (user)");
@@ -45583,8 +45805,8 @@ function checkMcpConfigs() {
45583
45805
  }
45584
45806
  function checkLegacyShadowEnv() {
45585
45807
  const home = homedir4();
45586
- const homeEnv = join6(home, USER_STATE_DIR_NAME, ".env");
45587
- const cwdEnv = join6(process.cwd(), ".env");
45808
+ const homeEnv = join7(home, USER_STATE_DIR_NAME, ".env");
45809
+ const cwdEnv = join7(process.cwd(), ".env");
45588
45810
  if (!existsSync8(homeEnv) || !existsSync8(cwdEnv))
45589
45811
  return null;
45590
45812
  try {
@@ -45689,14 +45911,14 @@ async function checkEdgeFunctionsCompat() {
45689
45911
  name: "edge functions",
45690
45912
  status: "error",
45691
45913
  detail: `Deployed EF v${deployed} is below the required minimum v${compat.edgeFunctions.min}.`,
45692
- hint: "Redeploy the Edge Functions: `cerefox deploy-server --functions-only`."
45914
+ hint: "Update the Edge Functions (see remediation below)."
45693
45915
  };
45694
45916
  case "above-min-but-old":
45695
45917
  return {
45696
45918
  name: "edge functions",
45697
45919
  status: "warn",
45698
45920
  detail: `Deployed EF v${deployed} works but is older than this client (v${PKG_VERSION}).`,
45699
- hint: "Consider redeploying: `cerefox deploy-server --functions-only`."
45921
+ hint: "Update the Edge Functions (see remediation below)."
45700
45922
  };
45701
45923
  default:
45702
45924
  return {
@@ -45731,7 +45953,7 @@ async function runAllChecks(opts = {}) {
45731
45953
  { name: "legacy env", phase: "Checking legacy env shadowing", run: () => checkLegacyShadowEnv() },
45732
45954
  { name: "supabase", phase: "Probing Supabase Data API", run: () => checkSupabase() },
45733
45955
  { name: "openai", phase: "Probing OpenAI embeddings", run: () => checkOpenAI() },
45734
- { name: "schema", phase: "Reading schema version", run: () => checkSchemaVersion() },
45956
+ { name: "schema + RPCs", phase: "Reading schema + RPC version", run: () => checkSchemaVersion() },
45735
45957
  { name: "edge functions", phase: "Probing Edge Function versions", run: () => checkEdgeFunctionsCompat() },
45736
45958
  { name: "postgres", phase: "Probing Postgres DDL endpoint", run: () => checkPostgres() },
45737
45959
  { name: "mcp clients", phase: "Scanning MCP client configs", run: () => checkMcpConfigs() }
@@ -45760,7 +45982,7 @@ function symbol(status) {
45760
45982
  return cErr.dim("ℹ");
45761
45983
  }
45762
45984
  }
45763
- async function action9(options) {
45985
+ async function action10(options) {
45764
45986
  const useSpinner = !options.json && process.stderr.isTTY;
45765
45987
  const spinner = useSpinner ? ora({ text: "Starting checks…", spinner: "dots", stream: process.stderr }).start() : null;
45766
45988
  const results = await runAllChecks({
@@ -45784,6 +46006,26 @@ async function action9(options) {
45784
46006
  }
45785
46007
  println("");
45786
46008
  }
46009
+ if (!options.json) {
46010
+ const stale = (name) => {
46011
+ const r = results.find((x) => x.name === name);
46012
+ return r != null && (r.status === "error" || r.status === "warn");
46013
+ };
46014
+ const needsSchema = stale("schema + RPCs");
46015
+ const needsEf = stale("edge functions");
46016
+ let remediation = null;
46017
+ if (needsSchema && needsEf) {
46018
+ remediation = "Update the server (schema + RPCs + Edge Functions): cerefox deploy-server";
46019
+ } else if (needsSchema) {
46020
+ remediation = "Update the schema + RPCs: cerefox deploy-server --schema-only";
46021
+ } else if (needsEf) {
46022
+ remediation = "Update the Edge Functions: cerefox deploy-server --functions-only";
46023
+ }
46024
+ if (remediation) {
46025
+ println(cErr.yellow("→ " + remediation));
46026
+ println("");
46027
+ }
46028
+ }
45787
46029
  const errCount = results.filter((r) => r.status === "error").length;
45788
46030
  const warnCount = results.filter((r) => r.status === "warn").length;
45789
46031
  if (errCount > 0) {
@@ -45797,13 +46039,13 @@ async function action9(options) {
45797
46039
  }
45798
46040
  }
45799
46041
  function registerDoctor(program2) {
45800
- program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action9);
46042
+ program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action10);
45801
46043
  }
45802
46044
 
45803
46045
  // src/cli/commands/get-audit-log.ts
45804
46046
  init_cli_core();
45805
46047
  init_client();
45806
- async function action10(options) {
46048
+ async function action11(options) {
45807
46049
  const limit = parsePositiveInt(options.limit, "--limit", 50);
45808
46050
  const client = getClient();
45809
46051
  const data = await client.rpc("cerefox_list_audit_entries", {
@@ -45841,14 +46083,14 @@ async function action10(options) {
45841
46083
  })));
45842
46084
  }
45843
46085
  function registerGetAuditLog(program2) {
45844
- program2.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("-a, --author <name>", "Filter by author.").option("-o, --operation <type>", "Filter by operation: create, update-content, update-metadata, delete, restore.").option("--since <iso>", "Lower-bound ISO timestamp.").option("--until <iso>", "Upper-bound ISO timestamp.").option("-l, --limit <n>", "Maximum entries (max 200).", "50").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action10);
46086
+ program2.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("-a, --author <name>", "Filter by author.").option("-o, --operation <type>", "Filter by operation: create, update-content, update-metadata, delete, restore.").option("--since <iso>", "Lower-bound ISO timestamp.").option("--until <iso>", "Upper-bound ISO timestamp.").option("-l, --limit <n>", "Maximum entries (max 200).", "50").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action11);
45845
46087
  }
45846
46088
 
45847
46089
  // src/cli/commands/get-doc.ts
45848
46090
  init_cli_core();
45849
46091
  init_cli_core();
45850
46092
  init_client();
45851
- async function action11(documentId, options) {
46093
+ async function action12(documentId, options) {
45852
46094
  const client = getClient();
45853
46095
  const rows = await client.rpc("cerefox_get_document", {
45854
46096
  p_document_id: documentId,
@@ -45878,7 +46120,7 @@ async function action11(documentId, options) {
45878
46120
  println(doc.full_content);
45879
46121
  }
45880
46122
  function registerGetDoc(program2) {
45881
- program2.command("get-doc").description("Retrieve the full content of a document by ID.").argument("<document-id>", "UUID of the document.").option("--version-id <uuid>", "Specific archived version (default: current).").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action11);
46123
+ program2.command("get-doc").description("Retrieve the full content of a document by ID.").argument("<document-id>", "UUID of the document.").option("--version-id <uuid>", "Specific archived version (default: current).").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action12);
45882
46124
  }
45883
46125
 
45884
46126
  // src/cli/commands/ingest.ts
@@ -46615,7 +46857,7 @@ async function readContent(path, paste) {
46615
46857
  const titleFromPath = basename2(path, extname2(path));
46616
46858
  return { content, titleFromPath };
46617
46859
  }
46618
- async function action12(path, options) {
46860
+ async function action13(path, options) {
46619
46861
  const { content, titleFromPath } = await readContent(path, Boolean(options.paste));
46620
46862
  const title = options.title ?? titleFromPath;
46621
46863
  if (!title || title.trim() === "") {
@@ -46687,7 +46929,7 @@ async function action12(path, options) {
46687
46929
  }
46688
46930
  }
46689
46931
  function registerIngest(program2) {
46690
- program2.command("ingest").description("Ingest a file (or stdin paste) into the knowledge base.").argument("[path]", "Path to the file to ingest. Omit when using --paste.").option("--paste", "Read content from stdin instead of a file.").option("-t, --title <title>", "Document title (required with --paste; defaults to filename without extension).").option("-p, --project-name <name>", "Single project membership (non-destructive on update).").option("-P, --project-names <names>", "Comma-separated full project membership set (destructive replace on update).").option("-m, --metadata <json>", "JSON metadata object.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-i, --document-id <uuid>", "Update a specific document by UUID (overrides --update-if-exists).").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").action(action12);
46932
+ program2.command("ingest").description("Ingest a file (or stdin paste) into the knowledge base.").argument("[path]", "Path to the file to ingest. Omit when using --paste.").option("--paste", "Read content from stdin instead of a file.").option("-t, --title <title>", "Document title (required with --paste; defaults to filename without extension).").option("-p, --project-name <name>", "Single project membership (non-destructive on update).").option("-P, --project-names <names>", "Comma-separated full project membership set (destructive replace on update).").option("-m, --metadata <json>", "JSON metadata object.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-i, --document-id <uuid>", "Update a specific document by UUID (overrides --update-if-exists).").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").action(action13);
46691
46933
  }
46692
46934
 
46693
46935
  // src/cli/commands/ingest-dir.ts
@@ -46696,7 +46938,7 @@ init_cli_core();
46696
46938
  init_config();
46697
46939
  var import_cli_progress = __toESM(require_cli_progress(), 1);
46698
46940
  import { readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
46699
- import { basename as basename3, extname as extname3, join as join7 } from "node:path";
46941
+ import { basename as basename3, extname as extname3, join as join8 } from "node:path";
46700
46942
  function walk(dir, extensions) {
46701
46943
  let entries;
46702
46944
  try {
@@ -46706,7 +46948,7 @@ function walk(dir, extensions) {
46706
46948
  }
46707
46949
  const files = [];
46708
46950
  for (const name of entries) {
46709
- const full = join7(dir, name);
46951
+ const full = join8(dir, name);
46710
46952
  let stat;
46711
46953
  try {
46712
46954
  stat = statSync3(full);
@@ -46723,7 +46965,7 @@ function walk(dir, extensions) {
46723
46965
  }
46724
46966
  return files;
46725
46967
  }
46726
- async function action13(dir, options) {
46968
+ async function action14(dir, options) {
46727
46969
  const extensions = new Set((options.extensions ?? ".md,.txt").split(",").map((e) => e.trim().toLowerCase()).map((e) => e.startsWith(".") ? e : "." + e).filter((e) => e.length > 0));
46728
46970
  const files = walk(dir, extensions);
46729
46971
  if (files.length === 0) {
@@ -46796,7 +47038,7 @@ async function action13(dir, options) {
46796
47038
  }
46797
47039
  }
46798
47040
  function registerIngestDir(program2) {
46799
- program2.command("ingest-dir").description("Recursively ingest a directory of markdown / text files.").argument("<dir>", "Root directory to walk.").option("-p, --project-name <name>", "Project membership for all ingested docs.").option("-m, --metadata <json>", "JSON metadata applied to every doc.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").option("-e, --extensions <list>", "Comma-separated file extensions to ingest.", ".md,.txt").action(action13);
47041
+ program2.command("ingest-dir").description("Recursively ingest a directory of markdown / text files.").argument("<dir>", "Root directory to walk.").option("-p, --project-name <name>", "Project membership for all ingested docs.").option("-m, --metadata <json>", "JSON metadata applied to every doc.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").option("-e, --extensions <list>", "Comma-separated file extensions to ingest.", ".md,.txt").action(action14);
46800
47042
  }
46801
47043
 
46802
47044
  // src/cli/commands/init.ts
@@ -46812,7 +47054,7 @@ import {
46812
47054
  writeFileSync as writeFileSync3
46813
47055
  } from "node:fs";
46814
47056
  import { homedir as homedir5 } from "node:os";
46815
- import { dirname as dirname4, join as join8 } from "node:path";
47057
+ import { dirname as dirname4, join as join9 } from "node:path";
46816
47058
  async function readConfigFile(path) {
46817
47059
  if (!existsSync9(path)) {
46818
47060
  throw userError(`--config file not found: ${path}`);
@@ -47062,11 +47304,11 @@ async function maybeOfferServerDeploy() {
47062
47304
  if (compareSemver(deployed, COMPATIBILITY.minSchema) < 0) {
47063
47305
  println(c.bold("Schema deploy"));
47064
47306
  println(c.yellow(` Deployed schema v${deployed} is below the required v${COMPATIBILITY.minSchema}.`));
47065
- const yes = await confirm(" Redeploy the server now (--reset DROPS existing data)?", true);
47307
+ const yes = await confirm(" Update the server now (applies pending migrations, refreshes RPCs + EFs)?", true);
47066
47308
  if (yes)
47067
- launchDeployServer(["--reset"]);
47309
+ launchDeployServer();
47068
47310
  else
47069
- println(c.dim(" Skipped. Run `cerefox deploy-server --reset` when ready."));
47311
+ println(c.dim(" Skipped. Run `cerefox deploy-server` when ready."));
47070
47312
  println("");
47071
47313
  return;
47072
47314
  }
@@ -47126,9 +47368,9 @@ function writeAnswersTo(target, answers) {
47126
47368
  }
47127
47369
  }
47128
47370
  }
47129
- async function action15(options) {
47130
- const homeEnv = join8(homedir5(), USER_STATE_DIR_NAME, ".env");
47131
- const cwdEnv = join8(process.cwd(), ".env");
47371
+ async function action16(options) {
47372
+ const homeEnv = join9(homedir5(), USER_STATE_DIR_NAME, ".env");
47373
+ const cwdEnv = join9(process.cwd(), ".env");
47132
47374
  const explicitDir = (process.env.CEREFOX_CONFIG_DIR ?? "").trim();
47133
47375
  if (explicitDir) {
47134
47376
  const target2 = resolveEnvFile();
@@ -47235,13 +47477,13 @@ async function action15(options) {
47235
47477
  await postWriteLifecycle(target, options);
47236
47478
  }
47237
47479
  function registerInit(program2) {
47238
- 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(action15);
47480
+ 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(action16);
47239
47481
  }
47240
47482
 
47241
47483
  // src/cli/commands/list-docs.ts
47242
47484
  init_cli_core();
47243
47485
  init_client();
47244
- async function action16(options) {
47486
+ async function action17(options) {
47245
47487
  const limit = parsePositiveInt(options.limit, "--limit", 100);
47246
47488
  const client = getClient();
47247
47489
  let projectId;
@@ -47296,13 +47538,13 @@ async function action16(options) {
47296
47538
  })));
47297
47539
  }
47298
47540
  function registerListDocs(program2) {
47299
- program2.command("list-docs").description("List documents in the knowledge base.").option("-p, --project <name>", "Filter to a specific project.").option("-l, --limit <n>", "Maximum docs to return.", "100").option("--json", "Emit machine-readable JSON.").action(action16);
47541
+ program2.command("list-docs").description("List documents in the knowledge base.").option("-p, --project <name>", "Filter to a specific project.").option("-l, --limit <n>", "Maximum docs to return.", "100").option("--json", "Emit machine-readable JSON.").action(action17);
47300
47542
  }
47301
47543
 
47302
47544
  // src/cli/commands/list-metadata-keys.ts
47303
47545
  init_cli_core();
47304
47546
  init_client();
47305
- async function action17(options) {
47547
+ async function action18(options) {
47306
47548
  const client = getClient();
47307
47549
  const data = await client.rpc("cerefox_list_metadata_keys");
47308
47550
  if (data === null) {
@@ -47330,13 +47572,13 @@ async function action17(options) {
47330
47572
  })));
47331
47573
  }
47332
47574
  function registerListMetadataKeys(program2) {
47333
- program2.command("list-metadata-keys").description("List all metadata keys with document counts and example values.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action17);
47575
+ program2.command("list-metadata-keys").description("List all metadata keys with document counts and example values.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action18);
47334
47576
  }
47335
47577
 
47336
47578
  // src/cli/commands/list-projects.ts
47337
47579
  init_cli_core();
47338
47580
  init_client();
47339
- async function action18(options) {
47581
+ async function action19(options) {
47340
47582
  const client = getClient();
47341
47583
  const { data, error: error2 } = await client.raw.from("cerefox_projects").select("id, name, description, created_at").order("name", { ascending: true });
47342
47584
  if (error2) {
@@ -47365,13 +47607,13 @@ async function action18(options) {
47365
47607
  })), "(no projects)");
47366
47608
  }
47367
47609
  function registerListProjects(program2) {
47368
- program2.command("list-projects").description("List all projects in the knowledge base.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action18);
47610
+ program2.command("list-projects").description("List all projects in the knowledge base.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action19);
47369
47611
  }
47370
47612
 
47371
47613
  // src/cli/commands/list-versions.ts
47372
47614
  init_cli_core();
47373
47615
  init_client();
47374
- async function action19(documentId, options) {
47616
+ async function action20(documentId, options) {
47375
47617
  const client = getClient();
47376
47618
  const data = await client.rpc("cerefox_list_document_versions", {
47377
47619
  p_document_id: documentId
@@ -47412,7 +47654,7 @@ async function action19(documentId, options) {
47412
47654
  })));
47413
47655
  }
47414
47656
  function registerListVersions(program2) {
47415
- program2.command("list-versions").description("List archived versions of a document.").argument("<document-id>", "UUID of the document.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action19);
47657
+ program2.command("list-versions").description("List archived versions of a document.").argument("<document-id>", "UUID of the document.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action20);
47416
47658
  }
47417
47659
 
47418
47660
  // src/cli/commands/mcp.ts
@@ -47427,7 +47669,7 @@ function registerMcp(program2) {
47427
47669
  // src/cli/commands/metadata-search.ts
47428
47670
  init_cli_core();
47429
47671
  init_client();
47430
- async function action20(options) {
47672
+ async function action21(options) {
47431
47673
  const metadataFilter = parseJsonObjectArg(options.metadataFilter, "--metadata-filter");
47432
47674
  if (!metadataFilter || Object.keys(metadataFilter).length === 0) {
47433
47675
  throw userError("--metadata-filter is required and must be a non-empty JSON object.", `Example: --metadata-filter '{"type":"decision-log"}'.`);
@@ -47490,7 +47732,7 @@ async function action20(options) {
47490
47732
  }
47491
47733
  }
47492
47734
  function registerMetadataSearch(program2) {
47493
- program2.command("metadata-search").description("Find documents by metadata criteria (no text query).").requiredOption("-f, --metadata-filter <json>", "JSON object; only docs whose metadata contains ALL pairs are returned.").option("-p, --project-name <name>", "Filter to a specific project.").option("--updated-since <iso>", "Only docs updated on/after this ISO timestamp.").option("--created-since <iso>", "Only docs created on/after this ISO timestamp.").option("--include-content", "Include full document text in results.").option("-l, --limit <n>", "Maximum docs to return.", "10").option("--max-bytes <n>", "Response size budget in bytes (with --include-content).", "200000").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action20);
47735
+ program2.command("metadata-search").description("Find documents by metadata criteria (no text query).").requiredOption("-f, --metadata-filter <json>", "JSON object; only docs whose metadata contains ALL pairs are returned.").option("-p, --project-name <name>", "Filter to a specific project.").option("--updated-since <iso>", "Only docs updated on/after this ISO timestamp.").option("--created-since <iso>", "Only docs created on/after this ISO timestamp.").option("--include-content", "Include full document text in results.").option("-l, --limit <n>", "Maximum docs to return.", "10").option("--max-bytes <n>", "Response size budget in bytes (with --include-content).", "200000").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action21);
47494
47736
  }
47495
47737
 
47496
47738
  // src/cli/commands/reindex.ts
@@ -47498,7 +47740,7 @@ init_dist4();
47498
47740
  init_cli_core();
47499
47741
  init_config();
47500
47742
  var DEFAULT_MODEL = "text-embedding-3-small";
47501
- async function action21(options) {
47743
+ async function action22(options) {
47502
47744
  const settings = loadSettings();
47503
47745
  if (!settings.supabaseUrl || !settings.supabaseKey) {
47504
47746
  throw userError("Supabase credentials not configured — run `cerefox init` first.");
@@ -47577,7 +47819,7 @@ ${c2.content}`;
47577
47819
  }
47578
47820
  }
47579
47821
  function registerReindex(program2) {
47580
- program2.command("reindex").description("Re-embed existing document chunks (v0.7+).").option("--all", "Reindex every chunk regardless of embedder.").option("--batch <n>", "Chunks per OpenAI batch call. Capped at 96 internally.", "32").option("--dry-run", "Show counts without re-embedding.").option("-i, --document-id <uuid>", "Limit reindex to a single document.").action(action21);
47822
+ program2.command("reindex").description("Re-embed existing document chunks (v0.7+).").option("--all", "Reindex every chunk regardless of embedder.").option("--batch <n>", "Chunks per OpenAI batch call. Capped at 96 internally.", "32").option("--dry-run", "Show counts without re-embedding.").option("-i, --document-id <uuid>", "Limit reindex to a single document.").action(action22);
47581
47823
  }
47582
47824
 
47583
47825
  // src/cli/commands/restore.ts
@@ -47585,12 +47827,12 @@ init_cli_core();
47585
47827
  init_client();
47586
47828
  import { existsSync as existsSync10, readFileSync as readFileSync11, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
47587
47829
  import { homedir as homedir6 } from "node:os";
47588
- import { join as join9, resolve as resolve4 } from "node:path";
47830
+ import { join as join10, resolve as resolve4 } from "node:path";
47589
47831
  function expandHome2(path) {
47590
47832
  if (path === "~")
47591
47833
  return homedir6();
47592
47834
  if (path.startsWith("~/"))
47593
- return join9(homedir6(), path.slice(2));
47835
+ return join10(homedir6(), path.slice(2));
47594
47836
  return path;
47595
47837
  }
47596
47838
  function resolveBackupFile(target) {
@@ -47601,13 +47843,13 @@ function resolveBackupFile(target) {
47601
47843
  const stat = statSync4(path);
47602
47844
  if (stat.isFile())
47603
47845
  return path;
47604
- const candidates = readdirSync5(path).filter((n) => n.endsWith(".json") && n.startsWith("cerefox-")).map((n) => ({ name: n, mtime: statSync4(join9(path, n)).mtimeMs })).sort((a, b2) => b2.mtime - a.mtime);
47846
+ const candidates = readdirSync5(path).filter((n) => n.endsWith(".json") && n.startsWith("cerefox-")).map((n) => ({ name: n, mtime: statSync4(join10(path, n)).mtimeMs })).sort((a, b2) => b2.mtime - a.mtime);
47605
47847
  if (candidates.length === 0) {
47606
47848
  throw userError(`No cerefox-*.json files in ${path}`);
47607
47849
  }
47608
- return join9(path, candidates[0].name);
47850
+ return join10(path, candidates[0].name);
47609
47851
  }
47610
- async function action22(target, options) {
47852
+ async function action23(target, options) {
47611
47853
  const file = resolveBackupFile(target);
47612
47854
  let payload;
47613
47855
  try {
@@ -47663,7 +47905,7 @@ async function action22(target, options) {
47663
47905
  }
47664
47906
  }
47665
47907
  function registerRestore(program2) {
47666
- program2.command("restore").description("Restore a JSON-snapshot backup into the knowledge base.").argument("<snapshot>", "Backup file (or directory; most recent is picked) produced by `cerefox backup`.").option("--dry-run", "Print what would be restored without writing.").option("-p, --project-name <name>", "Reserved for future use; currently ignored (project memberships ride along with each doc's metadata).").action(action22);
47908
+ program2.command("restore").description("Restore a JSON-snapshot backup into the knowledge base.").argument("<snapshot>", "Backup file (or directory; most recent is picked) produced by `cerefox backup`.").option("--dry-run", "Print what would be restored without writing.").option("-p, --project-name <name>", "Reserved for future use; currently ignored (project memberships ride along with each doc's metadata).").action(action23);
47667
47909
  }
47668
47910
 
47669
47911
  // src/cli/commands/search.ts
@@ -47688,7 +47930,7 @@ async function embedQuery(query) {
47688
47930
  }
47689
47931
 
47690
47932
  // src/cli/commands/search.ts
47691
- async function action23(query, options) {
47933
+ async function action24(query, options) {
47692
47934
  if (!query || query.trim() === "") {
47693
47935
  throw userError("Empty query.");
47694
47936
  }
@@ -47822,7 +48064,7 @@ async function action23(query, options) {
47822
48064
  }
47823
48065
  }
47824
48066
  function registerSearch(program2) {
47825
- program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold.", "0.5").option("--max-bytes <n>", "Response size budget in bytes.", "200000").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").action(action23);
48067
+ program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold.", "0.5").option("--max-bytes <n>", "Response size budget in bytes.", "200000").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").action(action24);
47826
48068
  }
47827
48069
 
47828
48070
  // src/cli/commands/self-update.ts
@@ -47869,7 +48111,7 @@ async function fetchLatestVersion() {
47869
48111
  }
47870
48112
  return body.version;
47871
48113
  }
47872
- async function action24(options) {
48114
+ async function action25(options) {
47873
48115
  let target;
47874
48116
  try {
47875
48117
  target = options.version ?? await fetchLatestVersion();
@@ -47921,7 +48163,7 @@ async function action24(options) {
47921
48163
  }
47922
48164
  function registerSelfUpdate(program2) {
47923
48165
  const desc = "Upgrade Cerefox in place. Alias: `cerefox upgrade`.";
47924
- const declaration = (cmd) => cmd.description(desc).option("--check", "Print current vs latest; do nothing.").option("--yes", "Non-interactive (skip confirmation).").option("--version <version>", "Pin a specific version (e.g. 0.5.1 or 0.6.0-rc.1).").action(action24);
48166
+ const declaration = (cmd) => cmd.description(desc).option("--check", "Print current vs latest; do nothing.").option("--yes", "Non-interactive (skip confirmation).").option("--version <version>", "Pin a specific version (e.g. 0.5.1 or 0.6.0-rc.1).").action(action25);
47925
48167
  declaration(program2.command("self-update"));
47926
48168
  declaration(program2.command("upgrade"));
47927
48169
  }
@@ -47940,7 +48182,7 @@ function symbol2(status) {
47940
48182
  return cErr.dim("ℹ");
47941
48183
  }
47942
48184
  }
47943
- async function action25(options) {
48185
+ async function action26(options) {
47944
48186
  const useSpinner = !options.json && process.stderr.isTTY;
47945
48187
  const spinner = useSpinner ? ora({ text: "Starting checks…", spinner: "dots", stream: process.stderr }).start() : null;
47946
48188
  const results = await runFastChecks({
@@ -47961,7 +48203,7 @@ async function action25(options) {
47961
48203
  }
47962
48204
  }
47963
48205
  function registerStatus(program2) {
47964
- program2.command("status").description("Quick sanity check (fast subset of `cerefox doctor`).").option("--json", "Emit machine-readable JSON.").action(action25);
48206
+ program2.command("status").description("Quick sanity check (fast subset of `cerefox doctor`).").option("--json", "Emit machine-readable JSON.").action(action26);
47965
48207
  }
47966
48208
 
47967
48209
  // src/cli/commands/sync-docs.ts
@@ -47975,14 +48217,14 @@ import {
47975
48217
  readdirSync as readdirSync6,
47976
48218
  statSync as statSync5
47977
48219
  } from "node:fs";
47978
- import { basename as basename5, extname as extname5, join as join10, relative } from "node:path";
48220
+ import { basename as basename5, extname as extname5, join as join11, relative } from "node:path";
47979
48221
  var ROOT_LEVEL_DOCS = ["README.md", "AGENT_GUIDE.md", "AGENT_QUICK_REFERENCE.md"];
47980
48222
  function walkMarkdown(dir) {
47981
48223
  const out = [];
47982
48224
  if (!existsSync11(dir))
47983
48225
  return out;
47984
48226
  for (const name of readdirSync6(dir)) {
47985
- const full = join10(dir, name);
48227
+ const full = join11(dir, name);
47986
48228
  let stat;
47987
48229
  try {
47988
48230
  stat = statSync5(full);
@@ -47997,16 +48239,16 @@ function walkMarkdown(dir) {
47997
48239
  }
47998
48240
  return out;
47999
48241
  }
48000
- async function action26(options) {
48242
+ async function action27(options) {
48001
48243
  const cwd = process.cwd();
48002
48244
  const project = options.project ?? "cerefox";
48003
48245
  const targets = [];
48004
48246
  for (const rel of ROOT_LEVEL_DOCS) {
48005
- const abs = join10(cwd, rel);
48247
+ const abs = join11(cwd, rel);
48006
48248
  if (existsSync11(abs))
48007
48249
  targets.push({ abs, rel });
48008
48250
  }
48009
- for (const abs of walkMarkdown(join10(cwd, "docs"))) {
48251
+ for (const abs of walkMarkdown(join11(cwd, "docs"))) {
48010
48252
  targets.push({ abs, rel: relative(cwd, abs) });
48011
48253
  }
48012
48254
  if (targets.length === 0) {
@@ -48059,7 +48301,7 @@ async function action26(options) {
48059
48301
  }
48060
48302
  }
48061
48303
  function registerSyncDocs(program2) {
48062
- program2.command("sync-docs").description("Sync repo docs (README, AGENT_*, docs/) into a Cerefox project.").option("--dry-run", "Print what would be synced without writing.").option("-p, --project <name>", "Target project for the sync.", "cerefox").action(action26);
48304
+ program2.command("sync-docs").description("Sync repo docs (README, AGENT_*, docs/) into a Cerefox project.").option("--dry-run", "Print what would be synced without writing.").option("-p, --project <name>", "Target project for the sync.", "cerefox").action(action27);
48063
48305
  }
48064
48306
 
48065
48307
  // src/cli/program.ts
@@ -49395,7 +49637,7 @@ var baseMimes = _baseMimes;
49395
49637
 
49396
49638
  // ../../node_modules/.bun/@hono+node-server@2.0.4+1bbe96acb4c5ebf1/node_modules/@hono/node-server/dist/serve-static.mjs
49397
49639
  import { createReadStream, existsSync as existsSync12, statSync as statSync6 } from "node:fs";
49398
- import { join as join11 } from "node:path";
49640
+ import { join as join12 } from "node:path";
49399
49641
  var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
49400
49642
  var ENCODINGS = {
49401
49643
  br: ".br",
@@ -49444,11 +49686,11 @@ var serveStatic = (options = { root: "" }) => {
49444
49686
  await options.onNotFound?.(c2.req.path, c2);
49445
49687
  return next();
49446
49688
  }
49447
- let path = join11(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c2) : filename);
49689
+ let path = join12(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c2) : filename);
49448
49690
  let stats = getStats(path);
49449
49691
  if (stats && stats.isDirectory()) {
49450
49692
  const indexFile = options.index ?? "index.html";
49451
- path = join11(path, indexFile);
49693
+ path = join12(path, indexFile);
49452
49694
  stats = getStats(path);
49453
49695
  }
49454
49696
  if (!stats) {
@@ -49507,7 +49749,7 @@ var serveStatic = (options = { root: "" }) => {
49507
49749
  // src/web/server.ts
49508
49750
  import { existsSync as existsSync15 } from "node:fs";
49509
49751
  import { readFileSync as readFileSync14 } from "node:fs";
49510
- import { join as join14 } from "node:path";
49752
+ import { join as join15 } from "node:path";
49511
49753
 
49512
49754
  // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/compose.js
49513
49755
  var compose = (middleware, onError, onNotFound) => {
@@ -51314,7 +51556,7 @@ function registerConfigRoutes(app, ctx) {
51314
51556
  }
51315
51557
 
51316
51558
  // src/web/routes/discovery.ts
51317
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
51559
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
51318
51560
  var DOC_COLS = "id, title, source, source_path, content_hash, metadata, chunk_count, total_chars, review_status, created_at, updated_at, deleted_at";
51319
51561
  function jsonByteLength(value) {
51320
51562
  return Buffer.byteLength(JSON.stringify(value), "utf8");
@@ -51760,7 +52002,7 @@ function registerDiscoveryRoutes(app, ctx) {
51760
52002
  if (!path) {
51761
52003
  return c2.json({ tried_path: "", anchor, matches: [] });
51762
52004
  }
51763
- if (UUID_RE.test(path)) {
52005
+ if (UUID_RE2.test(path)) {
51764
52006
  if (path === fromDocId) {
51765
52007
  return c2.json({ tried_path: path, anchor, matches: [] });
51766
52008
  }
@@ -52361,7 +52603,7 @@ import {
52361
52603
  readdirSync as readdirSync7,
52362
52604
  statSync as statSync7
52363
52605
  } from "node:fs";
52364
- import { basename as basename6, dirname as dirname5, join as join12, resolve as resolve5 } from "node:path";
52606
+ import { basename as basename6, dirname as dirname5, join as join13, resolve as resolve5 } from "node:path";
52365
52607
  import { fileURLToPath as fileURLToPath3 } from "node:url";
52366
52608
  var TOP_LEVEL_DOCS = [
52367
52609
  { filename: "README.md", path: "README.md", category: "readme" },
@@ -52382,27 +52624,27 @@ function moduleDir2() {
52382
52624
  function resolveDocsRoots() {
52383
52625
  const here = moduleDir2();
52384
52626
  const pkgRootCandidates = [
52385
- join12(here, "..", ".."),
52386
- join12(here, "..", "..", "..", "..")
52627
+ join13(here, "..", ".."),
52628
+ join13(here, "..", "..", "..", "..")
52387
52629
  ];
52388
52630
  let pkgGuides = null;
52389
52631
  let pkgTopLevel = null;
52390
52632
  for (const pkg of pkgRootCandidates) {
52391
- const guides = join12(pkg, "docs", "guides");
52633
+ const guides = join13(pkg, "docs", "guides");
52392
52634
  if (existsSync13(guides) && statSync7(guides).isDirectory()) {
52393
52635
  pkgGuides = guides;
52394
52636
  pkgTopLevel = pkg;
52395
52637
  break;
52396
52638
  }
52397
52639
  }
52398
- const repoCandidate = join12(here, "..", "..", "..", "..");
52399
- const repoGuides = join12(repoCandidate, "docs", "guides");
52640
+ const repoCandidate = join13(here, "..", "..", "..", "..");
52641
+ const repoGuides = join13(repoCandidate, "docs", "guides");
52400
52642
  const repoTopLevel = repoCandidate;
52401
52643
  return {
52402
52644
  pkgGuides,
52403
52645
  pkgTopLevel,
52404
52646
  repoGuides: existsSync13(repoGuides) ? repoGuides : null,
52405
- repoTopLevel: existsSync13(join12(repoTopLevel, "README.md")) ? repoTopLevel : null
52647
+ repoTopLevel: existsSync13(join13(repoTopLevel, "README.md")) ? repoTopLevel : null
52406
52648
  };
52407
52649
  }
52408
52650
  function readH1(filePath) {
@@ -52427,7 +52669,7 @@ function listBundledDocs2() {
52427
52669
  const topRoot = pkgTopLevel ?? repoTopLevel;
52428
52670
  if (topRoot) {
52429
52671
  for (const t of TOP_LEVEL_DOCS) {
52430
- const abs = join12(topRoot, t.filename);
52672
+ const abs = join13(topRoot, t.filename);
52431
52673
  if (existsSync13(abs)) {
52432
52674
  entries.push(entryForFile(abs, t.path, t.category));
52433
52675
  }
@@ -52437,7 +52679,7 @@ function listBundledDocs2() {
52437
52679
  if (guidesRoot) {
52438
52680
  const names = readdirSync7(guidesRoot).filter((n) => n.endsWith(".md")).sort();
52439
52681
  for (const name of names) {
52440
- const abs = join12(guidesRoot, name);
52682
+ const abs = join13(guidesRoot, name);
52441
52683
  entries.push(entryForFile(abs, `guides/${name}`, "guide"));
52442
52684
  }
52443
52685
  }
@@ -52482,7 +52724,7 @@ var VERSION_INFO = {
52482
52724
  git_commit_short: resolveGitCommitShort(),
52483
52725
  build_date: process.env.CEREFOX_BUILD_DATE ?? null
52484
52726
  };
52485
- var SCHEMA_VERSION_RE = /^--\s*@version:\s*(\S+)/m;
52727
+ var SCHEMA_VERSION_RE2 = /^--\s*@version:\s*(\S+)/m;
52486
52728
  function registerMetaRoutes(app, ctx) {
52487
52729
  app.get("/api/v1/version", (c2) => c2.json(VERSION_INFO));
52488
52730
  app.get("/api/v1/docs", (c2) => c2.json(listBundledDocs2()));
@@ -52501,16 +52743,16 @@ function registerMetaRoutes(app, ctx) {
52501
52743
  try {
52502
52744
  const { readFileSync: readFileSync14, existsSync: existsSync14 } = await import("node:fs");
52503
52745
  const { fileURLToPath: fileURLToPath4 } = await import("node:url");
52504
- const { dirname: dirname6, join: join13 } = await import("node:path");
52746
+ const { dirname: dirname6, join: join14 } = await import("node:path");
52505
52747
  const here = dirname6(fileURLToPath4(import.meta.url));
52506
52748
  const candidates = [
52507
- join13(here, "..", "..", "..", "db", "schema.sql"),
52508
- join13(here, "..", "..", "..", "..", "..", "src", "cerefox", "db", "schema.sql")
52749
+ join14(here, "..", "..", "..", "db", "schema.sql"),
52750
+ join14(here, "..", "..", "..", "..", "..", "src", "cerefox", "db", "schema.sql")
52509
52751
  ];
52510
52752
  for (const path of candidates) {
52511
52753
  if (existsSync14(path)) {
52512
52754
  const sql = readFileSync14(path, "utf8");
52513
- const match2 = sql.match(SCHEMA_VERSION_RE);
52755
+ const match2 = sql.match(SCHEMA_VERSION_RE2);
52514
52756
  bundled = match2 ? match2[1] : null;
52515
52757
  break;
52516
52758
  }
@@ -52620,20 +52862,20 @@ function registerProjectsRoutes(app, ctx) {
52620
52862
 
52621
52863
  // src/web/static.ts
52622
52864
  import { existsSync as existsSync14, statSync as statSync8 } from "node:fs";
52623
- import { dirname as dirname6, join as join13 } from "node:path";
52865
+ import { dirname as dirname6, join as join14 } from "node:path";
52624
52866
  import { fileURLToPath as fileURLToPath4 } from "node:url";
52625
52867
  function moduleDir3() {
52626
52868
  return dirname6(fileURLToPath4(import.meta.url));
52627
52869
  }
52628
52870
  function isUsableSpaDir(dir) {
52629
- return existsSync14(dir) && statSync8(dir).isDirectory() && existsSync14(join13(dir, "index.html"));
52871
+ return existsSync14(dir) && statSync8(dir).isDirectory() && existsSync14(join14(dir, "index.html"));
52630
52872
  }
52631
52873
  function resolveSpaDist() {
52632
52874
  const here = moduleDir3();
52633
52875
  const candidates = [
52634
- join13(here, "..", "frontend"),
52635
- join13(here, "..", "..", "..", "..", "frontend", "dist"),
52636
- join13(here, "..", "..", "dist", "frontend")
52876
+ join14(here, "..", "frontend"),
52877
+ join14(here, "..", "..", "..", "..", "frontend", "dist"),
52878
+ join14(here, "..", "..", "dist", "frontend")
52637
52879
  ];
52638
52880
  for (const c2 of candidates) {
52639
52881
  if (isUsableSpaDir(c2))
@@ -52644,8 +52886,8 @@ function resolveSpaDist() {
52644
52886
  function resolveStaticDir() {
52645
52887
  const here = moduleDir3();
52646
52888
  const candidates = [
52647
- join13(here, "..", "static"),
52648
- join13(here, "..", "..", "..", "..", "web", "static")
52889
+ join14(here, "..", "static"),
52890
+ join14(here, "..", "..", "..", "..", "web", "static")
52649
52891
  ];
52650
52892
  for (const c2 of candidates) {
52651
52893
  if (existsSync14(c2) && statSync8(c2).isDirectory())
@@ -52691,11 +52933,12 @@ var ROOT_REDIRECT_HTML = `<!DOCTYPE html>
52691
52933
 
52692
52934
  // src/web/server.ts
52693
52935
  init_meta();
52936
+ init_cli_core();
52694
52937
  init_config();
52695
52938
  function buildApp(ctx = buildWebContext()) {
52696
52939
  const app = new Hono2;
52697
52940
  if (true) {
52698
- app.use(logger());
52941
+ app.use(logger((message, ...rest) => console.log(`${localTimestamp()} ${message}`, ...rest)));
52699
52942
  }
52700
52943
  registerMetaRoutes(app, ctx);
52701
52944
  if (ctx) {
@@ -52731,7 +52974,7 @@ function buildApp(ctx = buildWebContext()) {
52731
52974
  root: spaDist,
52732
52975
  rewriteRequestPath: (path) => path.replace(/^\/app/, "") || "/"
52733
52976
  }));
52734
- const indexPath = join14(spaDist, "index.html");
52977
+ const indexPath = join15(spaDist, "index.html");
52735
52978
  if (existsSync15(indexPath)) {
52736
52979
  const indexHtml = readFileSync14(indexPath, "utf8");
52737
52980
  app.get("/app/*", (c2) => c2.html(indexHtml));
@@ -52799,10 +53042,10 @@ import {
52799
53042
  writeFileSync as writeFileSync4
52800
53043
  } from "node:fs";
52801
53044
  import { homedir as homedir7 } from "node:os";
52802
- import { join as join15 } from "node:path";
52803
- var STATE_DIR = join15(homedir7(), ".cerefox");
52804
- var PID_FILE = join15(STATE_DIR, "web.pid");
52805
- var LOG_FILE = join15(STATE_DIR, "web.log");
53045
+ import { join as join16 } from "node:path";
53046
+ var STATE_DIR = join16(homedir7(), ".cerefox");
53047
+ var PID_FILE = join16(STATE_DIR, "web.pid");
53048
+ var LOG_FILE = join16(STATE_DIR, "web.log");
52806
53049
  var daemonPaths = { stateDir: STATE_DIR, pidFile: PID_FILE, logFile: LOG_FILE };
52807
53050
  function ensureStateDir() {
52808
53051
  if (!existsSync16(STATE_DIR))
@@ -52950,11 +53193,11 @@ async function runForeground(host, port, watch) {
52950
53193
  }
52951
53194
  try {
52952
53195
  const handle = await buildWebServer({ host, port });
52953
- info(`Cerefox web listening on http://${handle.host}:${handle.port}/`);
53196
+ println(`${localTimestamp()} Cerefox web listening on http://${handle.host}:${handle.port}/`);
52954
53197
  println(` Web UI: http://${handle.host}:${handle.port}/app/`);
52955
53198
  println(` API: http://${handle.host}:${handle.port}/api/v1/`);
52956
53199
  const shutdown = async (signal) => {
52957
- info(`Received ${signal}; shutting down.`);
53200
+ println(`${localTimestamp()} Received ${signal}; shutting down.`);
52958
53201
  await handle.close().catch(() => {});
52959
53202
  process.exit(0);
52960
53203
  };
@@ -53056,7 +53299,7 @@ function buildProgram() {
53056
53299
  Command groups (each row in the list above falls into one):
53057
53300
  ` + ` READS search · get-doc · list-docs · list-versions · list-projects
53058
53301
  ` + ` · list-metadata-keys · metadata-search · get-audit-log
53059
- ` + ` WRITES ingest · ingest-dir · delete-doc
53302
+ ` + ` WRITES ingest · ingest-dir · delete-doc · delete-project
53060
53303
  ` + ` SERVERS mcp · web
53061
53304
  ` + ` LIFECYCLE init · doctor · status · configure-agent · self-update · upgrade · sync-self-docs · deploy-server
53062
53305
  ` + ` OPS backup · restore · sync-docs · docs · reindex · config-get · config-set · completion
@@ -53081,6 +53324,7 @@ Learn more:
53081
53324
  registerIngest(program2);
53082
53325
  registerIngestDir(program2);
53083
53326
  registerDeleteDoc(program2);
53327
+ registerDeleteProject(program2);
53084
53328
  registerMcp(program2);
53085
53329
  registerWeb(program2);
53086
53330
  registerInit(program2);
@@ -133,6 +133,26 @@ change.
133
133
  > make authenticated POST calls to the Edge Functions. The built-in local server is
134
134
  > the correct solution.
135
135
 
136
+ ### Fastest setup: `cerefox configure-agent`
137
+
138
+ You don't have to hand-edit the per-client config files below. `cerefox configure-agent
139
+ --tool <client>` writes the correct local-stdio entry (`npx -y --package=@cerefox/memory
140
+ cerefox mcp`) into the right config file for you. Supported clients:
141
+
142
+ ```bash
143
+ cerefox configure-agent --tool claude-code # ~/.claude.json (via `claude mcp add`)
144
+ cerefox configure-agent --tool claude-desktop # Claude Desktop config
145
+ cerefox configure-agent --tool cursor # ~/.cursor/mcp.json
146
+ cerefox configure-agent --tool codex # ~/.codex/config.toml
147
+ cerefox configure-agent --tool gemini # ~/.gemini/settings.json
148
+ ```
149
+
150
+ Useful flags: `--dry-run` (print the planned write without touching any file), `--json`
151
+ (machine-readable result), `--config-path <path>` (override the target file), `--no-backup`
152
+ (skip the `.pre-cerefox.bak` backup). The command is idempotent and backs up any existing
153
+ config before writing. The per-client sections below document the same entries for anyone
154
+ who prefers to edit by hand or needs the remote (`Path A-Remote`) HTTP transport instead.
155
+
136
156
  ### Path A MCP tools
137
157
 
138
158
  Once configured, every Path A client has these tools:
@@ -49,14 +49,18 @@ Cerefox usage guidance.
49
49
  # Run the commands that apply to your setup:
50
50
  cerefox configure-agent --tool claude-code # Claude Code (~/.claude.json)
51
51
  cerefox configure-agent --tool claude-desktop # Claude Desktop config
52
+ cerefox configure-agent --tool cursor # Cursor (~/.cursor/mcp.json)
53
+ cerefox configure-agent --tool codex # OpenAI Codex CLI (~/.codex/config.toml)
54
+ cerefox configure-agent --tool gemini # Gemini CLI (~/.gemini/settings.json)
52
55
  ```
53
56
 
54
57
  Then restart your client:
55
58
  - **Claude Code**: start a fresh session — running sessions cache the MCP tool list.
56
59
  - **Claude Desktop**: Cmd+Q to fully quit, then relaunch.
60
+ - **Cursor / Codex CLI / Gemini CLI**: reload or restart the client/session.
57
61
 
58
- Cursor, OpenAI Codex CLI, and Gemini CLI ship in a follow-up (v0.6+).
59
- For manual setup of those today, see [`connect-agents.md`](connect-agents.md).
62
+ All five writers configure the local stdio server. For the remote (Edge Function)
63
+ HTTP transport, or to edit configs by hand, see [`connect-agents.md`](connect-agents.md).
60
64
 
61
65
  ## 4. Try it
62
66
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerefox/memory",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
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",