@cerefox/memory 0.8.0 → 0.8.2

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.2";
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) {
@@ -41661,16 +41799,28 @@ function commandSucceeds(cmd, args) {
41661
41799
  return false;
41662
41800
  }
41663
41801
  }
41802
+ function parseProjectRef(supabaseUrl) {
41803
+ if (!supabaseUrl)
41804
+ return null;
41805
+ try {
41806
+ const host = new URL(supabaseUrl).hostname;
41807
+ const label = host.split(".")[0];
41808
+ return /^[a-z0-9]{20}$/.test(label) ? label : null;
41809
+ } catch {
41810
+ return null;
41811
+ }
41812
+ }
41664
41813
  function listEdgeFunctions(functionsDir) {
41665
41814
  if (!existsSync6(functionsDir))
41666
41815
  return [];
41667
41816
  return readdirSync2(functionsDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("cerefox-")).map((e) => e.name).sort();
41668
41817
  }
41669
- async function action7(options) {
41818
+ async function action8(options) {
41670
41819
  const settings = loadSettings();
41671
41820
  const assets = resolveServerAssets();
41672
41821
  const doSchema = !options.functionsOnly;
41673
41822
  const doFunctions = !options.schemaOnly;
41823
+ const projectRef = options.projectRef ?? parseProjectRef(settings.supabaseUrl);
41674
41824
  const checks = [];
41675
41825
  if (doSchema) {
41676
41826
  checks.push({
@@ -41698,12 +41848,11 @@ async function action7(options) {
41698
41848
  remediation: "Install Node 20+ (npx ships with it) from nodejs.org, then re-run. " + "`cerefox deploy-server` shells out to the Supabase CLI via npx."
41699
41849
  });
41700
41850
  checks.push({
41701
- label: "Supabase project linked (`npx supabase link`)",
41702
- ok: existsSync6("supabase/config.toml") || existsSync6(".supabase/config.toml"),
41703
- remediation: `Authenticate + link your project (one-time, from any working dir):
41704
- ` + ` npx supabase login
41705
- ` + ` npx supabase link --project-ref <your-project-ref>
41706
- ` + " Your project ref is in the dashboard URL: " + "https://supabase.com/dashboard/project/<project-ref>"
41851
+ label: "Supabase project ref resolved (from CEREFOX_SUPABASE_URL)",
41852
+ ok: Boolean(projectRef),
41853
+ remediation: `Could not derive the project ref from CEREFOX_SUPABASE_URL.
41854
+ ` + " Set CEREFOX_SUPABASE_URL to your project URL " + `(https://<project-ref>.supabase.co), or pass --project-ref <ref>.
41855
+ ` + " Also make sure you're logged in once: npx supabase login"
41707
41856
  });
41708
41857
  }
41709
41858
  const failed = checks.filter((ch) => !ch.ok);
@@ -41724,12 +41873,38 @@ async function action7(options) {
41724
41873
  }
41725
41874
  println(c.green(`
41726
41875
  ✓ All prerequisites satisfied.`));
41876
+ let schemaMode = "unknown";
41877
+ let pending = [];
41878
+ if (doSchema) {
41879
+ try {
41880
+ const exists = await detectExistingSchema(settings.databaseUrl);
41881
+ schemaMode = exists ? "existing" : "fresh";
41882
+ if (exists) {
41883
+ pending = (await migrationStatus({ dbUrl: settings.databaseUrl, assets })).pending;
41884
+ }
41885
+ } catch (err) {
41886
+ if (!options.dryRun) {
41887
+ eprintln(c.red(`
41888
+ ✗ Could not connect to the database: ${err instanceof Error ? err.message : String(err)}`));
41889
+ eprintln(c.dim(" Verify CEREFOX_DATABASE_URL (Session Pooler, port 5432)."));
41890
+ process.exit(1);
41891
+ }
41892
+ }
41893
+ }
41727
41894
  const planLines = [];
41728
41895
  if (doSchema) {
41729
- planLines.push(` • ${options.reset ? "RESET + redeploy" : "Deploy"} schema + RPCs to ${settings.supabaseUrl || "your Supabase database"}`);
41896
+ if (schemaMode === "fresh") {
41897
+ planLines.push(` • Deploy fresh schema + RPCs to ${settings.supabaseUrl || "your Supabase database"}`);
41898
+ } else if (schemaMode === "existing") {
41899
+ planLines.push(` • Update existing schema: apply ${pending.length} pending migration(s) + refresh RPCs`);
41900
+ for (const f of pending)
41901
+ planLines.push(c.dim(` – ${f}`));
41902
+ } else {
41903
+ planLines.push(` • Schema + RPCs (fresh or update — couldn't probe the DB)`);
41904
+ }
41730
41905
  }
41731
41906
  if (doFunctions) {
41732
- planLines.push(` • Deploy ${efNames.length} Edge Function(s): ${efNames.join(", ")}`);
41907
+ planLines.push(` • Deploy ${efNames.length} Edge Function(s) to project ${projectRef}: ${efNames.join(", ")}`);
41733
41908
  }
41734
41909
  println(c.bold(`
41735
41910
  Plan:`));
@@ -41741,26 +41916,52 @@ Plan:`));
41741
41916
  process.exit(0);
41742
41917
  }
41743
41918
  const proceed = await confirm(`
41744
- Proceed with deployment to Supabase${options.reset ? " (--reset DROPS existing data!)" : ""}?`, true);
41919
+ Proceed with deployment to Supabase?`, true);
41745
41920
  if (!proceed) {
41746
41921
  println(c.dim("Aborted."));
41747
41922
  process.exit(0);
41748
41923
  }
41749
41924
  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(`
41925
+ if (schemaMode === "fresh") {
41926
+ println(c.bold(`
41927
+ ▶ Deploying fresh schema + RPCs…`));
41928
+ const result = await runDbDeploy({
41929
+ dbUrl: settings.databaseUrl,
41930
+ assets,
41931
+ log: (line) => println(c.dim(` ${line}`))
41932
+ });
41933
+ if (!result.ok) {
41934
+ eprintln(c.red(`
41760
41935
  ✗ Schema deploy failed at "${result.failedStep}": ${result.error}`));
41761
- process.exit(1);
41936
+ process.exit(1);
41937
+ }
41938
+ println(c.green(` ✓ Fresh schema deployed (${result.stepsRun} steps).`));
41939
+ } else {
41940
+ println(c.bold(`
41941
+ ▶ Updating existing schema (migrations + RPC refresh)…`));
41942
+ const mig = await runDbMigrate({
41943
+ dbUrl: settings.databaseUrl,
41944
+ assets,
41945
+ log: (line) => println(c.dim(` ${line}`))
41946
+ });
41947
+ if (!mig.ok) {
41948
+ eprintln(c.red(`
41949
+ ✗ Migration "${mig.failedFile}" failed: ${mig.error}`));
41950
+ eprintln(c.dim(" Earlier migrations in this run were committed. Fix + re-run."));
41951
+ process.exit(1);
41952
+ }
41953
+ const rpcs = await applyRpcs({
41954
+ dbUrl: settings.databaseUrl,
41955
+ assets,
41956
+ log: (line) => println(c.dim(` ${line}`))
41957
+ });
41958
+ if (!rpcs.ok) {
41959
+ eprintln(c.red(`
41960
+ ✗ RPC refresh failed: ${rpcs.error}`));
41961
+ process.exit(1);
41962
+ }
41963
+ println(c.green(` ✓ Applied ${mig.applied.length} migration(s); RPCs refreshed.`));
41762
41964
  }
41763
- println(c.green(` ✓ Schema deployed (${result.stepsRun} steps).`));
41764
41965
  }
41765
41966
  if (doFunctions) {
41766
41967
  println(c.bold(`
@@ -41770,7 +41971,10 @@ Proceed with deployment to Supabase${options.reset ? " (--reset DROPS existing d
41770
41971
  for (const ef of efNames) {
41771
41972
  info(` deploying ${ef}…`);
41772
41973
  const workdir = assets.functionsDir.replace(/\/functions$/, "").replace(/\/supabase$/, "");
41773
- const r = spawnSync2("npx", ["--yes", "supabase", "functions", "deploy", ef], {
41974
+ const args = ["--yes", "supabase", "functions", "deploy", ef];
41975
+ if (projectRef)
41976
+ args.push("--project-ref", projectRef);
41977
+ const r = spawnSync2("npx", args, {
41774
41978
  encoding: "utf8",
41775
41979
  stdio: "inherit",
41776
41980
  cwd: workdir,
@@ -41794,7 +41998,7 @@ Proceed with deployment to Supabase${options.reset ? " (--reset DROPS existing d
41794
41998
  println(c.dim("Verify with: cerefox doctor"));
41795
41999
  }
41796
42000
  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);
42001
+ 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).").option("--project-ref <ref>", "Supabase project ref for Edge Function deploys (default: derived from CEREFOX_SUPABASE_URL).").action(action8);
41798
42002
  }
41799
42003
 
41800
42004
  // src/cli/commands/docs.ts
@@ -41808,7 +42012,7 @@ function openInBrowser(path) {
41808
42012
  println(c.dim(`(could not auto-open; the file is at: ${path})`));
41809
42013
  }
41810
42014
  }
41811
- function action8(topic, options) {
42015
+ function action9(topic, options) {
41812
42016
  const docs = listBundledDocs();
41813
42017
  if (options.list || !topic && !options.print) {
41814
42018
  if (docs.length === 0) {
@@ -41837,7 +42041,7 @@ function action8(topic, options) {
41837
42041
  openInBrowser(doc.path);
41838
42042
  }
41839
42043
  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);
42044
+ 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
42045
  }
41842
42046
 
41843
42047
  // ../../node_modules/.bun/ora@9.4.0/node_modules/ora/index.js
@@ -45227,11 +45431,16 @@ init_cli_core();
45227
45431
 
45228
45432
  // src/cli/util/checks.ts
45229
45433
  init_meta();
45230
- init_config();
45231
- init_config();
45232
45434
  import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync, statSync as statSync2 } from "node:fs";
45233
45435
  import { homedir as homedir4 } from "node:os";
45234
- import { join as join6 } from "node:path";
45436
+ import { join as join7 } from "node:path";
45437
+
45438
+ // ../../_shared/ef-meta/index.ts
45439
+ var EF_VERSION = "0.8.0";
45440
+
45441
+ // src/cli/util/checks.ts
45442
+ init_config();
45443
+ init_config();
45235
45444
 
45236
45445
  // ../../_shared/compatibility/index.ts
45237
45446
  var COMPATIBILITY = {
@@ -45502,11 +45711,24 @@ async function checkOpenAI() {
45502
45711
  };
45503
45712
  }
45504
45713
  }
45714
+ var SCHEMA_VERSION_RE = /^--\s*@version:\s*(\S+)/m;
45715
+ function readBundledSchemaVersion() {
45716
+ try {
45717
+ const assets = resolveServerAssets();
45718
+ if (!existsSync8(assets.schemaFile))
45719
+ return null;
45720
+ const m = readFileSync6(assets.schemaFile, "utf8").match(SCHEMA_VERSION_RE);
45721
+ return m ? m[1] : null;
45722
+ } catch {
45723
+ return null;
45724
+ }
45725
+ }
45726
+ var SCHEMA_CHECK_NAME = "schema + RPCs";
45505
45727
  async function checkSchemaVersion() {
45506
45728
  const settings = loadSettings();
45507
45729
  if (!settings.supabaseUrl || !settings.supabaseKey) {
45508
45730
  return {
45509
- name: "schema",
45731
+ name: SCHEMA_CHECK_NAME,
45510
45732
  status: "skipped",
45511
45733
  detail: "Supabase config missing; skipped."
45512
45734
  };
@@ -45524,21 +45746,40 @@ async function checkSchemaVersion() {
45524
45746
  });
45525
45747
  if (!resp.ok) {
45526
45748
  return {
45527
- name: "schema",
45749
+ name: SCHEMA_CHECK_NAME,
45528
45750
  status: "error",
45529
- detail: `cerefox_schema_version returned ${resp.status}`,
45530
- hint: "Deploy the schema: `uv run python scripts/db_deploy.py`"
45751
+ detail: `cerefox_schema_version returned ${resp.status} — schema not deployed.`,
45752
+ hint: "Deploy the schema + RPCs (see remediation below)."
45531
45753
  };
45532
45754
  }
45533
45755
  const deployed = await resp.json();
45534
- return {
45535
- name: "schema",
45536
- status: "ok",
45537
- detail: `cerefox_schema_version() → "${deployed}"`
45538
- };
45756
+ const bundled = readBundledSchemaVersion();
45757
+ const level = classifyCompat(deployed, COMPATIBILITY.minSchema, bundled);
45758
+ switch (level) {
45759
+ case "below-min":
45760
+ return {
45761
+ name: SCHEMA_CHECK_NAME,
45762
+ status: "error",
45763
+ detail: `Deployed schema v${deployed} is below the required minimum v${COMPATIBILITY.minSchema}.`,
45764
+ hint: "Update the schema + RPCs (see remediation below)."
45765
+ };
45766
+ case "above-min-but-old":
45767
+ return {
45768
+ name: SCHEMA_CHECK_NAME,
45769
+ status: "warn",
45770
+ detail: `Deployed schema v${deployed} works but is older than this client's bundled v${bundled}.`,
45771
+ hint: "Update the schema + RPCs (see remediation below)."
45772
+ };
45773
+ default:
45774
+ return {
45775
+ name: SCHEMA_CHECK_NAME,
45776
+ status: "ok",
45777
+ detail: `cerefox_schema_version() → "${deployed}"${bundled ? ` (bundled v${bundled})` : ""}`
45778
+ };
45779
+ }
45539
45780
  } catch (err) {
45540
45781
  return {
45541
- name: "schema",
45782
+ name: SCHEMA_CHECK_NAME,
45542
45783
  status: "error",
45543
45784
  detail: `Schema version probe failed: ${err instanceof Error ? err.message : String(err)}`
45544
45785
  };
@@ -45557,9 +45798,9 @@ function hasCerefoxInJsonFile(path) {
45557
45798
  }
45558
45799
  function checkMcpConfigs() {
45559
45800
  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");
45801
+ const claudeCodeUser = join7(home, ".claude.json");
45802
+ const claudeCodeProj = join7(process.cwd(), ".mcp.json");
45803
+ 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
45804
  const found = [];
45564
45805
  if (hasCerefoxInJsonFile(claudeCodeUser))
45565
45806
  found.push("Claude Code (user)");
@@ -45583,8 +45824,8 @@ function checkMcpConfigs() {
45583
45824
  }
45584
45825
  function checkLegacyShadowEnv() {
45585
45826
  const home = homedir4();
45586
- const homeEnv = join6(home, USER_STATE_DIR_NAME, ".env");
45587
- const cwdEnv = join6(process.cwd(), ".env");
45827
+ const homeEnv = join7(home, USER_STATE_DIR_NAME, ".env");
45828
+ const cwdEnv = join7(process.cwd(), ".env");
45588
45829
  if (!existsSync8(homeEnv) || !existsSync8(cwdEnv))
45589
45830
  return null;
45590
45831
  try {
@@ -45666,7 +45907,7 @@ async function checkEdgeFunctionsCompat() {
45666
45907
  compat = await checkServerCompatibility({
45667
45908
  aggregatorUrl: aggregatorUrlFor(settings.supabaseUrl),
45668
45909
  bearer: settings.supabaseAnonKey,
45669
- bundledEf: PKG_VERSION
45910
+ bundledEf: EF_VERSION
45670
45911
  });
45671
45912
  } catch (err) {
45672
45913
  return {
@@ -45689,14 +45930,14 @@ async function checkEdgeFunctionsCompat() {
45689
45930
  name: "edge functions",
45690
45931
  status: "error",
45691
45932
  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`."
45933
+ hint: "Update the Edge Functions (see remediation below)."
45693
45934
  };
45694
45935
  case "above-min-but-old":
45695
45936
  return {
45696
45937
  name: "edge functions",
45697
45938
  status: "warn",
45698
- detail: `Deployed EF v${deployed} works but is older than this client (v${PKG_VERSION}).`,
45699
- hint: "Consider redeploying: `cerefox deploy-server --functions-only`."
45939
+ detail: `Deployed EF v${deployed} works but is older than the bundled Edge Functions (v${EF_VERSION}).`,
45940
+ hint: "Update the Edge Functions (see remediation below)."
45700
45941
  };
45701
45942
  default:
45702
45943
  return {
@@ -45731,7 +45972,7 @@ async function runAllChecks(opts = {}) {
45731
45972
  { name: "legacy env", phase: "Checking legacy env shadowing", run: () => checkLegacyShadowEnv() },
45732
45973
  { name: "supabase", phase: "Probing Supabase Data API", run: () => checkSupabase() },
45733
45974
  { name: "openai", phase: "Probing OpenAI embeddings", run: () => checkOpenAI() },
45734
- { name: "schema", phase: "Reading schema version", run: () => checkSchemaVersion() },
45975
+ { name: "schema + RPCs", phase: "Reading schema + RPC version", run: () => checkSchemaVersion() },
45735
45976
  { name: "edge functions", phase: "Probing Edge Function versions", run: () => checkEdgeFunctionsCompat() },
45736
45977
  { name: "postgres", phase: "Probing Postgres DDL endpoint", run: () => checkPostgres() },
45737
45978
  { name: "mcp clients", phase: "Scanning MCP client configs", run: () => checkMcpConfigs() }
@@ -45760,7 +46001,7 @@ function symbol(status) {
45760
46001
  return cErr.dim("ℹ");
45761
46002
  }
45762
46003
  }
45763
- async function action9(options) {
46004
+ async function action10(options) {
45764
46005
  const useSpinner = !options.json && process.stderr.isTTY;
45765
46006
  const spinner = useSpinner ? ora({ text: "Starting checks…", spinner: "dots", stream: process.stderr }).start() : null;
45766
46007
  const results = await runAllChecks({
@@ -45784,6 +46025,26 @@ async function action9(options) {
45784
46025
  }
45785
46026
  println("");
45786
46027
  }
46028
+ if (!options.json) {
46029
+ const stale = (name) => {
46030
+ const r = results.find((x) => x.name === name);
46031
+ return r != null && (r.status === "error" || r.status === "warn");
46032
+ };
46033
+ const needsSchema = stale("schema + RPCs");
46034
+ const needsEf = stale("edge functions");
46035
+ let remediation = null;
46036
+ if (needsSchema && needsEf) {
46037
+ remediation = "Update the server (schema + RPCs + Edge Functions): cerefox deploy-server";
46038
+ } else if (needsSchema) {
46039
+ remediation = "Update the schema + RPCs: cerefox deploy-server --schema-only";
46040
+ } else if (needsEf) {
46041
+ remediation = "Update the Edge Functions: cerefox deploy-server --functions-only";
46042
+ }
46043
+ if (remediation) {
46044
+ println(cErr.yellow("→ " + remediation));
46045
+ println("");
46046
+ }
46047
+ }
45787
46048
  const errCount = results.filter((r) => r.status === "error").length;
45788
46049
  const warnCount = results.filter((r) => r.status === "warn").length;
45789
46050
  if (errCount > 0) {
@@ -45797,13 +46058,13 @@ async function action9(options) {
45797
46058
  }
45798
46059
  }
45799
46060
  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);
46061
+ program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action10);
45801
46062
  }
45802
46063
 
45803
46064
  // src/cli/commands/get-audit-log.ts
45804
46065
  init_cli_core();
45805
46066
  init_client();
45806
- async function action10(options) {
46067
+ async function action11(options) {
45807
46068
  const limit = parsePositiveInt(options.limit, "--limit", 50);
45808
46069
  const client = getClient();
45809
46070
  const data = await client.rpc("cerefox_list_audit_entries", {
@@ -45841,14 +46102,14 @@ async function action10(options) {
45841
46102
  })));
45842
46103
  }
45843
46104
  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);
46105
+ 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
46106
  }
45846
46107
 
45847
46108
  // src/cli/commands/get-doc.ts
45848
46109
  init_cli_core();
45849
46110
  init_cli_core();
45850
46111
  init_client();
45851
- async function action11(documentId, options) {
46112
+ async function action12(documentId, options) {
45852
46113
  const client = getClient();
45853
46114
  const rows = await client.rpc("cerefox_get_document", {
45854
46115
  p_document_id: documentId,
@@ -45878,7 +46139,7 @@ async function action11(documentId, options) {
45878
46139
  println(doc.full_content);
45879
46140
  }
45880
46141
  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);
46142
+ 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
46143
  }
45883
46144
 
45884
46145
  // src/cli/commands/ingest.ts
@@ -46615,7 +46876,7 @@ async function readContent(path, paste) {
46615
46876
  const titleFromPath = basename2(path, extname2(path));
46616
46877
  return { content, titleFromPath };
46617
46878
  }
46618
- async function action12(path, options) {
46879
+ async function action13(path, options) {
46619
46880
  const { content, titleFromPath } = await readContent(path, Boolean(options.paste));
46620
46881
  const title = options.title ?? titleFromPath;
46621
46882
  if (!title || title.trim() === "") {
@@ -46687,7 +46948,7 @@ async function action12(path, options) {
46687
46948
  }
46688
46949
  }
46689
46950
  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);
46951
+ 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
46952
  }
46692
46953
 
46693
46954
  // src/cli/commands/ingest-dir.ts
@@ -46696,7 +46957,7 @@ init_cli_core();
46696
46957
  init_config();
46697
46958
  var import_cli_progress = __toESM(require_cli_progress(), 1);
46698
46959
  import { readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
46699
- import { basename as basename3, extname as extname3, join as join7 } from "node:path";
46960
+ import { basename as basename3, extname as extname3, join as join8 } from "node:path";
46700
46961
  function walk(dir, extensions) {
46701
46962
  let entries;
46702
46963
  try {
@@ -46706,7 +46967,7 @@ function walk(dir, extensions) {
46706
46967
  }
46707
46968
  const files = [];
46708
46969
  for (const name of entries) {
46709
- const full = join7(dir, name);
46970
+ const full = join8(dir, name);
46710
46971
  let stat;
46711
46972
  try {
46712
46973
  stat = statSync3(full);
@@ -46723,7 +46984,7 @@ function walk(dir, extensions) {
46723
46984
  }
46724
46985
  return files;
46725
46986
  }
46726
- async function action13(dir, options) {
46987
+ async function action14(dir, options) {
46727
46988
  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
46989
  const files = walk(dir, extensions);
46729
46990
  if (files.length === 0) {
@@ -46796,7 +47057,7 @@ async function action13(dir, options) {
46796
47057
  }
46797
47058
  }
46798
47059
  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);
47060
+ 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
47061
  }
46801
47062
 
46802
47063
  // src/cli/commands/init.ts
@@ -46812,7 +47073,7 @@ import {
46812
47073
  writeFileSync as writeFileSync3
46813
47074
  } from "node:fs";
46814
47075
  import { homedir as homedir5 } from "node:os";
46815
- import { dirname as dirname4, join as join8 } from "node:path";
47076
+ import { dirname as dirname4, join as join9 } from "node:path";
46816
47077
  async function readConfigFile(path) {
46817
47078
  if (!existsSync9(path)) {
46818
47079
  throw userError(`--config file not found: ${path}`);
@@ -47062,11 +47323,11 @@ async function maybeOfferServerDeploy() {
47062
47323
  if (compareSemver(deployed, COMPATIBILITY.minSchema) < 0) {
47063
47324
  println(c.bold("Schema deploy"));
47064
47325
  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);
47326
+ const yes = await confirm(" Update the server now (applies pending migrations, refreshes RPCs + EFs)?", true);
47066
47327
  if (yes)
47067
- launchDeployServer(["--reset"]);
47328
+ launchDeployServer();
47068
47329
  else
47069
- println(c.dim(" Skipped. Run `cerefox deploy-server --reset` when ready."));
47330
+ println(c.dim(" Skipped. Run `cerefox deploy-server` when ready."));
47070
47331
  println("");
47071
47332
  return;
47072
47333
  }
@@ -47126,9 +47387,9 @@ function writeAnswersTo(target, answers) {
47126
47387
  }
47127
47388
  }
47128
47389
  }
47129
- async function action15(options) {
47130
- const homeEnv = join8(homedir5(), USER_STATE_DIR_NAME, ".env");
47131
- const cwdEnv = join8(process.cwd(), ".env");
47390
+ async function action16(options) {
47391
+ const homeEnv = join9(homedir5(), USER_STATE_DIR_NAME, ".env");
47392
+ const cwdEnv = join9(process.cwd(), ".env");
47132
47393
  const explicitDir = (process.env.CEREFOX_CONFIG_DIR ?? "").trim();
47133
47394
  if (explicitDir) {
47134
47395
  const target2 = resolveEnvFile();
@@ -47235,13 +47496,13 @@ async function action15(options) {
47235
47496
  await postWriteLifecycle(target, options);
47236
47497
  }
47237
47498
  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);
47499
+ 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
47500
  }
47240
47501
 
47241
47502
  // src/cli/commands/list-docs.ts
47242
47503
  init_cli_core();
47243
47504
  init_client();
47244
- async function action16(options) {
47505
+ async function action17(options) {
47245
47506
  const limit = parsePositiveInt(options.limit, "--limit", 100);
47246
47507
  const client = getClient();
47247
47508
  let projectId;
@@ -47296,13 +47557,13 @@ async function action16(options) {
47296
47557
  })));
47297
47558
  }
47298
47559
  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);
47560
+ 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
47561
  }
47301
47562
 
47302
47563
  // src/cli/commands/list-metadata-keys.ts
47303
47564
  init_cli_core();
47304
47565
  init_client();
47305
- async function action17(options) {
47566
+ async function action18(options) {
47306
47567
  const client = getClient();
47307
47568
  const data = await client.rpc("cerefox_list_metadata_keys");
47308
47569
  if (data === null) {
@@ -47330,13 +47591,13 @@ async function action17(options) {
47330
47591
  })));
47331
47592
  }
47332
47593
  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);
47594
+ 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
47595
  }
47335
47596
 
47336
47597
  // src/cli/commands/list-projects.ts
47337
47598
  init_cli_core();
47338
47599
  init_client();
47339
- async function action18(options) {
47600
+ async function action19(options) {
47340
47601
  const client = getClient();
47341
47602
  const { data, error: error2 } = await client.raw.from("cerefox_projects").select("id, name, description, created_at").order("name", { ascending: true });
47342
47603
  if (error2) {
@@ -47365,13 +47626,13 @@ async function action18(options) {
47365
47626
  })), "(no projects)");
47366
47627
  }
47367
47628
  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);
47629
+ 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
47630
  }
47370
47631
 
47371
47632
  // src/cli/commands/list-versions.ts
47372
47633
  init_cli_core();
47373
47634
  init_client();
47374
- async function action19(documentId, options) {
47635
+ async function action20(documentId, options) {
47375
47636
  const client = getClient();
47376
47637
  const data = await client.rpc("cerefox_list_document_versions", {
47377
47638
  p_document_id: documentId
@@ -47412,7 +47673,7 @@ async function action19(documentId, options) {
47412
47673
  })));
47413
47674
  }
47414
47675
  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);
47676
+ 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
47677
  }
47417
47678
 
47418
47679
  // src/cli/commands/mcp.ts
@@ -47427,7 +47688,7 @@ function registerMcp(program2) {
47427
47688
  // src/cli/commands/metadata-search.ts
47428
47689
  init_cli_core();
47429
47690
  init_client();
47430
- async function action20(options) {
47691
+ async function action21(options) {
47431
47692
  const metadataFilter = parseJsonObjectArg(options.metadataFilter, "--metadata-filter");
47432
47693
  if (!metadataFilter || Object.keys(metadataFilter).length === 0) {
47433
47694
  throw userError("--metadata-filter is required and must be a non-empty JSON object.", `Example: --metadata-filter '{"type":"decision-log"}'.`);
@@ -47490,7 +47751,7 @@ async function action20(options) {
47490
47751
  }
47491
47752
  }
47492
47753
  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);
47754
+ 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
47755
  }
47495
47756
 
47496
47757
  // src/cli/commands/reindex.ts
@@ -47498,7 +47759,7 @@ init_dist4();
47498
47759
  init_cli_core();
47499
47760
  init_config();
47500
47761
  var DEFAULT_MODEL = "text-embedding-3-small";
47501
- async function action21(options) {
47762
+ async function action22(options) {
47502
47763
  const settings = loadSettings();
47503
47764
  if (!settings.supabaseUrl || !settings.supabaseKey) {
47504
47765
  throw userError("Supabase credentials not configured — run `cerefox init` first.");
@@ -47577,7 +47838,7 @@ ${c2.content}`;
47577
47838
  }
47578
47839
  }
47579
47840
  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);
47841
+ 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
47842
  }
47582
47843
 
47583
47844
  // src/cli/commands/restore.ts
@@ -47585,12 +47846,12 @@ init_cli_core();
47585
47846
  init_client();
47586
47847
  import { existsSync as existsSync10, readFileSync as readFileSync11, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
47587
47848
  import { homedir as homedir6 } from "node:os";
47588
- import { join as join9, resolve as resolve4 } from "node:path";
47849
+ import { join as join10, resolve as resolve4 } from "node:path";
47589
47850
  function expandHome2(path) {
47590
47851
  if (path === "~")
47591
47852
  return homedir6();
47592
47853
  if (path.startsWith("~/"))
47593
- return join9(homedir6(), path.slice(2));
47854
+ return join10(homedir6(), path.slice(2));
47594
47855
  return path;
47595
47856
  }
47596
47857
  function resolveBackupFile(target) {
@@ -47601,13 +47862,13 @@ function resolveBackupFile(target) {
47601
47862
  const stat = statSync4(path);
47602
47863
  if (stat.isFile())
47603
47864
  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);
47865
+ 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
47866
  if (candidates.length === 0) {
47606
47867
  throw userError(`No cerefox-*.json files in ${path}`);
47607
47868
  }
47608
- return join9(path, candidates[0].name);
47869
+ return join10(path, candidates[0].name);
47609
47870
  }
47610
- async function action22(target, options) {
47871
+ async function action23(target, options) {
47611
47872
  const file = resolveBackupFile(target);
47612
47873
  let payload;
47613
47874
  try {
@@ -47663,7 +47924,7 @@ async function action22(target, options) {
47663
47924
  }
47664
47925
  }
47665
47926
  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);
47927
+ 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
47928
  }
47668
47929
 
47669
47930
  // src/cli/commands/search.ts
@@ -47688,7 +47949,7 @@ async function embedQuery(query) {
47688
47949
  }
47689
47950
 
47690
47951
  // src/cli/commands/search.ts
47691
- async function action23(query, options) {
47952
+ async function action24(query, options) {
47692
47953
  if (!query || query.trim() === "") {
47693
47954
  throw userError("Empty query.");
47694
47955
  }
@@ -47822,7 +48083,7 @@ async function action23(query, options) {
47822
48083
  }
47823
48084
  }
47824
48085
  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);
48086
+ 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
48087
  }
47827
48088
 
47828
48089
  // src/cli/commands/self-update.ts
@@ -47869,7 +48130,7 @@ async function fetchLatestVersion() {
47869
48130
  }
47870
48131
  return body.version;
47871
48132
  }
47872
- async function action24(options) {
48133
+ async function action25(options) {
47873
48134
  let target;
47874
48135
  try {
47875
48136
  target = options.version ?? await fetchLatestVersion();
@@ -47921,7 +48182,7 @@ async function action24(options) {
47921
48182
  }
47922
48183
  function registerSelfUpdate(program2) {
47923
48184
  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);
48185
+ 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
48186
  declaration(program2.command("self-update"));
47926
48187
  declaration(program2.command("upgrade"));
47927
48188
  }
@@ -47940,7 +48201,7 @@ function symbol2(status) {
47940
48201
  return cErr.dim("ℹ");
47941
48202
  }
47942
48203
  }
47943
- async function action25(options) {
48204
+ async function action26(options) {
47944
48205
  const useSpinner = !options.json && process.stderr.isTTY;
47945
48206
  const spinner = useSpinner ? ora({ text: "Starting checks…", spinner: "dots", stream: process.stderr }).start() : null;
47946
48207
  const results = await runFastChecks({
@@ -47961,7 +48222,7 @@ async function action25(options) {
47961
48222
  }
47962
48223
  }
47963
48224
  function registerStatus(program2) {
47964
- program2.command("status").description("Quick sanity check (fast subset of `cerefox doctor`).").option("--json", "Emit machine-readable JSON.").action(action25);
48225
+ program2.command("status").description("Quick sanity check (fast subset of `cerefox doctor`).").option("--json", "Emit machine-readable JSON.").action(action26);
47965
48226
  }
47966
48227
 
47967
48228
  // src/cli/commands/sync-docs.ts
@@ -47975,14 +48236,14 @@ import {
47975
48236
  readdirSync as readdirSync6,
47976
48237
  statSync as statSync5
47977
48238
  } from "node:fs";
47978
- import { basename as basename5, extname as extname5, join as join10, relative } from "node:path";
48239
+ import { basename as basename5, extname as extname5, join as join11, relative } from "node:path";
47979
48240
  var ROOT_LEVEL_DOCS = ["README.md", "AGENT_GUIDE.md", "AGENT_QUICK_REFERENCE.md"];
47980
48241
  function walkMarkdown(dir) {
47981
48242
  const out = [];
47982
48243
  if (!existsSync11(dir))
47983
48244
  return out;
47984
48245
  for (const name of readdirSync6(dir)) {
47985
- const full = join10(dir, name);
48246
+ const full = join11(dir, name);
47986
48247
  let stat;
47987
48248
  try {
47988
48249
  stat = statSync5(full);
@@ -47997,16 +48258,16 @@ function walkMarkdown(dir) {
47997
48258
  }
47998
48259
  return out;
47999
48260
  }
48000
- async function action26(options) {
48261
+ async function action27(options) {
48001
48262
  const cwd = process.cwd();
48002
48263
  const project = options.project ?? "cerefox";
48003
48264
  const targets = [];
48004
48265
  for (const rel of ROOT_LEVEL_DOCS) {
48005
- const abs = join10(cwd, rel);
48266
+ const abs = join11(cwd, rel);
48006
48267
  if (existsSync11(abs))
48007
48268
  targets.push({ abs, rel });
48008
48269
  }
48009
- for (const abs of walkMarkdown(join10(cwd, "docs"))) {
48270
+ for (const abs of walkMarkdown(join11(cwd, "docs"))) {
48010
48271
  targets.push({ abs, rel: relative(cwd, abs) });
48011
48272
  }
48012
48273
  if (targets.length === 0) {
@@ -48059,7 +48320,7 @@ async function action26(options) {
48059
48320
  }
48060
48321
  }
48061
48322
  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);
48323
+ 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
48324
  }
48064
48325
 
48065
48326
  // src/cli/program.ts
@@ -49395,7 +49656,7 @@ var baseMimes = _baseMimes;
49395
49656
 
49396
49657
  // ../../node_modules/.bun/@hono+node-server@2.0.4+1bbe96acb4c5ebf1/node_modules/@hono/node-server/dist/serve-static.mjs
49397
49658
  import { createReadStream, existsSync as existsSync12, statSync as statSync6 } from "node:fs";
49398
- import { join as join11 } from "node:path";
49659
+ import { join as join12 } from "node:path";
49399
49660
  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
49661
  var ENCODINGS = {
49401
49662
  br: ".br",
@@ -49444,11 +49705,11 @@ var serveStatic = (options = { root: "" }) => {
49444
49705
  await options.onNotFound?.(c2.req.path, c2);
49445
49706
  return next();
49446
49707
  }
49447
- let path = join11(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c2) : filename);
49708
+ let path = join12(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c2) : filename);
49448
49709
  let stats = getStats(path);
49449
49710
  if (stats && stats.isDirectory()) {
49450
49711
  const indexFile = options.index ?? "index.html";
49451
- path = join11(path, indexFile);
49712
+ path = join12(path, indexFile);
49452
49713
  stats = getStats(path);
49453
49714
  }
49454
49715
  if (!stats) {
@@ -49507,7 +49768,7 @@ var serveStatic = (options = { root: "" }) => {
49507
49768
  // src/web/server.ts
49508
49769
  import { existsSync as existsSync15 } from "node:fs";
49509
49770
  import { readFileSync as readFileSync14 } from "node:fs";
49510
- import { join as join14 } from "node:path";
49771
+ import { join as join15 } from "node:path";
49511
49772
 
49512
49773
  // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/compose.js
49513
49774
  var compose = (middleware, onError, onNotFound) => {
@@ -51314,7 +51575,7 @@ function registerConfigRoutes(app, ctx) {
51314
51575
  }
51315
51576
 
51316
51577
  // 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;
51578
+ 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
51579
  var DOC_COLS = "id, title, source, source_path, content_hash, metadata, chunk_count, total_chars, review_status, created_at, updated_at, deleted_at";
51319
51580
  function jsonByteLength(value) {
51320
51581
  return Buffer.byteLength(JSON.stringify(value), "utf8");
@@ -51760,7 +52021,7 @@ function registerDiscoveryRoutes(app, ctx) {
51760
52021
  if (!path) {
51761
52022
  return c2.json({ tried_path: "", anchor, matches: [] });
51762
52023
  }
51763
- if (UUID_RE.test(path)) {
52024
+ if (UUID_RE2.test(path)) {
51764
52025
  if (path === fromDocId) {
51765
52026
  return c2.json({ tried_path: path, anchor, matches: [] });
51766
52027
  }
@@ -52361,7 +52622,7 @@ import {
52361
52622
  readdirSync as readdirSync7,
52362
52623
  statSync as statSync7
52363
52624
  } from "node:fs";
52364
- import { basename as basename6, dirname as dirname5, join as join12, resolve as resolve5 } from "node:path";
52625
+ import { basename as basename6, dirname as dirname5, join as join13, resolve as resolve5 } from "node:path";
52365
52626
  import { fileURLToPath as fileURLToPath3 } from "node:url";
52366
52627
  var TOP_LEVEL_DOCS = [
52367
52628
  { filename: "README.md", path: "README.md", category: "readme" },
@@ -52382,27 +52643,27 @@ function moduleDir2() {
52382
52643
  function resolveDocsRoots() {
52383
52644
  const here = moduleDir2();
52384
52645
  const pkgRootCandidates = [
52385
- join12(here, "..", ".."),
52386
- join12(here, "..", "..", "..", "..")
52646
+ join13(here, "..", ".."),
52647
+ join13(here, "..", "..", "..", "..")
52387
52648
  ];
52388
52649
  let pkgGuides = null;
52389
52650
  let pkgTopLevel = null;
52390
52651
  for (const pkg of pkgRootCandidates) {
52391
- const guides = join12(pkg, "docs", "guides");
52652
+ const guides = join13(pkg, "docs", "guides");
52392
52653
  if (existsSync13(guides) && statSync7(guides).isDirectory()) {
52393
52654
  pkgGuides = guides;
52394
52655
  pkgTopLevel = pkg;
52395
52656
  break;
52396
52657
  }
52397
52658
  }
52398
- const repoCandidate = join12(here, "..", "..", "..", "..");
52399
- const repoGuides = join12(repoCandidate, "docs", "guides");
52659
+ const repoCandidate = join13(here, "..", "..", "..", "..");
52660
+ const repoGuides = join13(repoCandidate, "docs", "guides");
52400
52661
  const repoTopLevel = repoCandidate;
52401
52662
  return {
52402
52663
  pkgGuides,
52403
52664
  pkgTopLevel,
52404
52665
  repoGuides: existsSync13(repoGuides) ? repoGuides : null,
52405
- repoTopLevel: existsSync13(join12(repoTopLevel, "README.md")) ? repoTopLevel : null
52666
+ repoTopLevel: existsSync13(join13(repoTopLevel, "README.md")) ? repoTopLevel : null
52406
52667
  };
52407
52668
  }
52408
52669
  function readH1(filePath) {
@@ -52427,7 +52688,7 @@ function listBundledDocs2() {
52427
52688
  const topRoot = pkgTopLevel ?? repoTopLevel;
52428
52689
  if (topRoot) {
52429
52690
  for (const t of TOP_LEVEL_DOCS) {
52430
- const abs = join12(topRoot, t.filename);
52691
+ const abs = join13(topRoot, t.filename);
52431
52692
  if (existsSync13(abs)) {
52432
52693
  entries.push(entryForFile(abs, t.path, t.category));
52433
52694
  }
@@ -52437,7 +52698,7 @@ function listBundledDocs2() {
52437
52698
  if (guidesRoot) {
52438
52699
  const names = readdirSync7(guidesRoot).filter((n) => n.endsWith(".md")).sort();
52439
52700
  for (const name of names) {
52440
- const abs = join12(guidesRoot, name);
52701
+ const abs = join13(guidesRoot, name);
52441
52702
  entries.push(entryForFile(abs, `guides/${name}`, "guide"));
52442
52703
  }
52443
52704
  }
@@ -52482,7 +52743,7 @@ var VERSION_INFO = {
52482
52743
  git_commit_short: resolveGitCommitShort(),
52483
52744
  build_date: process.env.CEREFOX_BUILD_DATE ?? null
52484
52745
  };
52485
- var SCHEMA_VERSION_RE = /^--\s*@version:\s*(\S+)/m;
52746
+ var SCHEMA_VERSION_RE2 = /^--\s*@version:\s*(\S+)/m;
52486
52747
  function registerMetaRoutes(app, ctx) {
52487
52748
  app.get("/api/v1/version", (c2) => c2.json(VERSION_INFO));
52488
52749
  app.get("/api/v1/docs", (c2) => c2.json(listBundledDocs2()));
@@ -52501,16 +52762,16 @@ function registerMetaRoutes(app, ctx) {
52501
52762
  try {
52502
52763
  const { readFileSync: readFileSync14, existsSync: existsSync14 } = await import("node:fs");
52503
52764
  const { fileURLToPath: fileURLToPath4 } = await import("node:url");
52504
- const { dirname: dirname6, join: join13 } = await import("node:path");
52765
+ const { dirname: dirname6, join: join14 } = await import("node:path");
52505
52766
  const here = dirname6(fileURLToPath4(import.meta.url));
52506
52767
  const candidates = [
52507
- join13(here, "..", "..", "..", "db", "schema.sql"),
52508
- join13(here, "..", "..", "..", "..", "..", "src", "cerefox", "db", "schema.sql")
52768
+ join14(here, "..", "..", "..", "db", "schema.sql"),
52769
+ join14(here, "..", "..", "..", "..", "..", "src", "cerefox", "db", "schema.sql")
52509
52770
  ];
52510
52771
  for (const path of candidates) {
52511
52772
  if (existsSync14(path)) {
52512
52773
  const sql = readFileSync14(path, "utf8");
52513
- const match2 = sql.match(SCHEMA_VERSION_RE);
52774
+ const match2 = sql.match(SCHEMA_VERSION_RE2);
52514
52775
  bundled = match2 ? match2[1] : null;
52515
52776
  break;
52516
52777
  }
@@ -52620,20 +52881,20 @@ function registerProjectsRoutes(app, ctx) {
52620
52881
 
52621
52882
  // src/web/static.ts
52622
52883
  import { existsSync as existsSync14, statSync as statSync8 } from "node:fs";
52623
- import { dirname as dirname6, join as join13 } from "node:path";
52884
+ import { dirname as dirname6, join as join14 } from "node:path";
52624
52885
  import { fileURLToPath as fileURLToPath4 } from "node:url";
52625
52886
  function moduleDir3() {
52626
52887
  return dirname6(fileURLToPath4(import.meta.url));
52627
52888
  }
52628
52889
  function isUsableSpaDir(dir) {
52629
- return existsSync14(dir) && statSync8(dir).isDirectory() && existsSync14(join13(dir, "index.html"));
52890
+ return existsSync14(dir) && statSync8(dir).isDirectory() && existsSync14(join14(dir, "index.html"));
52630
52891
  }
52631
52892
  function resolveSpaDist() {
52632
52893
  const here = moduleDir3();
52633
52894
  const candidates = [
52634
- join13(here, "..", "frontend"),
52635
- join13(here, "..", "..", "..", "..", "frontend", "dist"),
52636
- join13(here, "..", "..", "dist", "frontend")
52895
+ join14(here, "..", "frontend"),
52896
+ join14(here, "..", "..", "..", "..", "frontend", "dist"),
52897
+ join14(here, "..", "..", "dist", "frontend")
52637
52898
  ];
52638
52899
  for (const c2 of candidates) {
52639
52900
  if (isUsableSpaDir(c2))
@@ -52644,8 +52905,8 @@ function resolveSpaDist() {
52644
52905
  function resolveStaticDir() {
52645
52906
  const here = moduleDir3();
52646
52907
  const candidates = [
52647
- join13(here, "..", "static"),
52648
- join13(here, "..", "..", "..", "..", "web", "static")
52908
+ join14(here, "..", "static"),
52909
+ join14(here, "..", "..", "..", "..", "web", "static")
52649
52910
  ];
52650
52911
  for (const c2 of candidates) {
52651
52912
  if (existsSync14(c2) && statSync8(c2).isDirectory())
@@ -52691,11 +52952,12 @@ var ROOT_REDIRECT_HTML = `<!DOCTYPE html>
52691
52952
 
52692
52953
  // src/web/server.ts
52693
52954
  init_meta();
52955
+ init_cli_core();
52694
52956
  init_config();
52695
52957
  function buildApp(ctx = buildWebContext()) {
52696
52958
  const app = new Hono2;
52697
52959
  if (true) {
52698
- app.use(logger());
52960
+ app.use(logger((message, ...rest) => console.log(`${localTimestamp()} ${message}`, ...rest)));
52699
52961
  }
52700
52962
  registerMetaRoutes(app, ctx);
52701
52963
  if (ctx) {
@@ -52731,7 +52993,7 @@ function buildApp(ctx = buildWebContext()) {
52731
52993
  root: spaDist,
52732
52994
  rewriteRequestPath: (path) => path.replace(/^\/app/, "") || "/"
52733
52995
  }));
52734
- const indexPath = join14(spaDist, "index.html");
52996
+ const indexPath = join15(spaDist, "index.html");
52735
52997
  if (existsSync15(indexPath)) {
52736
52998
  const indexHtml = readFileSync14(indexPath, "utf8");
52737
52999
  app.get("/app/*", (c2) => c2.html(indexHtml));
@@ -52752,7 +53014,7 @@ async function assertServerCompatible() {
52752
53014
  compat2 = await checkServerCompatibility({
52753
53015
  aggregatorUrl: aggregatorUrlFor(settings.supabaseUrl),
52754
53016
  bearer: settings.supabaseAnonKey,
52755
- bundledEf: PKG_VERSION
53017
+ bundledEf: EF_VERSION
52756
53018
  });
52757
53019
  } catch {
52758
53020
  return;
@@ -52799,10 +53061,10 @@ import {
52799
53061
  writeFileSync as writeFileSync4
52800
53062
  } from "node:fs";
52801
53063
  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");
53064
+ import { join as join16 } from "node:path";
53065
+ var STATE_DIR = join16(homedir7(), ".cerefox");
53066
+ var PID_FILE = join16(STATE_DIR, "web.pid");
53067
+ var LOG_FILE = join16(STATE_DIR, "web.log");
52806
53068
  var daemonPaths = { stateDir: STATE_DIR, pidFile: PID_FILE, logFile: LOG_FILE };
52807
53069
  function ensureStateDir() {
52808
53070
  if (!existsSync16(STATE_DIR))
@@ -52950,11 +53212,11 @@ async function runForeground(host, port, watch) {
52950
53212
  }
52951
53213
  try {
52952
53214
  const handle = await buildWebServer({ host, port });
52953
- info(`Cerefox web listening on http://${handle.host}:${handle.port}/`);
53215
+ println(`${localTimestamp()} Cerefox web listening on http://${handle.host}:${handle.port}/`);
52954
53216
  println(` Web UI: http://${handle.host}:${handle.port}/app/`);
52955
53217
  println(` API: http://${handle.host}:${handle.port}/api/v1/`);
52956
53218
  const shutdown = async (signal) => {
52957
- info(`Received ${signal}; shutting down.`);
53219
+ println(`${localTimestamp()} Received ${signal}; shutting down.`);
52958
53220
  await handle.close().catch(() => {});
52959
53221
  process.exit(0);
52960
53222
  };
@@ -53056,7 +53318,7 @@ function buildProgram() {
53056
53318
  Command groups (each row in the list above falls into one):
53057
53319
  ` + ` READS search · get-doc · list-docs · list-versions · list-projects
53058
53320
  ` + ` · list-metadata-keys · metadata-search · get-audit-log
53059
- ` + ` WRITES ingest · ingest-dir · delete-doc
53321
+ ` + ` WRITES ingest · ingest-dir · delete-doc · delete-project
53060
53322
  ` + ` SERVERS mcp · web
53061
53323
  ` + ` LIFECYCLE init · doctor · status · configure-agent · self-update · upgrade · sync-self-docs · deploy-server
53062
53324
  ` + ` OPS backup · restore · sync-docs · docs · reindex · config-get · config-set · completion
@@ -53081,6 +53343,7 @@ Learn more:
53081
53343
  registerIngest(program2);
53082
53344
  registerIngestDir(program2);
53083
53345
  registerDeleteDoc(program2);
53346
+ registerDeleteProject(program2);
53084
53347
  registerMcp(program2);
53085
53348
  registerWeb(program2);
53086
53349
  registerInit(program2);