caveat-cli 0.14.10 → 0.15.0

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.
@@ -9071,6 +9071,141 @@ function* walkMarkdown(root) {
9071
9071
  }
9072
9072
  }
9073
9073
 
9074
+ // ../../packages/core/dist/autoReindex.js
9075
+ import { createHash } from "node:crypto";
9076
+ import {
9077
+ existsSync as existsSync3,
9078
+ mkdirSync as mkdirSync2,
9079
+ readdirSync as readdirSync3,
9080
+ readFileSync as readFileSync3,
9081
+ renameSync,
9082
+ statSync as statSync2,
9083
+ unlinkSync,
9084
+ writeFileSync
9085
+ } from "node:fs";
9086
+ import { join as join3, relative as relative2 } from "node:path";
9087
+ function sourceRoots(paths) {
9088
+ const roots = [];
9089
+ if (existsSync3(paths.entriesDir)) roots.push({ source: "own", root: paths.entriesDir });
9090
+ if (!existsSync3(paths.communityDir)) return roots;
9091
+ for (const entry of requireDirectories(paths.communityDir)) {
9092
+ const root = join3(paths.communityDir, entry, "entries");
9093
+ if (existsSync3(root)) roots.push({ source: `community/${entry}`, root });
9094
+ }
9095
+ return roots;
9096
+ }
9097
+ function requireDirectories(root) {
9098
+ return readdirSync3(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
9099
+ }
9100
+ function computeEntriesDigest(paths) {
9101
+ const lines = [];
9102
+ for (const { source, root } of sourceRoots(paths)) {
9103
+ for (const filePath of walkMarkdown(root)) {
9104
+ const stat = statSync2(filePath);
9105
+ const rel = relative2(root, filePath).replace(/\\/g, "/");
9106
+ lines.push(`${source} ${rel} ${stat.mtimeMs} ${stat.size}`);
9107
+ }
9108
+ }
9109
+ lines.sort();
9110
+ return {
9111
+ digest: createHash("sha256").update(lines.join("\n")).digest("hex"),
9112
+ fileCount: lines.length
9113
+ };
9114
+ }
9115
+ function indexDir(caveatHome) {
9116
+ return join3(caveatHome, "index");
9117
+ }
9118
+ function digestMarkerPath(caveatHome) {
9119
+ return join3(indexDir(caveatHome), ".entries-digest");
9120
+ }
9121
+ function readDigestMarker(caveatHome) {
9122
+ try {
9123
+ const value = JSON.parse(readFileSync3(digestMarkerPath(caveatHome), "utf-8"));
9124
+ if (value !== null && typeof value === "object" && typeof value.digest === "string" && typeof value.fileCount === "number" && typeof value.generatedAt === "string") return value;
9125
+ } catch {
9126
+ }
9127
+ return null;
9128
+ }
9129
+ function writeDigestMarker(caveatHome, value) {
9130
+ const dir = indexDir(caveatHome);
9131
+ mkdirSync2(dir, { recursive: true });
9132
+ const path = digestMarkerPath(caveatHome);
9133
+ const temporary = `${path}.${process.pid}.tmp`;
9134
+ writeFileSync(temporary, JSON.stringify({ ...value, generatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
9135
+ renameSync(temporary, path);
9136
+ }
9137
+ function lockPath(caveatHome) {
9138
+ return join3(indexDir(caveatHome), ".reindex-lock");
9139
+ }
9140
+ function tryCreateLock(path) {
9141
+ try {
9142
+ writeFileSync(path, String(process.pid), { flag: "wx" });
9143
+ return true;
9144
+ } catch (err) {
9145
+ if (err.code === "EEXIST") return false;
9146
+ throw err;
9147
+ }
9148
+ }
9149
+ function acquireReindexLock(caveatHome) {
9150
+ const dir = indexDir(caveatHome);
9151
+ mkdirSync2(dir, { recursive: true });
9152
+ const path = lockPath(caveatHome);
9153
+ if (tryCreateLock(path)) return { path };
9154
+ let pid;
9155
+ try {
9156
+ pid = Number.parseInt(readFileSync3(path, "utf-8").trim(), 10);
9157
+ if (!Number.isInteger(pid) || pid <= 0) return null;
9158
+ } catch (err) {
9159
+ if (err.code === "ENOENT") return tryCreateLock(path) ? { path } : null;
9160
+ return null;
9161
+ }
9162
+ try {
9163
+ process.kill(pid, 0);
9164
+ return null;
9165
+ } catch (err) {
9166
+ const code = err.code;
9167
+ if (code !== "ESRCH") return null;
9168
+ }
9169
+ try {
9170
+ unlinkSync(path);
9171
+ } catch (err) {
9172
+ if (err.code !== "ENOENT") throw err;
9173
+ }
9174
+ return tryCreateLock(path) ? { path } : null;
9175
+ }
9176
+ function releaseReindexLock(lock) {
9177
+ try {
9178
+ unlinkSync(lock.path);
9179
+ } catch (err) {
9180
+ if (err.code !== "ENOENT") throw err;
9181
+ }
9182
+ }
9183
+ function reindexAllSources(opts) {
9184
+ const { db, paths, logger } = opts;
9185
+ const perSource = {};
9186
+ if (existsSync3(paths.entriesDir)) {
9187
+ perSource.own = scanSource({ db, source: "own", entriesRoot: paths.entriesDir });
9188
+ } else {
9189
+ logger.warn(`entries dir not found; preserving own index rows: ${paths.entriesDir}`);
9190
+ }
9191
+ const presentCommunitySources = /* @__PURE__ */ new Set();
9192
+ if (existsSync3(paths.communityDir)) {
9193
+ for (const handle of requireDirectories(paths.communityDir)) {
9194
+ const source = `community/${handle}`;
9195
+ const root = join3(paths.communityDir, handle, "entries");
9196
+ presentCommunitySources.add(source);
9197
+ if (!existsSync3(root)) continue;
9198
+ perSource[source] = scanSource({ db, source, entriesRoot: root });
9199
+ }
9200
+ }
9201
+ const rows = db.prepare("SELECT DISTINCT source FROM entries WHERE source LIKE 'community/%'").all();
9202
+ for (const { source } of rows) {
9203
+ if (presentCommunitySources.has(source)) continue;
9204
+ db.prepare("DELETE FROM entries WHERE source = ?").run(source);
9205
+ }
9206
+ return { perSource, fileCount: computeEntriesDigest(paths).fileCount };
9207
+ }
9208
+
9074
9209
  // ../../packages/core/dist/repository.js
9075
9210
  var SYMPTOM_EXCERPT_LENGTH = 200;
9076
9211
  function sanitizeFtsQuery(raw) {
@@ -9154,25 +9289,25 @@ function toSearchResult(row) {
9154
9289
  title: row.title,
9155
9290
  symptomExcerpt: symptom.slice(0, SYMPTOM_EXCERPT_LENGTH),
9156
9291
  confidence: row.confidence,
9157
- visibility: row.visibility ?? "public",
9292
+ visibility: row.visibility ?? "private",
9158
9293
  environment: fm.environment ?? {}
9159
9294
  };
9160
9295
  }
9161
9296
 
9162
9297
  // ../../packages/core/dist/paths.js
9163
- import { existsSync as existsSync3 } from "node:fs";
9164
- import { dirname as dirname2, isAbsolute, join as join3, resolve } from "node:path";
9298
+ import { existsSync as existsSync4 } from "node:fs";
9299
+ import { dirname as dirname2, isAbsolute, join as join4, resolve } from "node:path";
9165
9300
  import { fileURLToPath as fileURLToPath2 } from "node:url";
9166
9301
  function expandHome(p2, userHome) {
9167
9302
  if (p2 === "~" || p2.startsWith("~/") || p2.startsWith("~\\")) {
9168
- return join3(userHome, p2.slice(1));
9303
+ return join4(userHome, p2.slice(1));
9169
9304
  }
9170
9305
  return p2;
9171
9306
  }
9172
9307
  function findCaveatHome(userHome) {
9173
9308
  const fromEnv = process.env.CAVEAT_HOME;
9174
9309
  if (fromEnv && fromEnv.length > 0) return fromEnv;
9175
- return join3(userHome, ".caveat");
9310
+ return join4(userHome, ".caveat");
9176
9311
  }
9177
9312
  function resolvePaths(caveatHome, knowledgeRepo, userHome) {
9178
9313
  const expanded = expandHome(knowledgeRepo, userHome);
@@ -9180,32 +9315,39 @@ function resolvePaths(caveatHome, knowledgeRepo, userHome) {
9180
9315
  return {
9181
9316
  caveatHome,
9182
9317
  knowledgeRepo: resolved,
9183
- dbPath: join3(caveatHome, "index", "caveat.db"),
9184
- entriesDir: join3(resolved, "entries"),
9318
+ dbPath: join4(caveatHome, "index", "caveat.db"),
9319
+ entriesDir: join4(resolved, "entries"),
9185
9320
  // community/ lives at caveatHome level, NOT inside knowledgeRepo. Community
9186
9321
  // clones are external knowledge caches — not semantically "owned" by the
9187
9322
  // user — and they embed their own .git dirs which would otherwise nest
9188
9323
  // inside the user's git-tracked knowledge repo.
9189
- communityDir: join3(caveatHome, "community")
9324
+ communityDir: join4(caveatHome, "community"),
9325
+ publishMirrorDir: join4(caveatHome, "publish", "mirror")
9190
9326
  };
9191
9327
  }
9192
9328
 
9193
9329
  // ../../packages/core/dist/config.js
9194
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync } from "node:fs";
9330
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
9195
9331
  var DEFAULT_CONFIG = {
9196
9332
  knowledgeRepo: "own",
9197
9333
  semverKeys: ["driver", "cuda", "node"],
9198
- communitySources: []
9334
+ communitySources: [],
9335
+ publishTarget: null
9199
9336
  };
9200
9337
  function loadConfig(userConfigPath) {
9201
- const userCfg = existsSync4(userConfigPath) ? JSON.parse(readFileSync3(userConfigPath, "utf-8")) : {};
9338
+ const userCfg = existsSync5(userConfigPath) ? JSON.parse(readFileSync4(userConfigPath, "utf-8")) : {};
9202
9339
  return deepMerge(DEFAULT_CONFIG, userCfg);
9203
9340
  }
9204
9341
  function ensureUserConfig(userConfigPath) {
9205
- if (!existsSync4(userConfigPath)) {
9206
- writeFileSync(userConfigPath, "{}\n", "utf-8");
9342
+ if (!existsSync5(userConfigPath)) {
9343
+ writeFileSync2(userConfigPath, "{}\n", "utf-8");
9207
9344
  }
9208
9345
  }
9346
+ function writeUserConfigPatch(userConfigPath, patch) {
9347
+ const existing = existsSync5(userConfigPath) ? JSON.parse(readFileSync4(userConfigPath, "utf-8")) : {};
9348
+ writeFileSync2(userConfigPath, `${JSON.stringify({ ...existing, ...patch }, null, 2)}
9349
+ `, "utf-8");
9350
+ }
9209
9351
  function deepMerge(base, overlay) {
9210
9352
  if (overlay === null || overlay === void 0) return base;
9211
9353
  if (Array.isArray(overlay)) return overlay;
@@ -9219,8 +9361,8 @@ function deepMerge(base, overlay) {
9219
9361
  }
9220
9362
 
9221
9363
  // ../../packages/core/dist/community.js
9222
- import { existsSync as existsSync5, readdirSync as readdirSync3, rmSync, statSync as statSync2 } from "node:fs";
9223
- import { join as join4 } from "node:path";
9364
+ import { existsSync as existsSync6, readdirSync as readdirSync4, rmSync, statSync as statSync3 } from "node:fs";
9365
+ import { join as join5 } from "node:path";
9224
9366
 
9225
9367
  // ../../node_modules/.pnpm/simple-git@3.36.0/node_modules/simple-git/dist/esm/index.js
9226
9368
  var import_file_exists = __toESM(require_dist(), 1);
@@ -14185,6 +14327,7 @@ var simpleGit = gitInstanceFactory;
14185
14327
 
14186
14328
  // ../../packages/core/dist/community.js
14187
14329
  var GITHUB_URL_RE = /^https:\/\/github\.com\/[^/]+\/([^/]+?)(\.git)?\/?$/;
14330
+ var GITHUB_HANDLE_RE = /^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?$/;
14188
14331
  function validateCommunityUrl(url) {
14189
14332
  const trimmed2 = (url ?? "").trim();
14190
14333
  const m = GITHUB_URL_RE.exec(trimmed2);
@@ -14207,26 +14350,28 @@ function resolveHandleCollision(baseHandle, exists2) {
14207
14350
  return `${baseHandle}-${n}`;
14208
14351
  }
14209
14352
  async function communityAdd(opts) {
14210
- const validation = validateCommunityUrl(opts.url);
14353
+ const supplied = opts.url ?? "";
14354
+ const url = /^https?:\/\//.test(supplied.trim()) ? supplied : GITHUB_HANDLE_RE.test(supplied) ? `https://github.com/${supplied}/Caveat-Public` : supplied;
14355
+ const validation = validateCommunityUrl(url);
14211
14356
  if (!validation.valid) {
14212
14357
  throw new Error(`invalid community URL: ${validation.reason}`);
14213
14358
  }
14214
14359
  const handle = resolveHandleCollision(
14215
14360
  validation.handle,
14216
- (h2) => existsSync5(join4(opts.communityDir, h2))
14361
+ (h2) => existsSync6(join5(opts.communityDir, h2))
14217
14362
  );
14218
- const target = join4(opts.communityDir, handle);
14363
+ const target = join5(opts.communityDir, handle);
14219
14364
  const depth = opts.depth ?? 1;
14220
14365
  const git = simpleGit();
14221
- await git.clone(opts.url, target, ["--depth", String(depth)]);
14366
+ await git.clone(url, target, ["--depth", String(depth)]);
14222
14367
  return { handle, path: target };
14223
14368
  }
14224
14369
  async function communityPull(opts) {
14225
- if (!existsSync5(opts.communityDir)) return [];
14370
+ if (!existsSync6(opts.communityDir)) return [];
14226
14371
  const results = [];
14227
- for (const entry of readdirSync3(opts.communityDir, { withFileTypes: true })) {
14372
+ for (const entry of readdirSync4(opts.communityDir, { withFileTypes: true })) {
14228
14373
  if (!entry.isDirectory()) continue;
14229
- const path = join4(opts.communityDir, entry.name);
14374
+ const path = join5(opts.communityDir, entry.name);
14230
14375
  const git = simpleGit(path);
14231
14376
  try {
14232
14377
  await git.pull();
@@ -14239,11 +14384,11 @@ async function communityPull(opts) {
14239
14384
  return results;
14240
14385
  }
14241
14386
  function communityList(opts) {
14242
- if (!existsSync5(opts.communityDir)) return [];
14387
+ if (!existsSync6(opts.communityDir)) return [];
14243
14388
  const out = [];
14244
- for (const entry of readdirSync3(opts.communityDir, { withFileTypes: true })) {
14389
+ for (const entry of readdirSync4(opts.communityDir, { withFileTypes: true })) {
14245
14390
  if (!entry.isDirectory()) continue;
14246
- const path = join4(opts.communityDir, entry.name);
14391
+ const path = join5(opts.communityDir, entry.name);
14247
14392
  const row = opts.db.prepare("SELECT COUNT(*) AS n FROM entries WHERE source = ?").get(`community/${entry.name}`);
14248
14393
  out.push({ handle: entry.name, path, entryCount: row?.n ?? 0 });
14249
14394
  }
@@ -14257,8 +14402,8 @@ function communityRemove(opts) {
14257
14402
  if (handle === "." || handle === ".." || /[\\/]/.test(handle)) {
14258
14403
  throw new Error(`invalid handle (no path separators or relative segments): ${opts.handle}`);
14259
14404
  }
14260
- const target = join4(opts.communityDir, handle);
14261
- const dirExisted = existsSync5(target) && statSync2(target).isDirectory();
14405
+ const target = join5(opts.communityDir, handle);
14406
+ const dirExisted = existsSync6(target) && statSync3(target).isDirectory();
14262
14407
  const source = `community/${handle}`;
14263
14408
  const row = opts.db.prepare("SELECT COUNT(*) AS n FROM entries WHERE source = ?").get(source);
14264
14409
  const rowCount = row?.n ?? 0;
@@ -14369,7 +14514,7 @@ function toSearchResult2(row) {
14369
14514
  title: row.title,
14370
14515
  symptomExcerpt: symptom.slice(0, SYMPTOM_EXCERPT_LENGTH2),
14371
14516
  confidence: row.confidence,
14372
- visibility: row.visibility ?? "public",
14517
+ visibility: row.visibility ?? "private",
14373
14518
  environment: fm.environment ?? {}
14374
14519
  };
14375
14520
  }
@@ -14523,7 +14668,7 @@ function stopReminderText(signals, related) {
14523
14668
  }
14524
14669
 
14525
14670
  // ../../packages/core/dist/transcriptSignals.js
14526
- import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
14671
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
14527
14672
  var MAX_ERROR_SNIPPETS = 10;
14528
14673
  var MAX_ERROR_SNIPPET_LENGTH = 300;
14529
14674
  var MAX_SEARCH_QUERIES = 10;
@@ -14549,10 +14694,10 @@ function parseTimestamp(raw) {
14549
14694
  return Number.isNaN(ms) ? void 0 : ms;
14550
14695
  }
14551
14696
  function readSessionSignals(transcriptPath) {
14552
- if (!transcriptPath || !existsSync6(transcriptPath)) return null;
14697
+ if (!transcriptPath || !existsSync7(transcriptPath)) return null;
14553
14698
  let raw;
14554
14699
  try {
14555
- raw = readFileSync4(transcriptPath, "utf-8");
14700
+ raw = readFileSync5(transcriptPath, "utf-8");
14556
14701
  } catch {
14557
14702
  return null;
14558
14703
  }
@@ -14633,7 +14778,7 @@ function struggleSearchText(s) {
14633
14778
  }
14634
14779
 
14635
14780
  // ../../packages/core/dist/codexTranscriptSignals.js
14636
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
14781
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
14637
14782
  var MAX_ERROR_SNIPPETS2 = 10;
14638
14783
  var MAX_ERROR_SNIPPET_LENGTH2 = 300;
14639
14784
  var MAX_SEARCH_QUERIES2 = 10;
@@ -14698,10 +14843,10 @@ function addQuery(searchQueries, query) {
14698
14843
  searchQueries.push(query.slice(0, MAX_SEARCH_QUERY_LENGTH2));
14699
14844
  }
14700
14845
  function readCodexSessionSignals(transcriptPath) {
14701
- if (!transcriptPath || !existsSync7(transcriptPath)) return null;
14846
+ if (!transcriptPath || !existsSync8(transcriptPath)) return null;
14702
14847
  let raw;
14703
14848
  try {
14704
- raw = readFileSync5(transcriptPath, "utf-8");
14849
+ raw = readFileSync6(transcriptPath, "utf-8");
14705
14850
  } catch {
14706
14851
  return null;
14707
14852
  }
@@ -14805,30 +14950,30 @@ function readCodexSessionSignals(transcriptPath) {
14805
14950
 
14806
14951
  // ../../packages/core/dist/pendingReminders.js
14807
14952
  import {
14808
- existsSync as existsSync8,
14809
- mkdirSync as mkdirSync2,
14810
- readdirSync as readdirSync4,
14811
- readFileSync as readFileSync6,
14953
+ existsSync as existsSync9,
14954
+ mkdirSync as mkdirSync3,
14955
+ readdirSync as readdirSync5,
14956
+ readFileSync as readFileSync7,
14812
14957
  rmSync as rmSync2,
14813
- statSync as statSync3,
14814
- unlinkSync,
14815
- writeFileSync as writeFileSync2
14958
+ statSync as statSync4,
14959
+ unlinkSync as unlinkSync2,
14960
+ writeFileSync as writeFileSync3
14816
14961
  } from "node:fs";
14817
- import { join as join5 } from "node:path";
14962
+ import { join as join6 } from "node:path";
14818
14963
  import { randomBytes } from "node:crypto";
14819
14964
  function sanitizeSessionId(raw) {
14820
14965
  const clean = raw.replace(/[^A-Za-z0-9_-]/g, "");
14821
14966
  return clean.length > 0 ? clean : "_unknown";
14822
14967
  }
14823
14968
  function pendingDirFor(caveatHome, sessionId) {
14824
- return join5(caveatHome, "pending", sanitizeSessionId(sessionId));
14969
+ return join6(caveatHome, "pending", sanitizeSessionId(sessionId));
14825
14970
  }
14826
14971
  function appendPendingReminder(caveatHome, sessionId, text) {
14827
14972
  const dir = pendingDirFor(caveatHome, sessionId);
14828
- mkdirSync2(dir, { recursive: true });
14973
+ mkdirSync3(dir, { recursive: true });
14829
14974
  const name = `${Date.now()}-${randomBytes(4).toString("hex")}.txt`;
14830
- const path = join5(dir, name);
14831
- writeFileSync2(path, text, "utf-8");
14975
+ const path = join6(dir, name);
14976
+ writeFileSync3(path, text, "utf-8");
14832
14977
  return path;
14833
14978
  }
14834
14979
  function cleanupStalePendingDirs(caveatHome, options2 = {}) {
@@ -14838,21 +14983,21 @@ function cleanupStalePendingDirs(caveatHome, options2 = {}) {
14838
14983
  }
14839
14984
  const now = options2.now ?? /* @__PURE__ */ new Date();
14840
14985
  const cutoffMs = now.getTime() - staleDays * 24 * 60 * 60 * 1e3;
14841
- const root = join5(caveatHome, "pending");
14842
- if (!existsSync8(root)) return { removed: [], kept: 0 };
14986
+ const root = join6(caveatHome, "pending");
14987
+ if (!existsSync9(root)) return { removed: [], kept: 0 };
14843
14988
  let entries;
14844
14989
  try {
14845
- entries = readdirSync4(root);
14990
+ entries = readdirSync5(root);
14846
14991
  } catch {
14847
14992
  return { removed: [], kept: 0 };
14848
14993
  }
14849
14994
  const removed = [];
14850
14995
  let kept = 0;
14851
14996
  for (const entry of entries) {
14852
- const sub = join5(root, entry);
14997
+ const sub = join6(root, entry);
14853
14998
  let subStat;
14854
14999
  try {
14855
- subStat = statSync3(sub);
15000
+ subStat = statSync4(sub);
14856
15001
  } catch {
14857
15002
  continue;
14858
15003
  }
@@ -14860,10 +15005,10 @@ function cleanupStalePendingDirs(caveatHome, options2 = {}) {
14860
15005
  let newest = subStat.mtimeMs;
14861
15006
  let scanFailed = false;
14862
15007
  try {
14863
- for (const f of readdirSync4(sub)) {
14864
- const fp = join5(sub, f);
15008
+ for (const f of readdirSync5(sub)) {
15009
+ const fp = join6(sub, f);
14865
15010
  try {
14866
- const fs = statSync3(fp);
15011
+ const fs = statSync4(fp);
14867
15012
  if (fs.mtimeMs > newest) newest = fs.mtimeMs;
14868
15013
  } catch {
14869
15014
  scanFailed = true;
@@ -14898,11 +15043,11 @@ function maybeSweepPendingDirs(caveatHome, options2 = {}) {
14898
15043
  );
14899
15044
  }
14900
15045
  const now = options2.now ?? /* @__PURE__ */ new Date();
14901
- const pendingRoot = join5(caveatHome, "pending");
14902
- if (!existsSync8(pendingRoot)) return { skipped: "no_pending_dir" };
14903
- const marker = join5(pendingRoot, ".last-sweep");
15046
+ const pendingRoot = join6(caveatHome, "pending");
15047
+ if (!existsSync9(pendingRoot)) return { skipped: "no_pending_dir" };
15048
+ const marker = join6(pendingRoot, ".last-sweep");
14904
15049
  try {
14905
- const m = statSync3(marker);
15050
+ const m = statSync4(marker);
14906
15051
  const ageMs = now.getTime() - m.mtimeMs;
14907
15052
  if (ageMs < debounceDays * 24 * 60 * 60 * 1e3) {
14908
15053
  return { skipped: "debounced" };
@@ -14911,30 +15056,30 @@ function maybeSweepPendingDirs(caveatHome, options2 = {}) {
14911
15056
  }
14912
15057
  const result = cleanupStalePendingDirs(caveatHome, { staleDays, now });
14913
15058
  try {
14914
- writeFileSync2(marker, "", "utf-8");
15059
+ writeFileSync3(marker, "", "utf-8");
14915
15060
  } catch {
14916
15061
  }
14917
15062
  return { swept: result };
14918
15063
  }
14919
15064
  function drainPendingReminders(caveatHome, sessionId) {
14920
15065
  const dir = pendingDirFor(caveatHome, sessionId);
14921
- if (!existsSync8(dir)) return [];
15066
+ if (!existsSync9(dir)) return [];
14922
15067
  let entries;
14923
15068
  try {
14924
- entries = readdirSync4(dir).filter((f) => f.endsWith(".txt")).sort();
15069
+ entries = readdirSync5(dir).filter((f) => f.endsWith(".txt")).sort();
14925
15070
  } catch {
14926
15071
  return [];
14927
15072
  }
14928
15073
  const out = [];
14929
15074
  for (const entry of entries) {
14930
- const path = join5(dir, entry);
15075
+ const path = join6(dir, entry);
14931
15076
  try {
14932
- out.push(readFileSync6(path, "utf-8"));
15077
+ out.push(readFileSync7(path, "utf-8"));
14933
15078
  } catch {
14934
15079
  continue;
14935
15080
  }
14936
15081
  try {
14937
- unlinkSync(path);
15082
+ unlinkSync2(path);
14938
15083
  } catch {
14939
15084
  }
14940
15085
  }
@@ -14978,11 +15123,414 @@ function listStale(db, opts = {}) {
14978
15123
  id: r2.id,
14979
15124
  source: r2.source,
14980
15125
  title: r2.title,
14981
- visibility: r2.visibility ?? "public",
15126
+ visibility: r2.visibility ?? "private",
14982
15127
  last_hit_at: r2.last_hit_at
14983
15128
  }));
14984
15129
  }
14985
15130
 
15131
+ // ../../packages/core/dist/sync.js
15132
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, readdirSync as readdirSync6, realpathSync, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
15133
+ import { dirname as dirname3, join as join7, resolve as resolve2 } from "node:path";
15134
+
15135
+ // ../../packages/core/dist/remoteVisibility.js
15136
+ var PROBE_TIMEOUT_MS = 1e4;
15137
+ function deriveAnonymousProbeUrl(remoteUrl) {
15138
+ let url;
15139
+ try {
15140
+ url = new URL(remoteUrl);
15141
+ } catch {
15142
+ const scp = /^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/.exec(remoteUrl);
15143
+ if (!scp) return void 0;
15144
+ try {
15145
+ return new URL(`https://${scp[1]}/${scp[2]}`).toString();
15146
+ } catch {
15147
+ return void 0;
15148
+ }
15149
+ }
15150
+ if (url.protocol !== "https:" && url.protocol !== "ssh:") return void 0;
15151
+ return `https://${url.host}${url.pathname}${url.search}${url.hash}`;
15152
+ }
15153
+ async function probeAnonymousRead(probeUrl, options2 = {}) {
15154
+ if (!probeUrl) return { kind: "indeterminate", reason: "remote URL cannot be probed anonymously" };
15155
+ const endpoint = new URL("info/refs?service=git-upload-pack", ensureTrailingSlash(probeUrl));
15156
+ try {
15157
+ const response = await (options2.fetchImpl ?? globalThis.fetch)(endpoint, {
15158
+ method: "GET",
15159
+ redirect: "follow",
15160
+ signal: AbortSignal.timeout(options2.timeoutMs ?? PROBE_TIMEOUT_MS)
15161
+ });
15162
+ if (response.status === 401 || response.status === 404) {
15163
+ return { kind: "denied", status: response.status };
15164
+ }
15165
+ if (!response.ok) return { kind: "indeterminate", reason: `unexpected HTTP status ${response.status}` };
15166
+ const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
15167
+ if (contentType !== "application/x-git-upload-pack-advertisement") {
15168
+ return { kind: "indeterminate", reason: "missing Git smart-HTTP advertisement content type" };
15169
+ }
15170
+ return { kind: "anonymous-readable" };
15171
+ } catch {
15172
+ return { kind: "indeterminate", reason: "anonymous read probe request failed" };
15173
+ }
15174
+ }
15175
+ function ensureTrailingSlash(url) {
15176
+ return url.endsWith("/") ? url : `${url}/`;
15177
+ }
15178
+
15179
+ // ../../packages/core/dist/sync.js
15180
+ var SyncError = class extends Error {
15181
+ constructor(code, message) {
15182
+ super(message);
15183
+ this.code = code;
15184
+ this.name = "SyncError";
15185
+ }
15186
+ code;
15187
+ };
15188
+ function normalizePath(path) {
15189
+ const normalized = resolve2(path).replace(/\\/g, "/");
15190
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
15191
+ }
15192
+ async function effectivePushUrls(git) {
15193
+ let raw;
15194
+ try {
15195
+ raw = await git.raw(["remote", "get-url", "--push", "--all", "origin"]);
15196
+ } catch {
15197
+ const remotes = (await git.raw(["remote"])).trim();
15198
+ const available = remotes ? remotes.split(/\r?\n/).join(", ") : "(none)";
15199
+ throw new SyncError("NO_REMOTE", `NO_REMOTE: origin is required (configured remotes: ${available})`);
15200
+ }
15201
+ const urls = raw.split(/\r?\n/).map((u) => u.trim()).filter(Boolean);
15202
+ if (urls.length === 0) throw new SyncError("NO_REMOTE", "NO_REMOTE: origin has no push URL");
15203
+ return urls;
15204
+ }
15205
+ async function assertPrivateRemotes(remoteUrls, opts) {
15206
+ const probe = opts.probeImpl ?? probeAnonymousRead;
15207
+ let worst = { kind: "denied", status: 0 };
15208
+ for (const remoteUrl of remoteUrls) {
15209
+ const result = await probe(deriveAnonymousProbeUrl(remoteUrl));
15210
+ if (result.kind === "anonymous-readable") {
15211
+ throw new SyncError("REMOTE_PUBLIC", `remote is anonymously readable: ${remoteUrl}`);
15212
+ }
15213
+ if (result.kind === "indeterminate") {
15214
+ if (!opts.trustRemotePrivate) {
15215
+ throw new SyncError(
15216
+ "REMOTE_VISIBILITY_INDETERMINATE",
15217
+ `could not verify remote privacy: ${remoteUrl}; rerun with --trust-remote-private to accept this risk`
15218
+ );
15219
+ }
15220
+ worst = result;
15221
+ }
15222
+ }
15223
+ return worst;
15224
+ }
15225
+ async function preflightSync(ownDir, opts = {}) {
15226
+ const git = simpleGit(ownDir);
15227
+ if (!await git.checkIsRepo()) {
15228
+ throw new SyncError("NOT_A_REPO", "own knowledge directory is not a git repository; run `caveat sync --init` first");
15229
+ }
15230
+ const root = realpathSync.native((await git.revparse(["--show-toplevel"])).trim());
15231
+ const requested = realpathSync.native(ownDir);
15232
+ if (normalizePath(root) !== normalizePath(requested)) {
15233
+ throw new SyncError("EXTERNAL_TOPLEVEL", `EXTERNAL_TOPLEVEL: own directory must be the repository root: ${requested} (root: ${root})`);
15234
+ }
15235
+ let branch;
15236
+ try {
15237
+ branch = (await git.raw(["symbolic-ref", "--short", "HEAD"])).trim();
15238
+ } catch {
15239
+ throw new SyncError("DETACHED_HEAD", "cannot sync from a detached HEAD");
15240
+ }
15241
+ const pushUrls = await effectivePushUrls(git);
15242
+ const probe = await assertPrivateRemotes(pushUrls, opts);
15243
+ return { ownDir: requested, branch, pushUrls, probe };
15244
+ }
15245
+ function reindexAndMark(opts) {
15246
+ mkdirSync4(dirname3(opts.paths.dbPath), { recursive: true });
15247
+ const db = openDb({ path: opts.paths.dbPath, logger: opts.logger });
15248
+ try {
15249
+ reindexAllSources({ db, paths: opts.paths, logger: opts.logger });
15250
+ writeDigestMarker(opts.caveatHome, computeEntriesDigest(opts.paths));
15251
+ } finally {
15252
+ db.close();
15253
+ }
15254
+ }
15255
+ async function syncOwn(opts) {
15256
+ const preflight = await preflightSync(opts.ownDir, opts);
15257
+ const git = simpleGit(preflight.ownDir);
15258
+ const status = await git.status();
15259
+ if (opts.dryRun) {
15260
+ return {
15261
+ ...preflight,
15262
+ committed: false,
15263
+ pulled: false,
15264
+ pushed: false,
15265
+ dryRun: true,
15266
+ changedFiles: status.files.length
15267
+ };
15268
+ }
15269
+ let committed = false;
15270
+ if (!status.isClean()) {
15271
+ await git.add("-A");
15272
+ const changed = status.files.length;
15273
+ await git.commit(`caveat sync: ${changed} changed file${changed === 1 ? "" : "s"}`);
15274
+ committed = true;
15275
+ }
15276
+ const remoteBranch = (await git.raw(["ls-remote", "--heads", "origin", preflight.branch])).trim();
15277
+ let pulled = false;
15278
+ if (remoteBranch) {
15279
+ try {
15280
+ await git.pull("origin", preflight.branch, ["--rebase"]);
15281
+ pulled = true;
15282
+ } catch (err) {
15283
+ try {
15284
+ await git.raw(["rebase", "--abort"]);
15285
+ } catch {
15286
+ }
15287
+ const detail = err instanceof Error ? err.message : String(err);
15288
+ throw new SyncError("SYNC_CONFLICT", `sync rebase failed and was aborted: ${detail}`);
15289
+ }
15290
+ }
15291
+ reindexAndMark(opts);
15292
+ await git.push("origin", preflight.branch, ["-u"]);
15293
+ return {
15294
+ ...preflight,
15295
+ committed,
15296
+ pulled,
15297
+ pushed: true,
15298
+ dryRun: false,
15299
+ changedFiles: status.files.length
15300
+ };
15301
+ }
15302
+ var KNOWLEDGE_GITIGNORE = [
15303
+ "# Private entries DO sync to your private remote (Caveat-Private) \u2014 that is the",
15304
+ "# intended sharing boundary. The public boundary is enforced by `caveat publish`.",
15305
+ "",
15306
+ "# Obsidian per-user config: workspace layout, theme, plugin state, cache.",
15307
+ ".obsidian/",
15308
+ ""
15309
+ ].join("\n");
15310
+ function countMarkdownEntries(ownDir) {
15311
+ const entries = join7(ownDir, "entries");
15312
+ if (!existsSync10(entries)) return 0;
15313
+ let count = 0;
15314
+ const stack = [entries];
15315
+ while (stack.length > 0) {
15316
+ const dir = stack.pop();
15317
+ for (const entry of readdirSync6(dir, { withFileTypes: true })) {
15318
+ const path = join7(dir, entry.name);
15319
+ if (entry.isDirectory()) stack.push(path);
15320
+ else if (entry.isFile() && entry.name.endsWith(".md")) count++;
15321
+ }
15322
+ }
15323
+ return count;
15324
+ }
15325
+ function scaffold(ownDir) {
15326
+ mkdirSync4(join7(ownDir, "entries"), { recursive: true });
15327
+ const gitignore = join7(ownDir, ".gitignore");
15328
+ if (!existsSync10(gitignore)) writeFileSync4(gitignore, KNOWLEDGE_GITIGNORE, "utf-8");
15329
+ }
15330
+ async function defaultRemoteBranch(git, url) {
15331
+ const symref = await git.raw(["ls-remote", "--symref", url, "HEAD"]);
15332
+ const match = /^ref: refs\/heads\/([^\s]+)\s+HEAD$/m.exec(symref);
15333
+ if (match) return match[1];
15334
+ const heads = await git.raw(["ls-remote", "--heads", url]);
15335
+ const first2 = /refs\/heads\/([^\s]+)\s*$/m.exec(heads);
15336
+ if (!first2) throw new Error(`remote has refs but no branch heads: ${url}`);
15337
+ return first2[1];
15338
+ }
15339
+ async function initOwnSync(opts) {
15340
+ const ownDir = resolve2(opts.ownDir);
15341
+ mkdirSync4(ownDir, { recursive: true });
15342
+ const existing = simpleGit(ownDir);
15343
+ if (await existing.checkIsRepo()) {
15344
+ throw new SyncError("OWN_REPO_EXISTS", `own knowledge directory is already a git repository: ${ownDir}`);
15345
+ }
15346
+ const inspector = simpleGit(ownDir);
15347
+ const refs = (await inspector.raw(["ls-remote", "--heads", opts.url])).trim();
15348
+ const entryCount = countMarkdownEntries(ownDir);
15349
+ if (refs && entryCount > 0) {
15350
+ throw new SyncError("BOTH_HAVE_ENTRIES", "local and remote both contain entries; resolve the ownership conflict before initializing sync");
15351
+ }
15352
+ const git = simpleGit(ownDir);
15353
+ const createdGitDir = join7(ownDir, ".git");
15354
+ await git.init();
15355
+ try {
15356
+ await git.addRemote("origin", opts.url);
15357
+ await assertPrivateRemotes(await effectivePushUrls(git), opts);
15358
+ if (!refs) {
15359
+ scaffold(ownDir);
15360
+ await git.add("-A");
15361
+ await git.commit(`caveat sync: initial import (${entryCount} entries)`);
15362
+ const branch2 = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
15363
+ await git.push("origin", branch2, ["-u"]);
15364
+ reindexAndMark(opts);
15365
+ return { ownDir, branch: branch2, remoteWasEmpty: true };
15366
+ }
15367
+ const branch = await defaultRemoteBranch(git, opts.url);
15368
+ await git.fetch("origin", branch);
15369
+ await git.checkout(["--track", "-B", branch, `origin/${branch}`]);
15370
+ reindexAndMark(opts);
15371
+ return { ownDir, branch, remoteWasEmpty: false };
15372
+ } catch (err) {
15373
+ try {
15374
+ rmSync3(createdGitDir, { recursive: true, force: true });
15375
+ } catch {
15376
+ }
15377
+ throw err;
15378
+ }
15379
+ }
15380
+
15381
+ // ../../packages/core/dist/publish.js
15382
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync8, readdirSync as readdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
15383
+ import { dirname as dirname4, join as join8, relative as relative3 } from "node:path";
15384
+
15385
+ // ../../packages/core/dist/visibility.js
15386
+ function classifyVisibility(value) {
15387
+ if (value === "public") return "public";
15388
+ if (value === "private") return "private";
15389
+ return "invalid";
15390
+ }
15391
+
15392
+ // ../../packages/core/dist/publish.js
15393
+ function collectPublishSet(entriesDir) {
15394
+ const files = [];
15395
+ const invalid = [];
15396
+ if (!existsSync11(entriesDir)) return { files, invalid };
15397
+ for (const path of walkMarkdown(entriesDir)) {
15398
+ const relPath = relative3(entriesDir, path).replace(/\\/g, "/");
15399
+ const content = readFileSync8(path);
15400
+ try {
15401
+ const visibility = classifyVisibility(parseMarkdown(content.toString("utf-8")).frontmatter.visibility);
15402
+ if (visibility === "public") files.push({ relPath, content });
15403
+ else if (visibility === "invalid") invalid.push({ relPath, reason: "visibility must be exactly public or private" });
15404
+ } catch (err) {
15405
+ invalid.push({ relPath, reason: err instanceof Error ? err.message : String(err) });
15406
+ }
15407
+ }
15408
+ return { files, invalid };
15409
+ }
15410
+ async function preparePublishMirror(opts) {
15411
+ const git = opts.git ?? simpleGit();
15412
+ if (!existsSync11(opts.mirrorDir)) {
15413
+ mkdirSync5(dirname4(opts.mirrorDir), { recursive: true });
15414
+ await git.clone(opts.target, opts.mirrorDir);
15415
+ } else {
15416
+ const mirrorGit2 = simpleGit(opts.mirrorDir);
15417
+ const old = (await mirrorGit2.raw(["remote", "get-url", "origin"])).trim();
15418
+ if (old !== opts.target) {
15419
+ throw new Error(`publish mirror points at ${old} but publishTarget is ${opts.target} \u2014 remove ${opts.mirrorDir} and re-run`);
15420
+ }
15421
+ }
15422
+ const mirrorGit = simpleGit(opts.mirrorDir);
15423
+ await mirrorGit.fetch("origin");
15424
+ const head = (await mirrorGit.raw(["symbolic-ref", "--quiet", "refs/remotes/origin/HEAD"]).catch(() => "")).trim();
15425
+ if (head) await mirrorGit.reset(["--hard", head]);
15426
+ await mirrorGit.raw(["clean", "-ffdx"]);
15427
+ }
15428
+ function publishReadme(fileCount, files) {
15429
+ const categories = [...new Set(files.map((f) => f.relPath.split("/")[0]).filter(Boolean))].sort();
15430
+ return [
15431
+ "# Caveat-Public",
15432
+ "",
15433
+ "This repository is generated by `caveat publish`. Do not edit it by hand.",
15434
+ "",
15435
+ "Subscribe with `caveat community add <https URL or username>`.",
15436
+ "",
15437
+ `Public entries: ${fileCount}`,
15438
+ `Categories: ${categories.length ? categories.join(", ") : "(none)"}`,
15439
+ "",
15440
+ "Caveat: https://github.com/kitepon-rgb/Caveat",
15441
+ ""
15442
+ ].join("\n");
15443
+ }
15444
+ function writeMirror(opts) {
15445
+ for (const item of readdirSync7(opts.mirrorDir, { withFileTypes: true })) {
15446
+ if (item.name === ".git") continue;
15447
+ rmSync4(join8(opts.mirrorDir, item.name), { recursive: true, force: true });
15448
+ }
15449
+ const entries = join8(opts.mirrorDir, "entries");
15450
+ for (const file of opts.files) {
15451
+ const target = join8(entries, file.relPath);
15452
+ mkdirSync5(dirname4(target), { recursive: true });
15453
+ writeFileSync5(target, file.content);
15454
+ }
15455
+ writeFileSync5(join8(opts.mirrorDir, "README.md"), opts.readme, "utf-8");
15456
+ }
15457
+ function* walkAllFiles(root) {
15458
+ for (const item of readdirSync7(root, { withFileTypes: true })) {
15459
+ if (item.name === ".git") continue;
15460
+ const full = join8(root, item.name);
15461
+ if (item.isDirectory()) yield* walkAllFiles(full);
15462
+ else yield full;
15463
+ }
15464
+ }
15465
+ function verifyMirror(mirrorDir) {
15466
+ const entriesRoot = join8(mirrorDir, "entries");
15467
+ const bad = [];
15468
+ let fileCount = 0;
15469
+ for (const path of walkAllFiles(mirrorDir)) {
15470
+ const relToMirror = relative3(mirrorDir, path).replace(/\\/g, "/");
15471
+ if (relToMirror === "README.md") continue;
15472
+ if (!relToMirror.startsWith("entries/")) {
15473
+ bad.push(`${relToMirror}: unexpected file outside entries/`);
15474
+ continue;
15475
+ }
15476
+ if (!relToMirror.endsWith(".md")) {
15477
+ bad.push(`${relToMirror}: unexpected non-markdown file`);
15478
+ continue;
15479
+ }
15480
+ fileCount++;
15481
+ const relPath = relative3(entriesRoot, path).replace(/\\/g, "/");
15482
+ try {
15483
+ const visibility = classifyVisibility(parseMarkdown(readFileSync8(path, "utf-8")).frontmatter.visibility);
15484
+ if (visibility !== "public") bad.push(`${relPath}: visibility is ${visibility}`);
15485
+ } catch (err) {
15486
+ bad.push(`${relPath}: ${err instanceof Error ? err.message : String(err)}`);
15487
+ }
15488
+ }
15489
+ if (bad.length) throw new Error(`publish mirror verification failed:
15490
+ ${bad.join("\n")}`);
15491
+ return { fileCount };
15492
+ }
15493
+ function stagedChanges(raw) {
15494
+ const lines = raw.trim() ? raw.trim().split(/\r?\n/) : [];
15495
+ let added = 0;
15496
+ let modified = 0;
15497
+ let deleted = 0;
15498
+ for (const line of lines) {
15499
+ if (line.startsWith("A")) added++;
15500
+ else if (line.startsWith("D")) deleted++;
15501
+ else modified++;
15502
+ }
15503
+ return { lines, added, modified, deleted };
15504
+ }
15505
+ async function publishOwn(opts) {
15506
+ if (!opts.config.publishTarget) throw new Error("publishTarget is not configured");
15507
+ const collected = collectPublishSet(opts.paths.entriesDir);
15508
+ if (collected.invalid.length) throw new Error(`cannot publish invalid entries:
15509
+ ${collected.invalid.map((x2) => `${x2.relPath}: ${x2.reason}`).join("\n")}`);
15510
+ await preparePublishMirror({ mirrorDir: opts.paths.publishMirrorDir, target: opts.config.publishTarget });
15511
+ writeMirror({ mirrorDir: opts.paths.publishMirrorDir, files: collected.files, readme: publishReadme(collected.files.length, collected.files) });
15512
+ const verified = verifyMirror(opts.paths.publishMirrorDir);
15513
+ if (verified.fileCount !== collected.files.length) throw new Error(`publish mirror file count mismatch: expected ${collected.files.length}, got ${verified.fileCount}`);
15514
+ const git = simpleGit(opts.paths.publishMirrorDir);
15515
+ await git.add("-A");
15516
+ const changes = stagedChanges(await git.raw(["diff", "--cached", "--name-status"]));
15517
+ if (!changes.lines.length) {
15518
+ opts.logger.info("no changes to publish");
15519
+ return { fileCount: verified.fileCount, changed: false, dryRun: Boolean(opts.dryRun) };
15520
+ }
15521
+ for (const line of changes.lines) opts.logger.info(line);
15522
+ if (opts.dryRun) return { fileCount: verified.fileCount, changed: true, dryRun: true };
15523
+ const question = `publish ${changes.lines.length} file change(s)? [y/N]`;
15524
+ if (!opts.yes) {
15525
+ if (!(opts.isTty ?? (() => Boolean(process.stdin.isTTY)))()) throw new Error(`${question}; rerun with --yes to approve non-interactively`);
15526
+ if (!opts.confirmImpl(question)) throw new Error("publish cancelled");
15527
+ }
15528
+ await git.commit(`caveat publish: ${verified.fileCount} public entries (+${changes.added} ~${changes.modified} -${changes.deleted})`);
15529
+ const branch = (await git.raw(["symbolic-ref", "--short", "HEAD"])).trim();
15530
+ await git.push("origin", branch, ["-u"]);
15531
+ return { fileCount: verified.fileCount, changed: true, dryRun: false };
15532
+ }
15533
+
14986
15534
  // ../../packages/core/dist/codexSidecar.js
14987
15535
  function caveatEntryToSidecarContextBlock(entry) {
14988
15536
  const fm = entry.frontmatter;
@@ -15176,8 +15724,8 @@ function generateSourceSession(now = () => /* @__PURE__ */ new Date()) {
15176
15724
  }
15177
15725
 
15178
15726
  // ../../packages/core/dist/writer.js
15179
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
15180
- import { dirname as dirname3 } from "node:path";
15727
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "node:fs";
15728
+ import { dirname as dirname5 } from "node:path";
15181
15729
  function buildEntry(frontmatter, sections) {
15182
15730
  const bodyParts = [];
15183
15731
  for (const [heading, content2] of Object.entries(sections)) {
@@ -15195,14 +15743,14 @@ ${body}${body ? "\n" : ""}`;
15195
15743
  return { content, body };
15196
15744
  }
15197
15745
  function writeEntryFile(filePath, content) {
15198
- const dir = dirname3(filePath);
15199
- if (!existsSync9(dir)) mkdirSync3(dir, { recursive: true });
15200
- writeFileSync3(filePath, content, "utf-8");
15746
+ const dir = dirname5(filePath);
15747
+ if (!existsSync12(dir)) mkdirSync6(dir, { recursive: true });
15748
+ writeFileSync6(filePath, content, "utf-8");
15201
15749
  }
15202
15750
 
15203
15751
  // ../../packages/core/dist/record.js
15204
- import { statSync as statSync4 } from "node:fs";
15205
- import { join as join6 } from "node:path";
15752
+ import { statSync as statSync5 } from "node:fs";
15753
+ import { join as join9 } from "node:path";
15206
15754
  var DEFAULT_CATEGORY = "misc";
15207
15755
  function recordEntry(input, opts) {
15208
15756
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
@@ -15218,7 +15766,7 @@ function recordEntry(input, opts) {
15218
15766
  const frontmatter = {
15219
15767
  id,
15220
15768
  title: input.title,
15221
- visibility: input.visibility ?? "public",
15769
+ visibility: input.visibility ?? "private",
15222
15770
  confidence: input.confidence ?? "tentative",
15223
15771
  outcome: input.outcome ?? "resolved",
15224
15772
  tags: input.tags ?? [],
@@ -15238,9 +15786,9 @@ function recordEntry(input, opts) {
15238
15786
  const built = buildEntry(frontmatter, sections);
15239
15787
  const category = input.category ?? DEFAULT_CATEGORY;
15240
15788
  const relPath = `${category}/${id}.md`;
15241
- const filePath = join6(opts.entriesRoot, relPath);
15789
+ const filePath = join9(opts.entriesRoot, relPath);
15242
15790
  writeEntryFile(filePath, built.content);
15243
- const stat = statSync4(filePath);
15791
+ const stat = statSync5(filePath);
15244
15792
  upsertEntry(opts.db, {
15245
15793
  id,
15246
15794
  source,
@@ -15268,8 +15816,8 @@ function formatYmd(d) {
15268
15816
  }
15269
15817
 
15270
15818
  // ../../packages/core/dist/update.js
15271
- import { readFileSync as readFileSync7, statSync as statSync5, writeFileSync as writeFileSync4 } from "node:fs";
15272
- import { join as join7 } from "node:path";
15819
+ import { readFileSync as readFileSync9, statSync as statSync6, writeFileSync as writeFileSync7 } from "node:fs";
15820
+ import { join as join10 } from "node:path";
15273
15821
  var IMMUTABLE_KEYS = /* @__PURE__ */ new Set([
15274
15822
  "id",
15275
15823
  "created_at",
@@ -15285,8 +15833,8 @@ function updateEntry(id, patch, opts) {
15285
15833
  throw new Error(`caveat not found: id=${id} source=${source}`);
15286
15834
  }
15287
15835
  const relPath = row.path;
15288
- const filePath = join7(opts.entriesRoot, relPath);
15289
- const raw = readFileSync7(filePath, "utf-8");
15836
+ const filePath = join10(opts.entriesRoot, relPath);
15837
+ const raw = readFileSync9(filePath, "utf-8");
15290
15838
  const parsed = parseMarkdown(raw);
15291
15839
  if (patch.frontmatter) {
15292
15840
  for (const key of Object.keys(patch.frontmatter)) {
@@ -15327,8 +15875,8 @@ function updateEntry(id, patch, opts) {
15327
15875
  }
15328
15876
  }
15329
15877
  const built = buildEntry(mergedFrontmatter, mergedSections);
15330
- writeFileSync4(filePath, built.content, "utf-8");
15331
- const stat = statSync5(filePath);
15878
+ writeFileSync7(filePath, built.content, "utf-8");
15879
+ const stat = statSync6(filePath);
15332
15880
  upsertEntry(opts.db, {
15333
15881
  id,
15334
15882
  source,
@@ -15359,6 +15907,12 @@ export {
15359
15907
  openDb,
15360
15908
  scanSource,
15361
15909
  rebuildAll,
15910
+ computeEntriesDigest,
15911
+ readDigestMarker,
15912
+ writeDigestMarker,
15913
+ acquireReindexLock,
15914
+ releaseReindexLock,
15915
+ reindexAllSources,
15362
15916
  search,
15363
15917
  get,
15364
15918
  listRecent,
@@ -15368,6 +15922,7 @@ export {
15368
15922
  resolvePaths,
15369
15923
  loadConfig,
15370
15924
  ensureUserConfig,
15925
+ writeUserConfigPatch,
15371
15926
  communityAdd,
15372
15927
  communityPull,
15373
15928
  communityList,
@@ -15387,6 +15942,10 @@ export {
15387
15942
  drainPendingReminders,
15388
15943
  markHit,
15389
15944
  listStale,
15945
+ syncOwn,
15946
+ KNOWLEDGE_GITIGNORE,
15947
+ initOwnSync,
15948
+ publishOwn,
15390
15949
  caveatEntriesToSidecarContextBlocks,
15391
15950
  decideCodexSidecarExecution,
15392
15951
  buildCodexSidecarDiagnosticsCommand,
@@ -15414,4 +15973,4 @@ strip-bom-string/index.js:
15414
15973
  js-yaml/dist/js-yaml.mjs:
15415
15974
  (*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT *)
15416
15975
  */
15417
- //# sourceMappingURL=chunk-ECC5LJZF.js.map
15976
+ //# sourceMappingURL=chunk-Z2XLYCR3.js.map