@vortex-os/base 0.12.1 → 0.13.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.
package/dist/index.js CHANGED
@@ -8,7 +8,10 @@ import {
8
8
  recordGuardDenial,
9
9
  runFailureCli,
10
10
  scanFailures
11
- } from "./chunk-UV76ZEDC.js";
11
+ } from "./chunk-T4TJZUPP.js";
12
+ import {
13
+ catchUpSessions
14
+ } from "./chunk-3L5DLEGP.js";
12
15
  import {
13
16
  SESSION_END_COMMAND,
14
17
  SESSION_START_COMMAND,
@@ -27,7 +30,7 @@ import {
27
30
  serializeSettings,
28
31
  sniffEffortFromTranscript,
29
32
  statuslineCommand
30
- } from "./chunk-P7IMUUNY.js";
33
+ } from "./chunk-OFID33QA.js";
31
34
  import {
32
35
  GUARD_WRITE_COMMAND,
33
36
  GUARD_WRITE_MATCHER,
@@ -37,7 +40,7 @@ import {
37
40
  resolveInstanceRoot,
38
41
  runGuardCli,
39
42
  scanToolInput
40
- } from "./chunk-2FVNWW77.js";
43
+ } from "./chunk-JKQAB5OK.js";
41
44
  import {
42
45
  atomicWriteFile,
43
46
  dist_exports,
@@ -50,10 +53,7 @@ import {
50
53
  resolveEnvironment,
51
54
  serializeFrontmatter,
52
55
  validateDataRelativePath
53
- } from "./chunk-T53UWSTR.js";
54
- import {
55
- catchUpSessions
56
- } from "./chunk-3L5DLEGP.js";
56
+ } from "./chunk-IZIAMK3L.js";
57
57
  import {
58
58
  __export
59
59
  } from "./chunk-PZ5AY32C.js";
@@ -4024,6 +4024,7 @@ __export(dist_exports14, {
4024
4024
  agendaCommand: () => agendaCommand,
4025
4025
  aggregateHandoff: () => aggregateHandoff,
4026
4026
  applyGlobalSetup: () => applyGlobalSetup,
4027
+ archiveRelPath: () => archiveRelPath,
4027
4028
  argvToSlash: () => argvToSlash,
4028
4029
  autoReindexMemory: () => autoReindexMemory,
4029
4030
  buildDenyDecision: () => buildDenyDecision,
@@ -4057,6 +4058,7 @@ __export(dist_exports14, {
4057
4058
  formatTokens: () => formatTokens,
4058
4059
  formatWindow: () => formatWindow,
4059
4060
  gapWindowSinceArg: () => gapWindowSinceArg,
4061
+ gitOut: () => gitOut,
4060
4062
  globalMemoryPath: () => globalMemoryPath,
4061
4063
  globalSettingsHasHook: () => globalSettingsHasHook,
4062
4064
  globalSettingsPath: () => globalSettingsPath,
@@ -4092,6 +4094,7 @@ __export(dist_exports14, {
4092
4094
  repairOwnershipManifest: () => repairOwnershipManifest,
4093
4095
  resolveInstanceRoot: () => resolveInstanceRoot,
4094
4096
  resolveRepoRoot: () => resolveRepoRoot,
4097
+ runArchiveSync: () => runArchiveSync,
4095
4098
  runCurateAccept: () => runCurateAccept,
4096
4099
  runCurateCandidates: () => runCurateCandidates,
4097
4100
  runCurateDecline: () => runCurateDecline,
@@ -4109,6 +4112,8 @@ __export(dist_exports14, {
4109
4112
  sessionStartCommand: () => sessionStartCommand,
4110
4113
  sniffEffortFromTranscript: () => sniffEffortFromTranscript,
4111
4114
  statuslineCommand: () => statuslineCommand,
4115
+ sweepOrphanTempFiles: () => sweepOrphanTempFiles,
4116
+ sweepRawArchive: () => sweepRawArchive,
4112
4117
  templateDestRelPath: () => templateDestRelPath,
4113
4118
  upsertGlobalBlock: () => upsertGlobalBlock,
4114
4119
  validateCuratePayload: () => validateCuratePayload,
@@ -6009,9 +6014,13 @@ async function seedInstanceConfig(repoRoot, templatesDir) {
6009
6014
  decision: true,
6010
6015
  ambientRecall: true,
6011
6016
  archive: true,
6017
+ archiveCommit: true,
6018
+ archiveAtEnd: true,
6019
+ archiveRawRetentionDays: 30,
6012
6020
  vectorize: true
6013
6021
  },
6014
6022
  updates: { check: "session" },
6023
+ sync: { autoPush: false },
6015
6024
  environments: []
6016
6025
  }, null, 2) + "\n", "utf8");
6017
6026
  }
@@ -7611,23 +7620,280 @@ function createRitualRegistry(options) {
7611
7620
  }
7612
7621
 
7613
7622
  // ../plugins/session-rituals/dist/cli-dispatch.js
7614
- import { execFileSync as execFileSync2, spawn as spawn2 } from "child_process";
7615
- import { existsSync as existsSync15, readFileSync as readFileSync3, mkdirSync, openSync, writeSync, closeSync, linkSync, rmSync, statSync } from "fs";
7623
+ import { spawn as spawn2 } from "child_process";
7624
+ import { existsSync as existsSync16, readFileSync as readFileSync4, mkdirSync as mkdirSync2, openSync as openSync2, writeSync as writeSync2, closeSync as closeSync2, linkSync, rmSync as rmSync2, statSync as statSync2 } from "fs";
7616
7625
  import { createRequire } from "module";
7617
7626
  import { hostname } from "os";
7618
- import { isAbsolute as isAbsolute4, join as join28 } from "path";
7627
+ import { join as join29 } from "path";
7628
+
7629
+ // ../plugins/session-rituals/dist/archive-sync.js
7630
+ import { closeSync, existsSync as existsSync12, mkdirSync, openSync, readdirSync, readFileSync as readFileSync2, renameSync, rmSync, statSync, writeSync } from "fs";
7631
+ import { execFileSync as execFileSync2 } from "child_process";
7632
+ import { isAbsolute as isAbsolute4, join as join25, relative as relative5, sep as sep4 } from "path";
7633
+ function gitOut(cwd, gitArgs) {
7634
+ return execFileSync2("git", [...gitArgs], {
7635
+ cwd,
7636
+ encoding: "utf8",
7637
+ stdio: ["ignore", "pipe", "ignore"]
7638
+ });
7639
+ }
7640
+ function detectInterruptedGitOp(repoRoot) {
7641
+ const markers = [
7642
+ "MERGE_HEAD",
7643
+ "rebase-merge",
7644
+ "rebase-apply",
7645
+ "CHERRY_PICK_HEAD",
7646
+ "REVERT_HEAD",
7647
+ "BISECT_LOG",
7648
+ "index.lock"
7649
+ ];
7650
+ try {
7651
+ const args = ["rev-parse", ...markers.flatMap((m2) => ["--git-path", m2])];
7652
+ const resolved = gitOut(repoRoot, args).split(/\r?\n/).map((s) => s.trim());
7653
+ for (let i = 0; i < markers.length; i++) {
7654
+ const p = resolved[i];
7655
+ if (p && existsSync12(isAbsolute4(p) ? p : join25(repoRoot, p)))
7656
+ return markers[i];
7657
+ }
7658
+ } catch {
7659
+ }
7660
+ return null;
7661
+ }
7662
+ var NOTHING = {
7663
+ ran: false,
7664
+ lockSkipped: false,
7665
+ catchUp: null,
7666
+ rawPruned: 0,
7667
+ retentionDays: 0,
7668
+ committed: false,
7669
+ pushed: false
7670
+ };
7671
+ var ARCHIVE_SYNC_LOCK_TTL_MS = 10 * 60 * 1e3;
7672
+ function lockPath(dataDir) {
7673
+ return join25(dataDir, "_session-archive", ".sync.lock");
7674
+ }
7675
+ function archiveRelPath(ctx) {
7676
+ return relative5(ctx.repoRoot, join25(ctx.dataDir, "_session-archive")).split(sep4).join("/");
7677
+ }
7678
+ function sweepRawArchive(dataDir, retentionDays, nowMs = Date.now()) {
7679
+ if (!(retentionDays > 0))
7680
+ return 0;
7681
+ const rawDir = join25(dataDir, "_session-archive", "raw");
7682
+ const cutoff = nowMs - retentionDays * 864e5;
7683
+ let pruned = 0;
7684
+ const walk5 = (dir) => {
7685
+ let entries;
7686
+ try {
7687
+ entries = readdirSync(dir, { withFileTypes: true });
7688
+ } catch {
7689
+ return;
7690
+ }
7691
+ for (const e of entries) {
7692
+ const p = join25(dir, e.name);
7693
+ if (e.isDirectory()) {
7694
+ walk5(p);
7695
+ } else if (e.isFile()) {
7696
+ try {
7697
+ if (statSync(p).mtimeMs < cutoff) {
7698
+ rmSync(p, { force: true });
7699
+ pruned++;
7700
+ }
7701
+ } catch {
7702
+ }
7703
+ }
7704
+ }
7705
+ };
7706
+ walk5(rawDir);
7707
+ return pruned;
7708
+ }
7709
+ function sweepOrphanTempFiles(dataDir, nowMs = Date.now()) {
7710
+ let removed = 0;
7711
+ const sweep = (dir, recurse, minAgeMs) => {
7712
+ let entries;
7713
+ try {
7714
+ entries = readdirSync(dir, { withFileTypes: true });
7715
+ } catch {
7716
+ return;
7717
+ }
7718
+ for (const e of entries) {
7719
+ const p = join25(dir, e.name);
7720
+ if (e.isDirectory()) {
7721
+ if (recurse)
7722
+ sweep(p, recurse, minAgeMs);
7723
+ } else if (e.isFile() && /\.tmp-\d+$/.test(e.name)) {
7724
+ try {
7725
+ if (minAgeMs === 0 || nowMs - statSync(p).mtimeMs >= minAgeMs) {
7726
+ rmSync(p, { force: true });
7727
+ removed++;
7728
+ }
7729
+ } catch {
7730
+ }
7731
+ }
7732
+ }
7733
+ };
7734
+ sweep(join25(dataDir, "_session-archive", "raw"), true, 0);
7735
+ sweep(join25(dataDir, "_session-archive", "normalized"), true, 0);
7736
+ sweep(join25(dataDir, "_session-archive", ".tmp"), false, ARCHIVE_SYNC_LOCK_TTL_MS);
7737
+ return removed;
7738
+ }
7739
+ function isDetachedHead(repoRoot) {
7740
+ try {
7741
+ gitOut(repoRoot, ["symbolic-ref", "-q", "HEAD"]);
7742
+ return false;
7743
+ } catch {
7744
+ return true;
7745
+ }
7746
+ }
7747
+ function pushCurrentBranch(repoRoot) {
7748
+ try {
7749
+ if (!gitOut(repoRoot, ["remote"]).trim())
7750
+ return { pushed: false, skip: "no-remote" };
7751
+ } catch {
7752
+ return { pushed: false, skip: "git-error" };
7753
+ }
7754
+ try {
7755
+ gitOut(repoRoot, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]);
7756
+ } catch {
7757
+ return { pushed: false, skip: "no-upstream" };
7758
+ }
7759
+ const counts = () => {
7760
+ const [a, b2] = gitOut(repoRoot, ["rev-list", "--left-right", "--count", "HEAD...@{upstream}"]).trim().split(/\s+/);
7761
+ return { ahead: Number(a ?? 0) || 0, behind: Number(b2 ?? 0) || 0 };
7762
+ };
7763
+ try {
7764
+ let { ahead, behind } = counts();
7765
+ if (behind > 0) {
7766
+ try {
7767
+ gitOut(repoRoot, ["pull", "--ff-only"]);
7768
+ } catch {
7769
+ return { pushed: false, skip: "diverged" };
7770
+ }
7771
+ ({ ahead, behind } = counts());
7772
+ }
7773
+ if (ahead === 0)
7774
+ return { pushed: false, skip: "no-commit" };
7775
+ gitOut(repoRoot, ["push"]);
7776
+ return { pushed: true };
7777
+ } catch {
7778
+ return { pushed: false, skip: "git-error" };
7779
+ }
7780
+ }
7781
+ async function runArchiveSync(ctx, config, _phase, opts) {
7782
+ const lock = lockPath(ctx.dataDir);
7783
+ const token = `${process.pid}-${process.hrtime.bigint()}`;
7784
+ let lockFd;
7785
+ try {
7786
+ mkdirSync(join25(ctx.dataDir, "_session-archive"), { recursive: true });
7787
+ lockFd = openSync(lock, "wx");
7788
+ } catch (e) {
7789
+ if (e.code !== "EEXIST") {
7790
+ return { ...NOTHING, lockSkipped: true };
7791
+ }
7792
+ let stale = true;
7793
+ try {
7794
+ stale = Date.now() - statSync(lock).mtimeMs >= ARCHIVE_SYNC_LOCK_TTL_MS;
7795
+ } catch {
7796
+ stale = true;
7797
+ }
7798
+ if (!stale)
7799
+ return { ...NOTHING, lockSkipped: true };
7800
+ const claimed = `${lock}.stale-${process.pid}`;
7801
+ try {
7802
+ renameSync(lock, claimed);
7803
+ rmSync(claimed, { force: true });
7804
+ } catch {
7805
+ return { ...NOTHING, lockSkipped: true };
7806
+ }
7807
+ try {
7808
+ lockFd = openSync(lock, "wx");
7809
+ } catch {
7810
+ return { ...NOTHING, lockSkipped: true };
7811
+ }
7812
+ }
7813
+ let tokenWritten = false;
7814
+ const releaseLock = () => {
7815
+ try {
7816
+ const cur = existsSync12(lock) ? readFileSync2(lock, "utf8").trim() : "";
7817
+ if (cur === token || cur === "" && !tokenWritten)
7818
+ rmSync(lock, { force: true });
7819
+ } catch {
7820
+ }
7821
+ };
7822
+ try {
7823
+ try {
7824
+ writeSync(lockFd, token + "\n");
7825
+ tokenWritten = true;
7826
+ } finally {
7827
+ closeSync(lockFd);
7828
+ }
7829
+ let catchUp = null;
7830
+ try {
7831
+ const { catchUpSessions: catchUpSessions2 } = await import("./catch-up-GDDKPZHJ.js");
7832
+ catchUp = await catchUpSessions2(ctx, opts?.catchUp);
7833
+ } catch {
7834
+ }
7835
+ const retentionDays = config.autoRecord.archiveRawRetentionDays;
7836
+ let rawPruned = 0;
7837
+ try {
7838
+ rawPruned = sweepRawArchive(ctx.dataDir, retentionDays);
7839
+ } catch {
7840
+ }
7841
+ try {
7842
+ sweepOrphanTempFiles(ctx.dataDir);
7843
+ } catch {
7844
+ }
7845
+ let committed = false;
7846
+ let commitSkipped;
7847
+ if (!config.autoRecord.archiveCommit) {
7848
+ commitSkipped = "off";
7849
+ } else if (detectInterruptedGitOp(ctx.repoRoot)) {
7850
+ commitSkipped = "git-busy";
7851
+ } else if (isDetachedHead(ctx.repoRoot)) {
7852
+ commitSkipped = "detached-head";
7853
+ } else {
7854
+ const rel = archiveRelPath(ctx);
7855
+ const paths = [`${rel}/raw`, `${rel}/normalized`].filter((p) => existsSync12(join25(ctx.repoRoot, ...p.split("/"))));
7856
+ const ingested = catchUp?.ingestedLocal ?? 0;
7857
+ const result = commitFrameworkPaths(ctx.repoRoot, paths, `chore(vortex): session archive sync (${ingested} ingested, ${rawPruned} raw pruned)`);
7858
+ committed = result.committed;
7859
+ if (!result.committed) {
7860
+ commitSkipped = result.reason === "nothing-to-commit" || result.reason === "no-paths" ? "nothing-to-commit" : result.reason === "not-a-git-repo" ? "not-a-git-repo" : "git-error";
7861
+ }
7862
+ }
7863
+ let pushed = false;
7864
+ let pushSkipped;
7865
+ if (!config.sync.autoPush) {
7866
+ pushSkipped = "off";
7867
+ } else {
7868
+ ({ pushed, skip: pushSkipped } = pushCurrentBranch(ctx.repoRoot));
7869
+ }
7870
+ return {
7871
+ ran: true,
7872
+ lockSkipped: false,
7873
+ catchUp,
7874
+ rawPruned,
7875
+ retentionDays,
7876
+ committed,
7877
+ ...commitSkipped !== void 0 ? { commitSkipped } : {},
7878
+ pushed,
7879
+ ...pushSkipped !== void 0 ? { pushSkipped } : {}
7880
+ };
7881
+ } finally {
7882
+ releaseLock();
7883
+ }
7884
+ }
7619
7885
 
7620
7886
  // ../plugins/session-rituals/dist/update-check.js
7621
7887
  import { execSync } from "child_process";
7622
- import { existsSync as existsSync12, readFileSync as readFileSync2 } from "fs";
7623
- import { join as join25 } from "path";
7888
+ import { existsSync as existsSync13, readFileSync as readFileSync3 } from "fs";
7889
+ import { join as join26 } from "path";
7624
7890
  var PKG = "@vortex-os/base";
7625
7891
  var NPM_TIMEOUT_MS = 4e3;
7626
7892
  function readInstalledBaseVersion(templatesDir = resolveTemplatesDir()) {
7627
7893
  if (!templatesDir)
7628
7894
  return null;
7629
7895
  try {
7630
- const m2 = JSON.parse(readFileSync2(join25(templatesDir, "manifest.json"), "utf8"));
7896
+ const m2 = JSON.parse(readFileSync3(join26(templatesDir, "manifest.json"), "utf8"));
7631
7897
  return typeof m2.baseVersion === "string" && parseCore(m2.baseVersion) ? m2.baseVersion.trim() : null;
7632
7898
  } catch {
7633
7899
  return null;
@@ -7699,8 +7965,8 @@ function isStableUpdate(latest, installed) {
7699
7965
  return compareSemver(latest, installed) === 1;
7700
7966
  }
7701
7967
  function buildInstallCommand(repoRoot) {
7702
- const has = (f) => existsSync12(join25(repoRoot, f));
7703
- const local = existsSync12(join25(repoRoot, "node_modules", "@vortex-os", "base"));
7968
+ const has = (f) => existsSync13(join26(repoRoot, f));
7969
+ const local = existsSync13(join26(repoRoot, "node_modules", "@vortex-os", "base"));
7704
7970
  let installPart;
7705
7971
  if (!local) {
7706
7972
  installPart = `npm i -g ${PKG}@latest`;
@@ -7728,9 +7994,9 @@ function checkBaseUpdate(ctx) {
7728
7994
  }
7729
7995
 
7730
7996
  // ../plugins/session-rituals/dist/session-start-report.js
7731
- import { existsSync as existsSync13 } from "fs";
7997
+ import { existsSync as existsSync14 } from "fs";
7732
7998
  import { readdir as readdir17, readFile as readFile22, stat as stat9 } from "fs/promises";
7733
- import { join as join26 } from "path";
7999
+ import { join as join27 } from "path";
7734
8000
  var COUNTED_DIRS2 = ["_memory", "worklog", "decision-log"];
7735
8001
  var DEFAULT_GAP_WINDOW_DAYS = 30;
7736
8002
  function gapWindowSinceArg() {
@@ -7747,8 +8013,8 @@ async function collectSessionStartReport(ctx, opts) {
7747
8013
  const counts = {};
7748
8014
  const missing = [];
7749
8015
  for (const name of COUNTED_DIRS2) {
7750
- const dir = join26(ctx.dataDir, name);
7751
- if (!existsSync13(dir)) {
8016
+ const dir = join27(ctx.dataDir, name);
8017
+ if (!existsSync14(dir)) {
7752
8018
  missing.push(name);
7753
8019
  counts[name] = 0;
7754
8020
  continue;
@@ -7758,7 +8024,7 @@ async function collectSessionStartReport(ctx, opts) {
7758
8024
  const { recent, recentGroup, recentWorklogsOmitted, dates, latestBodies } = await scanWorklog(ctx.dataDir);
7759
8025
  const cutoff = isoDate2(addDays2(now, -(opts?.gapWindowDays ?? DEFAULT_GAP_WINDOW_DAYS)));
7760
8026
  const recentWorklogDates = dates.filter((d2) => d2 >= cutoff);
7761
- const mem = await scanMemoryTiers(join26(ctx.dataDir, "_memory"));
8027
+ const mem = await scanMemoryTiers(join27(ctx.dataDir, "_memory"));
7762
8028
  const ho = await scanHandoffs(ctx.dataDir);
7763
8029
  const handoffs = ho.active.map((h) => ({
7764
8030
  date: h.date,
@@ -7825,7 +8091,7 @@ async function scanMemoryTiers(memoryDir) {
7825
8091
  for (const e of entries) {
7826
8092
  if (!e.isFile() || !e.name.endsWith(".md"))
7827
8093
  continue;
7828
- const full = join26(memoryDir, e.name);
8094
+ const full = join27(memoryDir, e.name);
7829
8095
  if (e.name === "_INDEX.md") {
7830
8096
  indexExists = true;
7831
8097
  try {
@@ -7996,6 +8262,7 @@ function renderSessionStartReport(report, extras) {
7996
8262
  lines.push(` - \u2026(+${fl.omitted} more \u2014 \`vortex failure list\`)`);
7997
8263
  }
7998
8264
  const cu = extras?.catchUp;
8265
+ const as = extras?.archiveSync;
7999
8266
  if (cu && (cu.ingestedLocal > 0 || cu.indexedPulled > 0 || cu.errors > 0)) {
8000
8267
  const parts = [];
8001
8268
  if (cu.ingestedLocal > 0)
@@ -8004,9 +8271,19 @@ function renderSessionStartReport(report, extras) {
8004
8271
  parts.push(`${cu.indexedPulled} pulled`);
8005
8272
  const n = cu.ingestedLocal + cu.indexedPulled;
8006
8273
  let line = `- caught up: archived ${parts.join(" + ")} conversation${n === 1 ? "" : "s"}`;
8274
+ if (as?.committed)
8275
+ line += as.pushed ? " \xB7 committed & pushed" : " \xB7 committed";
8007
8276
  if (cu.errors > 0)
8008
8277
  line += ` (${cu.errors} error${cu.errors === 1 ? "" : "s"})`;
8009
8278
  lines.push(line);
8279
+ } else if (as?.committed) {
8280
+ lines.push(`- archive: changes committed${as.pushed ? " & pushed" : ""}`);
8281
+ }
8282
+ if (as && as.rawPruned > 0) {
8283
+ lines.push(`- \u{1F9F9} archive: removed ${as.rawPruned} raw transcript cop${as.rawPruned === 1 ? "y" : "ies"} older than ${as.retentionDays}d (local duplicates; the normalized archive keeps everything)`);
8284
+ }
8285
+ if (as && !as.pushed && as.pushSkipped && as.pushSkipped !== "off" && as.pushSkipped !== "no-commit") {
8286
+ lines.push(`- \u26A0\uFE0F auto-push is on but could not push (${as.pushSkipped}) \u2014 push manually or check the remote.`);
8010
8287
  }
8011
8288
  const vec = extras?.vectorized;
8012
8289
  if (vec && vec.sessionChunks > 0) {
@@ -8064,15 +8341,15 @@ async function countMarkdown3(dir, recursive) {
8064
8341
  } else if (e.isDirectory() && recursive) {
8065
8342
  if (e.name.startsWith(".") || e.name.startsWith("_"))
8066
8343
  continue;
8067
- total += await countMarkdown3(join26(dir, e.name), recursive);
8344
+ total += await countMarkdown3(join27(dir, e.name), recursive);
8068
8345
  }
8069
8346
  }
8070
8347
  return total;
8071
8348
  }
8072
8349
  var MAX_GROUP_READ = 20;
8073
8350
  async function scanWorklog(dataDir) {
8074
- const root = join26(dataDir, "worklog");
8075
- if (!existsSync13(root))
8351
+ const root = join27(dataDir, "worklog");
8352
+ if (!existsSync14(root))
8076
8353
  return { recent: null, recentGroup: [], recentWorklogsOmitted: 0, dates: [], latestBodies: [] };
8077
8354
  const dates = /* @__PURE__ */ new Set();
8078
8355
  const consistent = [];
@@ -8087,7 +8364,7 @@ async function scanWorklog(dataDir) {
8087
8364
  for (const e of entries) {
8088
8365
  const childRel = rel ? `${rel}/${e.name}` : e.name;
8089
8366
  if (e.isDirectory()) {
8090
- await walk5(join26(absDir, e.name), childRel);
8367
+ await walk5(join27(absDir, e.name), childRel);
8091
8368
  } else if (e.isFile()) {
8092
8369
  const m2 = e.name.match(/^(\d{4})-(\d{2})-(\d{2})(?:_\d{4})?-.+\.md$/);
8093
8370
  if (!m2)
@@ -8116,7 +8393,7 @@ async function scanWorklog(dataDir) {
8116
8393
  const recentGroup = [];
8117
8394
  const latestBodies = [];
8118
8395
  for (const g of group) {
8119
- const { title, body } = await readWorklogTitleAndBody(join26(root, g.rel));
8396
+ const { title, body } = await readWorklogTitleAndBody(join27(root, g.rel));
8120
8397
  recentGroup.push({ path: defangReportPath(`worklog/${g.rel}`), title });
8121
8398
  latestBodies.push(body);
8122
8399
  }
@@ -8195,10 +8472,10 @@ function isoDate2(d2) {
8195
8472
  }
8196
8473
 
8197
8474
  // ../plugins/session-rituals/dist/curate-cli.js
8198
- import { existsSync as existsSync14 } from "fs";
8475
+ import { existsSync as existsSync15 } from "fs";
8199
8476
  import { createHash as createHash3 } from "crypto";
8200
8477
  import { readFile as readFile23, readdir as readdir18 } from "fs/promises";
8201
- import { join as join27 } from "path";
8478
+ import { join as join28 } from "path";
8202
8479
  var SYSTEM_META_DIRS2 = /* @__PURE__ */ new Set([
8203
8480
  "worklog",
8204
8481
  "decision-log",
@@ -8278,10 +8555,10 @@ function joinRel(...parts) {
8278
8555
  }
8279
8556
  async function runCurateCandidates(repoRoot, options) {
8280
8557
  const maxEntries = options?.maxEntries ?? 200;
8281
- const dataDir = join27(repoRoot, "data");
8558
+ const dataDir = join28(repoRoot, "data");
8282
8559
  const candidates = [];
8283
8560
  let truncated = false;
8284
- if (existsSync14(dataDir)) {
8561
+ if (existsSync15(dataDir)) {
8285
8562
  async function visit(absDir, relDir) {
8286
8563
  if (candidates.length >= maxEntries) {
8287
8564
  truncated = true;
@@ -8304,7 +8581,7 @@ async function runCurateCandidates(repoRoot, options) {
8304
8581
  continue;
8305
8582
  if (atRoot && (SYSTEM_META_DIRS2.has(e.name) || e.name.startsWith("_")))
8306
8583
  continue;
8307
- await visit(join27(absDir, e.name), joinRel(relDir, e.name));
8584
+ await visit(join28(absDir, e.name), joinRel(relDir, e.name));
8308
8585
  } else if (e.isFile() && e.name.endsWith(".md")) {
8309
8586
  if (NON_DOC_FILES.has(e.name))
8310
8587
  continue;
@@ -8313,7 +8590,7 @@ async function runCurateCandidates(repoRoot, options) {
8313
8590
  let topic = null;
8314
8591
  let tags = [];
8315
8592
  try {
8316
- const raw = await readFile23(join27(absDir, e.name), "utf8");
8593
+ const raw = await readFile23(join28(absDir, e.name), "utf8");
8317
8594
  const parsed = parseFrontmatter(raw);
8318
8595
  if (typeof parsed.frontmatter.topic === "string") {
8319
8596
  topic = parsed.frontmatter.topic.trim().toLowerCase();
@@ -8351,7 +8628,7 @@ async function runCuratePreview(repoRoot, payload, now = /* @__PURE__ */ new Dat
8351
8628
  };
8352
8629
  }
8353
8630
  try {
8354
- validateDataRelativePath(join27(repoRoot, "data"), v2.effectiveRelPath);
8631
+ validateDataRelativePath(join28(repoRoot, "data"), v2.effectiveRelPath);
8355
8632
  } catch (e) {
8356
8633
  return {
8357
8634
  subcommand: "curate-preview",
@@ -8368,10 +8645,10 @@ async function runCuratePreview(repoRoot, payload, now = /* @__PURE__ */ new Dat
8368
8645
  let targetExists;
8369
8646
  let wouldDo;
8370
8647
  if (payload.action === "create-file") {
8371
- targetExists = existsSync14(join27(repoRoot, "data", v2.effectiveRelPath));
8648
+ targetExists = existsSync15(join28(repoRoot, "data", v2.effectiveRelPath));
8372
8649
  wouldDo = targetExists ? `create-file at ${v2.effectiveRelPath} \u2014 but the file already EXISTS, so accept would REFUSE (no overwrite).` : `create a new document at data/${v2.effectiveRelPath}.`;
8373
8650
  } else {
8374
- targetExists = existsSync14(join27(repoRoot, "data", v2.effectiveRelPath));
8651
+ targetExists = existsSync15(join28(repoRoot, "data", v2.effectiveRelPath));
8375
8652
  wouldDo = targetExists ? `append a "## ${payload.sectionHeader}" section to data/${v2.effectiveRelPath}.` : `append-section to data/${v2.effectiveRelPath} \u2014 but the file does NOT exist, so accept would FAIL (append-section never creates).`;
8376
8653
  }
8377
8654
  const nextActions = [];
@@ -8516,13 +8793,13 @@ function readCuratePayload(args) {
8516
8793
  break;
8517
8794
  }
8518
8795
  if (t === "--payload-file" && i + 1 < args.length) {
8519
- raw = readFileSync3(args[++i], "utf8");
8796
+ raw = readFileSync4(args[++i], "utf8");
8520
8797
  break;
8521
8798
  }
8522
8799
  }
8523
8800
  if (raw === null) {
8524
8801
  try {
8525
- raw = readFileSync3(0, "utf8");
8802
+ raw = readFileSync4(0, "utf8");
8526
8803
  } catch {
8527
8804
  raw = "";
8528
8805
  }
@@ -8546,18 +8823,18 @@ async function runVortexCli(argv, io) {
8546
8823
  const err = io?.stderr ?? ((s) => process.stderr.write(s));
8547
8824
  try {
8548
8825
  if (argv[0] === "statusline") {
8549
- const { runStatuslineCli: runStatuslineCli2 } = await import("./statusline-JTSL5CCH.js");
8826
+ const { runStatuslineCli: runStatuslineCli2 } = await import("./statusline-6BGWOOLA.js");
8550
8827
  const statuslineRoot = process.env.VORTEX_REPO_ROOT?.trim() || process.cwd();
8551
8828
  return runStatuslineCli2(argv.slice(1), statuslineRoot, out, err);
8552
8829
  }
8553
8830
  if (argv[0] === "guard") {
8554
- const { runGuardCli: runGuardCli2 } = await import("./guard-IMJR6ET7.js");
8831
+ const { runGuardCli: runGuardCli2 } = await import("./guard-S6NGMDXR.js");
8555
8832
  return runGuardCli2(argv.slice(1), out, err);
8556
8833
  }
8557
8834
  const repoRoot = resolveRepoRoot();
8558
8835
  if (argv[0] === "failure") {
8559
- const { runFailureCli: runFailureCli2 } = await import("./failures-PMURLMVB.js");
8560
- const { resolveInstanceRoot: resolveInstanceRoot2 } = await import("./guard-IMJR6ET7.js");
8836
+ const { runFailureCli: runFailureCli2 } = await import("./failures-DBXJKFYX.js");
8837
+ const { resolveInstanceRoot: resolveInstanceRoot2 } = await import("./guard-S6NGMDXR.js");
8561
8838
  const root = resolveInstanceRoot2(process.cwd()) ?? repoRoot;
8562
8839
  const ctx = makeContext(root);
8563
8840
  return runFailureCli2(argv.slice(1), ctx.dataDir, out, err);
@@ -8570,6 +8847,10 @@ async function runVortexCli(argv, io) {
8570
8847
  await runSessionEnd(repoRoot, out);
8571
8848
  return 0;
8572
8849
  }
8850
+ if (argv[0] === "catch-up") {
8851
+ await runCatchUpCli(repoRoot, out);
8852
+ return 0;
8853
+ }
8573
8854
  if (argv[0] === "check-updates") {
8574
8855
  const result2 = checkBaseUpdate(makeContext(repoRoot));
8575
8856
  out(JSON.stringify(result2, null, 2) + "\n");
@@ -8597,8 +8878,9 @@ async function runVortexCli(argv, io) {
8597
8878
 
8598
8879
  Commands:
8599
8880
  ${names}
8600
- session-start \u2014 emit the start-of-session boot report (git pull + data counts + catch-up)
8601
- session-end \u2014 no-op (kept for hook compatibility; worklog gap handling is at session-start)
8881
+ session-start \u2014 emit the start-of-session boot report (git pull + data counts + archive sync)
8882
+ session-end \u2014 best-effort archive sync at wind-down (ingest closed sessions + auto-commit; crash-safe \u2014 session-start repeats it)
8883
+ catch-up \u2014 explicit archive sync now (ingest closed sessions, sweep raw copies, auto-commit, opt-in push); run before a manual sync
8602
8884
  check-updates \u2014 check the npm registry for a newer @vortex-os/base (read-only; prints the exact update command)
8603
8885
  statusline \u2014 render the Claude Code status bar from stdin JSON (\`lite\` for 1-line; \`install [--lite]\` wires .claude/settings.json)
8604
8886
  failure \u2014 failure ledger (\`record\` an occurrence by recurrence key / \`list\` open groups with their escalation stage)
@@ -8669,14 +8951,14 @@ function memoryExtendedPresent() {
8669
8951
  }
8670
8952
  var VECTORIZE_LOCK_TTL_MS = 6 * 60 * 60 * 1e3;
8671
8953
  function vectorizeLockPath(ctx) {
8672
- return join28(ctx.dataDir, "_indexes", ".vectorize.lock");
8954
+ return join29(ctx.dataDir, "_indexes", ".vectorize.lock");
8673
8955
  }
8674
8956
  function vectorizeSetupInProgress(ctx) {
8675
8957
  const lock = vectorizeLockPath(ctx);
8676
8958
  try {
8677
- if (!existsSync15(lock))
8959
+ if (!existsSync16(lock))
8678
8960
  return false;
8679
- return Date.now() - statSync(lock).mtimeMs < VECTORIZE_LOCK_TTL_MS;
8961
+ return Date.now() - statSync2(lock).mtimeMs < VECTORIZE_LOCK_TTL_MS;
8680
8962
  } catch {
8681
8963
  return false;
8682
8964
  }
@@ -8695,24 +8977,24 @@ function spawnVectorizeSetup(repoRoot) {
8695
8977
  }
8696
8978
  async function runVectorizeSetup(repoRoot, out, err) {
8697
8979
  const ctx = makeContext(repoRoot);
8698
- const indexDir = join28(ctx.dataDir, "_indexes");
8699
- const finalDb = join28(indexDir, "memory.sqlite");
8700
- if (existsSync15(finalDb)) {
8980
+ const indexDir = join29(ctx.dataDir, "_indexes");
8981
+ const finalDb = join29(indexDir, "memory.sqlite");
8982
+ if (existsSync16(finalDb)) {
8701
8983
  out("recall index already present \u2014 nothing to do\n");
8702
8984
  return;
8703
8985
  }
8704
- mkdirSync(indexDir, { recursive: true });
8705
- const lockPath = vectorizeLockPath(ctx);
8986
+ mkdirSync2(indexDir, { recursive: true });
8987
+ const lockPath2 = vectorizeLockPath(ctx);
8706
8988
  const token = `${process.pid}-${process.hrtime.bigint()}`;
8707
8989
  let lockFd;
8708
8990
  try {
8709
- lockFd = openSync(lockPath, "wx");
8991
+ lockFd = openSync2(lockPath2, "wx");
8710
8992
  } catch (e) {
8711
8993
  if (e.code !== "EEXIST")
8712
8994
  throw e;
8713
8995
  let stale = true;
8714
8996
  try {
8715
- stale = Date.now() - statSync(lockPath).mtimeMs >= VECTORIZE_LOCK_TTL_MS;
8997
+ stale = Date.now() - statSync2(lockPath2).mtimeMs >= VECTORIZE_LOCK_TTL_MS;
8716
8998
  } catch {
8717
8999
  stale = true;
8718
9000
  }
@@ -8720,38 +9002,38 @@ async function runVectorizeSetup(repoRoot, out, err) {
8720
9002
  out("another recall setup is already in progress \u2014 skipping\n");
8721
9003
  return;
8722
9004
  }
8723
- rmSync(lockPath, { force: true });
9005
+ rmSync2(lockPath2, { force: true });
8724
9006
  try {
8725
- lockFd = openSync(lockPath, "wx");
9007
+ lockFd = openSync2(lockPath2, "wx");
8726
9008
  } catch {
8727
9009
  out("another recall setup just started \u2014 skipping\n");
8728
9010
  return;
8729
9011
  }
8730
9012
  }
8731
- const tmpDb = join28(indexDir, `memory.sqlite.building-${process.pid}`);
9013
+ const tmpDb = join29(indexDir, `memory.sqlite.building-${process.pid}`);
8732
9014
  const tmpSidecars = [tmpDb + "-wal", tmpDb + "-shm", tmpDb + "-journal"];
8733
9015
  const cleanTmp = () => {
8734
- rmSync(tmpDb, { force: true });
9016
+ rmSync2(tmpDb, { force: true });
8735
9017
  for (const s of tmpSidecars)
8736
- rmSync(s, { force: true });
9018
+ rmSync2(s, { force: true });
8737
9019
  };
8738
9020
  let tokenWritten = false;
8739
9021
  const releaseLock = () => {
8740
9022
  try {
8741
- const cur = existsSync15(lockPath) ? readFileSync3(lockPath, "utf8").trim() : "";
9023
+ const cur = existsSync16(lockPath2) ? readFileSync4(lockPath2, "utf8").trim() : "";
8742
9024
  if (cur === token || cur === "" && !tokenWritten)
8743
- rmSync(lockPath, { force: true });
9025
+ rmSync2(lockPath2, { force: true });
8744
9026
  } catch {
8745
9027
  }
8746
9028
  };
8747
9029
  try {
8748
9030
  try {
8749
- writeSync(lockFd, token + "\n");
9031
+ writeSync2(lockFd, token + "\n");
8750
9032
  tokenWritten = true;
8751
9033
  } finally {
8752
- closeSync(lockFd);
9034
+ closeSync2(lockFd);
8753
9035
  }
8754
- if (existsSync15(finalDb)) {
9036
+ if (existsSync16(finalDb)) {
8755
9037
  out("recall index already present \u2014 nothing to do\n");
8756
9038
  return;
8757
9039
  }
@@ -8767,7 +9049,7 @@ async function runVectorizeSetup(repoRoot, out, err) {
8767
9049
  } finally {
8768
9050
  db.close();
8769
9051
  }
8770
- if (existsSync15(tmpDb + "-wal")) {
9052
+ if (existsSync16(tmpDb + "-wal")) {
8771
9053
  throw new Error("temp index retained a WAL sidecar after consolidation; refusing to publish");
8772
9054
  }
8773
9055
  try {
@@ -8780,7 +9062,7 @@ async function runVectorizeSetup(repoRoot, out, err) {
8780
9062
  }
8781
9063
  throw e;
8782
9064
  }
8783
- rmSync(tmpDb, { force: true });
9065
+ rmSync2(tmpDb, { force: true });
8784
9066
  out(`recall index built \u2014 semantic search ready (memories: ${result.memories}, conversation chunks: ${result.sessionChunks})
8785
9067
  `);
8786
9068
  } catch (e) {
@@ -8816,7 +9098,8 @@ async function runSessionStart(repoRoot, out) {
8816
9098
  } catch {
8817
9099
  }
8818
9100
  const bookkeepingPrefix = frameworkBookkeepingPrefix(ctx);
8819
- const carryover = collectCarryover(repoRoot, (p) => p.startsWith(bookkeepingPrefix));
9101
+ const archivePrefix = archiveRelPath(ctx) + "/";
9102
+ const carryover = collectCarryover(repoRoot, (p) => p.startsWith(bookkeepingPrefix) || p.startsWith(archivePrefix));
8820
9103
  if (config.autoRecord.reindex && !git2?.conflict) {
8821
9104
  await autoReindexMemory(ctx);
8822
9105
  }
@@ -8830,14 +9113,14 @@ async function runSessionStart(repoRoot, out) {
8830
9113
  } catch {
8831
9114
  }
8832
9115
  }
8833
- let catchUp = null;
9116
+ let archiveSync = null;
8834
9117
  if (config.autoRecord.archive) {
8835
9118
  try {
8836
- const { catchUpSessions: catchUpSessions2 } = await import("./catch-up-GDDKPZHJ.js");
8837
- catchUp = await catchUpSessions2(ctx);
9119
+ archiveSync = await runArchiveSync(ctx, config, "start");
8838
9120
  } catch {
8839
9121
  }
8840
9122
  }
9123
+ const catchUp = archiveSync?.catchUp ?? null;
8841
9124
  let handoffPrune = null;
8842
9125
  if (config.autoRecord.handoff) {
8843
9126
  try {
@@ -8851,7 +9134,7 @@ async function runSessionStart(repoRoot, out) {
8851
9134
  let vectorized = null;
8852
9135
  let vectorizeSetupStarted = false;
8853
9136
  if (config.autoRecord.vectorize) {
8854
- const dbExists = existsSync15(join28(ctx.dataDir, "_indexes", "memory.sqlite"));
9137
+ const dbExists = existsSync16(join29(ctx.dataDir, "_indexes", "memory.sqlite"));
8855
9138
  const action = decideVectorizeAction({
8856
9139
  vectorizeOn: true,
8857
9140
  dbExists,
@@ -8877,7 +9160,7 @@ async function runSessionStart(repoRoot, out) {
8877
9160
  let failures = null;
8878
9161
  if (config.autoRecord.failures) {
8879
9162
  try {
8880
- const { scanFailures: scanFailures2, failureReportSlice: failureReportSlice2 } = await import("./failures-PMURLMVB.js");
9163
+ const { scanFailures: scanFailures2, failureReportSlice: failureReportSlice2 } = await import("./failures-DBXJKFYX.js");
8881
9164
  const slice = failureReportSlice2(await scanFailures2(ctx.dataDir));
8882
9165
  if (slice.warnings.length > 0)
8883
9166
  failures = slice;
@@ -8917,6 +9200,13 @@ async function runSessionStart(repoRoot, out) {
8917
9200
  baseVersion,
8918
9201
  missingWorklogDays,
8919
9202
  catchUp: catchUp ?? void 0,
9203
+ archiveSync: archiveSync ? {
9204
+ committed: archiveSync.committed,
9205
+ pushed: archiveSync.pushed,
9206
+ pushSkipped: archiveSync.pushSkipped,
9207
+ rawPruned: archiveSync.rawPruned,
9208
+ retentionDays: archiveSync.retentionDays
9209
+ } : void 0,
8920
9210
  vectorized: vectorized ?? void 0,
8921
9211
  vectorizeSetup: vectorizeSetupStarted || void 0,
8922
9212
  templateUpdate: templateUpdate ?? void 0,
@@ -8927,36 +9217,48 @@ async function runSessionStart(repoRoot, out) {
8927
9217
  failures: failures ?? void 0
8928
9218
  }));
8929
9219
  }
8930
- async function runSessionEnd(_repoRoot, _out) {
8931
- }
8932
- function gitOut(cwd, gitArgs) {
8933
- return execFileSync2("git", [...gitArgs], {
8934
- cwd,
8935
- encoding: "utf8",
8936
- stdio: ["ignore", "pipe", "ignore"]
8937
- });
8938
- }
8939
- function detectInterruptedGitOp(repoRoot) {
8940
- const markers = [
8941
- "MERGE_HEAD",
8942
- "rebase-merge",
8943
- "rebase-apply",
8944
- "CHERRY_PICK_HEAD",
8945
- "REVERT_HEAD",
8946
- "BISECT_LOG",
8947
- "index.lock"
8948
- ];
9220
+ async function runSessionEnd(repoRoot, out) {
8949
9221
  try {
8950
- const args = ["rev-parse", ...markers.flatMap((m2) => ["--git-path", m2])];
8951
- const resolved = gitOut(repoRoot, args).split(/\r?\n/).map((s) => s.trim());
8952
- for (let i = 0; i < markers.length; i++) {
8953
- const p = resolved[i];
8954
- if (p && existsSync15(isAbsolute4(p) ? p : join28(repoRoot, p)))
8955
- return markers[i];
9222
+ const ctx = makeContext(repoRoot);
9223
+ const config = loadVortexConfig(ctx);
9224
+ if (!config.autoRecord.archive || !config.autoRecord.archiveAtEnd)
9225
+ return;
9226
+ const r = await runArchiveSync(ctx, config, "end");
9227
+ if (r.lockSkipped) {
9228
+ out("archive sync skipped (another session is syncing)\n");
9229
+ return;
8956
9230
  }
9231
+ const parts = [];
9232
+ if (r.catchUp)
9233
+ parts.push(`${r.catchUp.ingestedLocal} archived`);
9234
+ if (r.rawPruned > 0)
9235
+ parts.push(`${r.rawPruned} raw pruned`);
9236
+ if (r.committed)
9237
+ parts.push("committed");
9238
+ if (r.pushed)
9239
+ parts.push("pushed");
9240
+ out(`session-end archive sync: ${parts.length ? parts.join(", ") : "nothing to do"}
9241
+ `);
8957
9242
  } catch {
8958
9243
  }
8959
- return null;
9244
+ }
9245
+ async function runCatchUpCli(repoRoot, out) {
9246
+ const ctx = makeContext(repoRoot);
9247
+ const config = loadVortexConfig(ctx);
9248
+ const r = await runArchiveSync(ctx, config, "manual");
9249
+ out(JSON.stringify({
9250
+ ran: r.ran,
9251
+ lockSkipped: r.lockSkipped,
9252
+ ingested: r.catchUp?.ingestedLocal ?? 0,
9253
+ indexedPulled: r.catchUp?.indexedPulled ?? 0,
9254
+ ingestErrors: r.catchUp?.errors ?? 0,
9255
+ rawPruned: r.rawPruned,
9256
+ rawRetentionDays: r.retentionDays,
9257
+ committed: r.committed,
9258
+ ...r.commitSkipped ? { commitSkipped: r.commitSkipped } : {},
9259
+ pushed: r.pushed,
9260
+ ...r.pushSkipped ? { pushSkipped: r.pushSkipped } : {}
9261
+ }, null, 2) + "\n");
8960
9262
  }
8961
9263
  function collectCarryover(repoRoot, ignore) {
8962
9264
  const interrupted = detectInterruptedGitOp(repoRoot);
@@ -8971,23 +9273,23 @@ function resolveSessionEnvironment(ctx, config) {
8971
9273
  let environment = resolveEnvironment(config, {
8972
9274
  hostname: hostname(),
8973
9275
  env: process.env,
8974
- pathExists: existsSync15
9276
+ pathExists: existsSync16
8975
9277
  });
8976
9278
  if (!environment)
8977
9279
  environment = process.env.VORTEX_ENV?.trim() || null;
8978
9280
  if (!environment) {
8979
- const envFile = join28(ctx.repoRoot, ".agent", "environment");
8980
- if (existsSync15(envFile)) {
8981
- environment = readFileSync3(envFile, "utf8").split(/\r?\n/)[0]?.trim() || null;
9281
+ const envFile = join29(ctx.repoRoot, ".agent", "environment");
9282
+ if (existsSync16(envFile)) {
9283
+ environment = readFileSync4(envFile, "utf8").split(/\r?\n/)[0]?.trim() || null;
8982
9284
  }
8983
9285
  }
8984
9286
  return environment;
8985
9287
  }
8986
9288
 
8987
9289
  // ../plugins/session-rituals/dist/ambient-recall.js
8988
- import { join as join29 } from "path";
9290
+ import { join as join30 } from "path";
8989
9291
  function defaultDbPath2(ctx) {
8990
- return join29(ctx.dataDir, "_indexes", "memory.sqlite");
9292
+ return join30(ctx.dataDir, "_indexes", "memory.sqlite");
8991
9293
  }
8992
9294
  function createAmbientRecaller(ctx, options) {
8993
9295
  const resolveDb = options.dbPath ?? defaultDbPath2;
@@ -9025,13 +9327,13 @@ function createAmbientRecaller(ctx, options) {
9025
9327
 
9026
9328
  // ../plugins/session-rituals/dist/worklog-write.js
9027
9329
  import { mkdir as mkdir10, writeFile as writeFile11 } from "fs/promises";
9028
- import { dirname as dirname6, join as join30 } from "path";
9330
+ import { dirname as dirname6, join as join31 } from "path";
9029
9331
  async function ensureWorklogEntry(ctx, opts) {
9030
9332
  const now = opts?.now ?? /* @__PURE__ */ new Date();
9031
9333
  const date = isoDate3(now);
9032
9334
  const time = isoTime2(now);
9033
9335
  const keyword = (opts?.keyword ?? "worklog").trim() || "worklog";
9034
- const store = new WorklogStore(join30(ctx.dataDir, "worklog"));
9336
+ const store = new WorklogStore(join31(ctx.dataDir, "worklog"));
9035
9337
  const existing = await store.get(date);
9036
9338
  if (existing) {
9037
9339
  return { path: existing.path, date: existing.date, keyword: existing.keyword, created: false };