@onebrain-ai/cli 2.1.4 → 2.1.6

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.
Files changed (2) hide show
  1. package/dist/onebrain +387 -202
  2. package/package.json +1 -1
package/dist/onebrain CHANGED
@@ -9105,10 +9105,10 @@ async function checkQmdEmbeddings(config) {
9105
9105
  check: "qmd-embeddings",
9106
9106
  status: "warn",
9107
9107
  message: summary,
9108
- hint: "Run onebrain doctor --fix to reindex and embed",
9108
+ hint: "Advisory: run /qmd embed when ready (or onebrain doctor --fix)",
9109
9109
  details: [
9110
9110
  `collection: ${config.qmd_collection}`,
9111
- "Run onebrain doctor --fix to reindex and embed"
9111
+ "Advisory: run /qmd embed when ready (or onebrain doctor --fix)"
9112
9112
  ]
9113
9113
  };
9114
9114
  }
@@ -9140,20 +9140,7 @@ async function checkOrphanCheckpoints(vaultRoot, config) {
9140
9140
  message: "0 orphans"
9141
9141
  };
9142
9142
  }
9143
- if (checkpointFiles.length === 0) {
9144
- return {
9145
- check: "orphan-checkpoints",
9146
- status: "ok",
9147
- message: "0 orphans"
9148
- };
9149
- }
9150
- let orphanCount = 0;
9151
- for (const filePath of checkpointFiles) {
9152
- const merged = await readMergedField(filePath);
9153
- if (merged !== true) {
9154
- orphanCount++;
9155
- }
9156
- }
9143
+ const orphanCount = checkpointFiles.length;
9157
9144
  if (orphanCount === 0) {
9158
9145
  return {
9159
9146
  check: "orphan-checkpoints",
@@ -9169,30 +9156,6 @@ async function checkOrphanCheckpoints(vaultRoot, config) {
9169
9156
  details: ["Run /wrapup to synthesize and merge them"]
9170
9157
  };
9171
9158
  }
9172
- async function readMergedField(filePath) {
9173
- try {
9174
- const file = Bun.file(filePath);
9175
- const text = await file.text();
9176
- if (!text.startsWith("---"))
9177
- return;
9178
- const endIdx = text.indexOf(`
9179
- ---`, 3);
9180
- if (endIdx === -1)
9181
- return;
9182
- const frontmatter = text.slice(3, endIdx).trim();
9183
- const parsed = import_yaml2.parse(frontmatter);
9184
- if (!parsed)
9185
- return;
9186
- const merged = parsed["merged"];
9187
- if (merged === true || merged === "true")
9188
- return true;
9189
- if (merged === false || merged === "false")
9190
- return false;
9191
- return;
9192
- } catch {
9193
- return;
9194
- }
9195
- }
9196
9159
  async function checkPluginFiles(vaultRoot) {
9197
9160
  const pluginBase = join2(vaultRoot, ".claude", "plugins", "onebrain");
9198
9161
  const missingFiles = [];
@@ -9401,17 +9364,16 @@ async function checkSettingsHooks(vaultRoot, config) {
9401
9364
  confirmedHooks.push("PostToolUse \u2713");
9402
9365
  }
9403
9366
  }
9404
- const precompactGroups = settings.hooks?.["PreCompact"] ?? [];
9405
- const hasStalePreCompact = precompactGroups.some((g) => g.hooks?.some((h) => (h.command ?? "").includes(PRECOMPACT_ONEBRAIN_SUBSTRING)));
9406
- if (hasStalePreCompact) {
9407
- warnings.push("stale PreCompact hook found");
9408
- }
9409
9367
  for (const event of Object.keys(settings.hooks ?? {})) {
9410
9368
  const groups = settings.hooks?.[event] ?? [];
9411
9369
  for (const g of groups) {
9412
9370
  for (const h of g.hooks ?? []) {
9371
+ const cmd = h.command ?? "";
9372
+ if (!ALLOWED_HOOK_EVENTS.has(event) && cmd.includes(ONEBRAIN_COMMAND_SUBSTRING)) {
9373
+ warnings.push(`stale ${event} hook found (onebrain CLI only registers Stop + PostToolUse)`);
9374
+ }
9413
9375
  for (const sub of STALE_HOOK_SUBSTRINGS) {
9414
- if ((h.command ?? "").includes(sub)) {
9376
+ if (cmd.includes(sub)) {
9415
9377
  warnings.push(`stale bash hook reference: ${sub}`);
9416
9378
  }
9417
9379
  }
@@ -9445,7 +9407,7 @@ async function checkSettingsHooks(vaultRoot, config) {
9445
9407
  ...okDetails.length > 0 ? { details: okDetails } : {}
9446
9408
  };
9447
9409
  }
9448
- var import_yaml2, STANDARD_FOLDER_KEYS, REQUIRED_PLUGIN_FILES, REQUIRED_PLUGIN_DIRS, STALE_BASH_FILES, REQUIRED_VAULT_YML_KEYS, REQUIRED_FOLDER_KEYS, REQUIRED_HOOKS, QMD_HOOK_SUBSTRING = "onebrain qmd-reindex", PRECOMPACT_ONEBRAIN_SUBSTRING = "onebrain", REQUIRED_PERMISSION = "Bash(onebrain *)", STALE_HOOK_SUBSTRINGS;
9410
+ var import_yaml2, STANDARD_FOLDER_KEYS, REQUIRED_PLUGIN_FILES, REQUIRED_PLUGIN_DIRS, STALE_BASH_FILES, REQUIRED_VAULT_YML_KEYS, REQUIRED_FOLDER_KEYS, REQUIRED_HOOKS, ALLOWED_HOOK_EVENTS, QMD_HOOK_SUBSTRING = "onebrain qmd-reindex", ONEBRAIN_COMMAND_SUBSTRING = "onebrain", REQUIRED_PERMISSION = "Bash(onebrain *)", STALE_HOOK_SUBSTRINGS;
9449
9411
  var init_validator = __esm(() => {
9450
9412
  import_yaml2 = __toESM(require_dist(), 1);
9451
9413
  STANDARD_FOLDER_KEYS = [
@@ -9481,9 +9443,9 @@ var init_validator = __esm(() => {
9481
9443
  "logs"
9482
9444
  ];
9483
9445
  REQUIRED_HOOKS = [
9484
- { event: "Stop", cmdSubstring: "onebrain checkpoint stop" },
9485
- { event: "PostCompact", cmdSubstring: "onebrain checkpoint postcompact" }
9446
+ { event: "Stop", cmdSubstring: "onebrain checkpoint stop" }
9486
9447
  ];
9448
+ ALLOWED_HOOK_EVENTS = new Set(["Stop", "PostToolUse"]);
9487
9449
  STALE_HOOK_SUBSTRINGS = ["checkpoint-hook.sh", "session-init.sh"];
9488
9450
  });
9489
9451
 
@@ -9497,7 +9459,7 @@ var init_lib = __esm(() => {
9497
9459
  var require_package = __commonJS((exports, module) => {
9498
9460
  module.exports = {
9499
9461
  name: "@onebrain-ai/cli",
9500
- version: "2.1.4",
9462
+ version: "2.1.6",
9501
9463
  description: "CLI for OneBrain \u2014 personal AI OS for Obsidian with persistent memory, 24+ skills, and Claude Code integration",
9502
9464
  keywords: [
9503
9465
  "onebrain",
@@ -9562,6 +9524,10 @@ function barBlank() {
9562
9524
  out(`${bar}
9563
9525
  `);
9564
9526
  }
9527
+ function barOpen(msg) {
9528
+ out(`${import_picocolors2.default.cyan("\u250C")} ${msg}
9529
+ `);
9530
+ }
9565
9531
  function close(msg, isError = false, isWarning = false) {
9566
9532
  if (isError) {
9567
9533
  out(`${import_picocolors2.default.cyan("\u2514")} ${import_picocolors2.default.bold(import_picocolors2.default.red(msg))}
@@ -10011,12 +9977,22 @@ function applyHooks(settings) {
10011
9977
  settings.hooks = {};
10012
9978
  const hooks = settings.hooks;
10013
9979
  const result = {};
10014
- for (const [event, staleCmd] of Object.entries(STALE_HOOK_COMMANDS)) {
10015
- if (!hooks[event])
9980
+ for (const event of Object.keys(hooks)) {
9981
+ if (ALLOWED_HOOK_EVENTS2.has(event))
10016
9982
  continue;
10017
- hooks[event] = hooks[event].filter((group) => !group.hooks?.some((entry) => entry.command === staleCmd));
10018
- if (hooks[event].length === 0)
9983
+ const groups = hooks[event] ?? [];
9984
+ const filtered = groups.map((group) => ({
9985
+ ...group,
9986
+ hooks: (group.hooks ?? []).filter((entry) => {
9987
+ const cmd = entry.command ?? "";
9988
+ return !cmd.includes("onebrain");
9989
+ })
9990
+ })).filter((group) => (group.hooks?.length ?? 0) > 0);
9991
+ if (filtered.length === 0) {
10019
9992
  delete hooks[event];
9993
+ } else {
9994
+ hooks[event] = filtered;
9995
+ }
10020
9996
  }
10021
9997
  for (const event of HOOK_EVENTS) {
10022
9998
  const cmd = HOOK_COMMANDS[event];
@@ -10205,20 +10181,16 @@ async function registerHooksCommand(vaultDir) {
10205
10181
  process.exit(1);
10206
10182
  }
10207
10183
  }
10208
- var import_picocolors4, HOOK_COMMANDS, HOOK_EVENTS, STALE_HOOK_COMMANDS, PERMISSIONS_TO_ADD, ONEBRAIN_MARKER = "# onebrain", PATH_EXPORT = 'export PATH="$HOME/.bun/bin:$HOME/.npm-global/bin:$PATH"', QMD_CMD = "onebrain qmd-reindex", QMD_MATCHER = "Write|Edit";
10184
+ var import_picocolors4, HOOK_COMMANDS, HOOK_EVENTS, PERMISSIONS_TO_ADD, ONEBRAIN_MARKER = "# onebrain", PATH_EXPORT = 'export PATH="$HOME/.bun/bin:$HOME/.npm-global/bin:$PATH"', ALLOWED_HOOK_EVENTS2, QMD_CMD = "onebrain qmd-reindex", QMD_MATCHER = "Write|Edit";
10209
10185
  var init_register_hooks = __esm(() => {
10210
10186
  init_dist2();
10211
10187
  init_lib();
10212
10188
  init_harness();
10213
10189
  import_picocolors4 = __toESM(require_picocolors(), 1);
10214
10190
  HOOK_COMMANDS = {
10215
- Stop: "onebrain checkpoint stop",
10216
- PostCompact: "onebrain checkpoint postcompact"
10217
- };
10218
- HOOK_EVENTS = ["Stop", "PostCompact"];
10219
- STALE_HOOK_COMMANDS = {
10220
- PreCompact: "onebrain checkpoint precompact"
10191
+ Stop: "onebrain checkpoint stop"
10221
10192
  };
10193
+ HOOK_EVENTS = ["Stop"];
10222
10194
  PERMISSIONS_TO_ADD = [
10223
10195
  "Read",
10224
10196
  "Write",
@@ -10235,6 +10207,7 @@ var init_register_hooks = __esm(() => {
10235
10207
  "WebFetch",
10236
10208
  "WebSearch"
10237
10209
  ];
10210
+ ALLOWED_HOOK_EVENTS2 = new Set(["Stop", "PostToolUse"]);
10238
10211
  });
10239
10212
 
10240
10213
  // src/commands/internal/vault-sync.ts
@@ -10788,7 +10761,7 @@ var import_picocolors5 = __toESM(require_picocolors(), 1);
10788
10761
  var import_picocolors = __toESM(require_picocolors(), 1);
10789
10762
  function resolveBinaryVersion() {
10790
10763
  if (true)
10791
- return "2.1.4";
10764
+ return "2.1.6";
10792
10765
  try {
10793
10766
  const pkg = require_package();
10794
10767
  return pkg.version ?? "dev";
@@ -10797,43 +10770,52 @@ function resolveBinaryVersion() {
10797
10770
  }
10798
10771
  }
10799
10772
  var ART_LINES = [
10800
- ` \u25C6${"\u2500".repeat(25)}\u25C6`,
10801
- " \u250C\u2500\u2510\u250C\u2510\u2577\u250C\u2500\u2574\u250C\u2510 \u250C\u2500\u2510\u250C\u2500\u2510\u2577\u250C\u2510\u2577",
10802
- " \u2502 \u2502\u2502\u2514\u2524\u251C\u2574 \u251C\u2534\u2510\u251C\u252C\u2518\u251C\u2500\u2524\u2502\u2502\u2514\u2524",
10803
- " \u2514\u2500\u2518\u2575 \u2575\u2514\u2500\u2574\u2514\u2500\u2518\u2575\u2514\u2574\u2575 \u2575\u2575\u2575 \u2575",
10804
- ` \u25C6${"\u2500".repeat(25)}\u25C6`
10773
+ ` \u25C6${"\u2500".repeat(26)}\u25C6`,
10774
+ " \u250C\u2500\u2510\u250C\u2510\u2577\u250C\u2500\u2574\u250C\u2510 \u250C\u2500\u2510\u250C\u2500\u2510\u2577\u250C\u2510\u2577",
10775
+ " \u2502 \u2502\u2502\u2514\u2524\u251C\u2574 \u251C\u2534\u2510\u251C\u252C\u2518\u251C\u2500\u2524\u2502\u2502\u2514\u2524",
10776
+ " \u2514\u2500\u2518\u2575 \u2575\u2514\u2500\u2574\u2514\u2500\u2518\u2575\u2514\u2574\u2575 \u2575\u2575\u2575 \u2575",
10777
+ ` \u25C6${"\u2500".repeat(26)}\u25C6`
10805
10778
  ];
10806
- var TAGLINE = "Your AI Thinking Partner";
10779
+ var PREFIX = "Your AI ";
10780
+ var TAGLINE_LEAD = " ";
10781
+ var TAGLINE_FALLBACK = `${PREFIX}Thinking Partner`;
10807
10782
  var BANNER_LINE_COUNT = 1 + ART_LINES.length + 3;
10783
+ var PREFIX_COLOR = [120, 230, 255];
10784
+ var TRAILING_COLOR = [255, 80, 255];
10785
+ var FINAL_COLOR = [120, 230, 255];
10786
+ var SENTENCES = [
10787
+ { trailing: "Remembers You", trailingWords: ["Remembers", "You"], wordTicks: [24, 32] },
10788
+ { trailing: "Catches Insights", trailingWords: ["Catches", "Insights"], wordTicks: [27, 26] },
10789
+ { trailing: "Thinking Partner", trailingWords: ["Thinking", "Partner"], wordTicks: [26, 31] }
10790
+ ];
10808
10791
  function supportsRgb() {
10809
10792
  const c = process.env["COLORTERM"] ?? "";
10810
10793
  return c === "truecolor" || c === "24bit";
10811
10794
  }
10795
+ function rgb(r, g, b, ch) {
10796
+ return `\x1B[1;38;2;${r};${g};${b}m${ch}\x1B[0m`;
10797
+ }
10798
+ function rgbStr(c, ch) {
10799
+ return rgb(c[0], c[1], c[2], ch);
10800
+ }
10812
10801
  function hsvToRgb(h, floor = 80) {
10813
10802
  const c = 255;
10814
10803
  const x = Math.round(c * (1 - Math.abs(h / 60 % 2 - 1)));
10815
10804
  let r = 0;
10816
10805
  let g = 0;
10817
10806
  let b = 0;
10818
- if (h < 60) {
10819
- r = c;
10820
- g = x;
10821
- } else if (h < 120) {
10822
- r = x;
10823
- g = c;
10824
- } else if (h < 180) {
10825
- g = c;
10826
- b = x;
10827
- } else if (h < 240) {
10828
- g = x;
10829
- b = c;
10830
- } else if (h < 300) {
10831
- r = x;
10832
- b = c;
10833
- } else {
10834
- r = c;
10835
- b = x;
10836
- }
10807
+ if (h < 60)
10808
+ [r, g, b] = [c, x, 0];
10809
+ else if (h < 120)
10810
+ [r, g, b] = [x, c, 0];
10811
+ else if (h < 180)
10812
+ [r, g, b] = [0, c, x];
10813
+ else if (h < 240)
10814
+ [r, g, b] = [0, x, c];
10815
+ else if (h < 300)
10816
+ [r, g, b] = [x, 0, c];
10817
+ else
10818
+ [r, g, b] = [c, 0, x];
10837
10819
  return [Math.min(255, r + floor), Math.min(255, g + floor), Math.min(255, b + floor)];
10838
10820
  }
10839
10821
  var HUE_PER_CHAR = 10;
@@ -10844,15 +10826,25 @@ function neonLine(line, lineIndex = 0, floor = 80) {
10844
10826
  return ch;
10845
10827
  const hue = ((i * HUE_PER_CHAR - lineIndex * HUE_PER_ROW) % 360 + 360) % 360;
10846
10828
  const [r, g, b] = hsvToRgb(hue, floor);
10847
- return `\x1B[1;38;2;${r};${g};${b}m${ch}\x1B[0m`;
10829
+ return rgb(r, g, b, ch);
10848
10830
  }).join("");
10849
10831
  }
10850
- function scanLine(line) {
10851
- return line.split("").map((ch) => ch === " " ? ch : `\x1B[1;38;2;140;255;255m${ch}\x1B[0m`).join("");
10832
+ function whiteLine(line) {
10833
+ return line.split("").map((ch) => ch === " " ? ch : `\x1B[1;97m${ch}\x1B[0m`).join("");
10834
+ }
10835
+ function whiteGlowLine(line, alpha) {
10836
+ return line.split("").map((ch) => ch === " " ? ch : `\x1B[1;38;2;${alpha};${alpha};${alpha}m${ch}\x1B[0m`).join("");
10852
10837
  }
10853
10838
  function dimLine(line) {
10854
10839
  return line.split("").map((ch) => ch === " " ? ch : `\x1B[2;38;2;50;50;70m${ch}\x1B[0m`).join("");
10855
10840
  }
10841
+ function scanLineCh(line) {
10842
+ return line.split("").map((ch) => ch === " " ? ch : rgb(140, 255, 255, ch)).join("");
10843
+ }
10844
+ var CURSOR = rgb(140, 255, 255, "\u258C");
10845
+ var GLYPHS = "\u2593\u2591\u2592\u2588\u2502\u2524\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u256A\u256B\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u2518\u250C\u2551\u258C\u2580\u2584\u2590\u2206\u0192\u03A9\xA7\xB6\xB1\xF7\xD7\xF8\xA5\u20AC";
10846
+ var randGlyph = () => GLYPHS[Math.floor(Math.random() * GLYPHS.length)] ?? "?";
10847
+ var glitchWhite = (g) => `\x1B[1;97m${g}\x1B[0m`;
10856
10848
  function outb(str) {
10857
10849
  process.stdout.write(Buffer.from(str, "utf8"));
10858
10850
  }
@@ -10869,11 +10861,274 @@ function printFrame(artLines, tagline) {
10869
10861
  outb(`
10870
10862
  `);
10871
10863
  }
10864
+ function blankTagline() {
10865
+ return `${TAGLINE_LEAD}${" ".repeat(TAGLINE_FALLBACK.length)}`;
10866
+ }
10867
+ function buildTaglineLine(prefixLockedChars, trailingPart) {
10868
+ let s = TAGLINE_LEAD;
10869
+ for (let i = 0;i < PREFIX.length; i++) {
10870
+ if (i < prefixLockedChars) {
10871
+ s += PREFIX[i] === " " ? " " : rgbStr(PREFIX_COLOR, PREFIX[i]);
10872
+ } else {
10873
+ s += " ";
10874
+ }
10875
+ }
10876
+ s += trailingPart;
10877
+ return `${s}\x1B[K`;
10878
+ }
10879
+ var LOCK_LATENCY = 4;
10880
+ var PREFIX_TICK_MS = [27, 27];
10881
+ var INTER_WORD_PAUSE_MS = 65;
10882
+ var SENTENCE_HOLD_MS = 500;
10883
+ var WIPE_TICK_MS = 22;
10884
+ var WIPE_TRAIL = 3;
10885
+ var WIPE_PAUSE_MS = 80;
10886
+ async function playBannerIntro(rainbowArt, whiteArt) {
10887
+ const delay = (ms) => new Promise((r) => setTimeout(r, ms));
10888
+ const up = (n) => outb(`\x1B[${n}F`);
10889
+ printFrame(ART_LINES.map(dimLine), blankTagline());
10890
+ for (let scan = 0;scan < ART_LINES.length; scan++) {
10891
+ await delay(55);
10892
+ up(BANNER_LINE_COUNT);
10893
+ printFrame(ART_LINES.map((l, i) => {
10894
+ if (i < scan - 2)
10895
+ return whiteLine(l);
10896
+ if (i === scan - 2)
10897
+ return whiteGlowLine(l, 200);
10898
+ if (i === scan - 1)
10899
+ return whiteGlowLine(l, 230);
10900
+ if (i === scan)
10901
+ return scanLineCh(l);
10902
+ return dimLine(l);
10903
+ }), blankTagline());
10904
+ }
10905
+ await delay(40);
10906
+ up(BANNER_LINE_COUNT);
10907
+ printFrame(whiteArt, blankTagline());
10908
+ await delay(600);
10909
+ let minD = 0;
10910
+ let maxD = 0;
10911
+ for (let row = 0;row < ART_LINES.length; row++) {
10912
+ minD = Math.min(minD, -row * 3);
10913
+ maxD = Math.max(maxD, ART_LINES[row].length - 1 - row * 3);
10914
+ }
10915
+ function flowFrame(frontD) {
10916
+ return ART_LINES.map((line, row) => line.split("").map((ch, col) => {
10917
+ if (ch === " ")
10918
+ return ch;
10919
+ const d = col - 3 * row;
10920
+ if (d <= frontD) {
10921
+ const hue = ((col * HUE_PER_CHAR - row * HUE_PER_ROW) % 360 + 360) % 360;
10922
+ const [r, g, b] = hsvToRgb(hue);
10923
+ return rgb(r, g, b, ch);
10924
+ }
10925
+ return `\x1B[1;97m${ch}\x1B[0m`;
10926
+ }).join(""));
10927
+ }
10928
+ for (let d = minD;d <= maxD; d++) {
10929
+ await delay(9);
10930
+ up(BANNER_LINE_COUNT);
10931
+ printFrame(flowFrame(d), blankTagline());
10932
+ }
10933
+ up(BANNER_LINE_COUNT);
10934
+ printFrame(rainbowArt, blankTagline());
10935
+ await delay(180);
10936
+ function shimmerArtFrame(highlight) {
10937
+ return ART_LINES.map((line, row) => line.split("").map((ch, col) => {
10938
+ if (ch === " ")
10939
+ return ch;
10940
+ const d = col - 3 * row;
10941
+ if (Math.abs(d - highlight) <= 1)
10942
+ return `\x1B[1;97m${ch}\x1B[0m`;
10943
+ const hue = ((col * HUE_PER_CHAR - row * HUE_PER_ROW) % 360 + 360) % 360;
10944
+ const [r, g, b] = hsvToRgb(hue);
10945
+ return rgb(r, g, b, ch);
10946
+ }).join(""));
10947
+ }
10948
+ for (let d = minD;d <= maxD; d++) {
10949
+ await delay(9);
10950
+ up(BANNER_LINE_COUNT);
10951
+ printFrame(shimmerArtFrame(d), blankTagline());
10952
+ }
10953
+ up(BANNER_LINE_COUNT);
10954
+ printFrame(rainbowArt, blankTagline());
10955
+ await delay(80);
10956
+ }
10957
+ async function decodeFirstSentence(rainbowArt, s) {
10958
+ const delay = (ms) => new Promise((r) => setTimeout(r, ms));
10959
+ const up = (n) => outb(`\x1B[${n}F`);
10960
+ const prefixWords = ["Your", "AI"];
10961
+ for (let wi = 0;wi < prefixWords.length; wi++) {
10962
+ const w = prefixWords[wi];
10963
+ const tickMs = PREFIX_TICK_MS[wi];
10964
+ const totalTicks = w.length + LOCK_LATENCY;
10965
+ const baseIdx = prefixWords.slice(0, wi).reduce((a, x) => a + x.length + 1, 0);
10966
+ for (let t = 1;t <= totalTicks; t++) {
10967
+ await delay(tickMs);
10968
+ up(BANNER_LINE_COUNT);
10969
+ let prefixPart = TAGLINE_LEAD;
10970
+ for (let i = 0;i < PREFIX.length; i++) {
10971
+ if (i < baseIdx) {
10972
+ prefixPart += PREFIX[i] === " " ? " " : rgbStr(PREFIX_COLOR, PREFIX[i]);
10973
+ } else if (i >= baseIdx + w.length) {
10974
+ prefixPart += " ";
10975
+ } else {
10976
+ const localIdx = i - baseIdx;
10977
+ const age = t - localIdx;
10978
+ if (age > LOCK_LATENCY)
10979
+ prefixPart += rgbStr(PREFIX_COLOR, PREFIX[i]);
10980
+ else if (age > 0)
10981
+ prefixPart += glitchWhite(randGlyph());
10982
+ else if (age === 0 && t < w.length)
10983
+ prefixPart += CURSOR;
10984
+ else
10985
+ prefixPart += " ";
10986
+ }
10987
+ }
10988
+ const trailingBlank = " ".repeat(s.trailing.length);
10989
+ printFrame(rainbowArt, `${prefixPart}${trailingBlank}\x1B[K`);
10990
+ }
10991
+ if (wi < prefixWords.length - 1) {
10992
+ await delay(INTER_WORD_PAUSE_MS);
10993
+ }
10994
+ }
10995
+ await delay(INTER_WORD_PAUSE_MS);
10996
+ await decodeTrailing(rainbowArt, s, PREFIX.length);
10997
+ }
10998
+ async function decodeTrailing(rainbowArt, s, lockedPrefixChars) {
10999
+ const delay = (ms) => new Promise((r) => setTimeout(r, ms));
11000
+ const up = (n) => outb(`\x1B[${n}F`);
11001
+ const words = s.trailingWords;
11002
+ const ticks = s.wordTicks;
11003
+ const offsets = [];
11004
+ let off = 0;
11005
+ for (const w of words) {
11006
+ offsets.push(off);
11007
+ off += w.length + 1;
11008
+ }
11009
+ for (let wi = 0;wi < words.length; wi++) {
11010
+ const w = words[wi];
11011
+ const tickMs = ticks[wi];
11012
+ const totalTicks = w.length + LOCK_LATENCY;
11013
+ for (let t = 1;t <= totalTicks; t++) {
11014
+ await delay(tickMs);
11015
+ up(BANNER_LINE_COUNT);
11016
+ let trailing = "";
11017
+ for (let j = 0;j < s.trailing.length; j++) {
11018
+ const ch = s.trailing[j];
11019
+ if (ch === " ") {
11020
+ trailing += " ";
11021
+ continue;
11022
+ }
11023
+ let owningWi = -1;
11024
+ let localIdx = -1;
11025
+ for (let k = 0;k < words.length; k++) {
11026
+ const start = offsets[k];
11027
+ const end = start + words[k].length;
11028
+ if (j >= start && j < end) {
11029
+ owningWi = k;
11030
+ localIdx = j - start;
11031
+ break;
11032
+ }
11033
+ }
11034
+ if (owningWi < wi) {
11035
+ trailing += rgbStr(TRAILING_COLOR, ch);
11036
+ } else if (owningWi > wi) {
11037
+ trailing += " ";
11038
+ } else {
11039
+ const age = t - localIdx;
11040
+ if (age > LOCK_LATENCY)
11041
+ trailing += rgbStr(TRAILING_COLOR, ch);
11042
+ else if (age > 0)
11043
+ trailing += glitchWhite(randGlyph());
11044
+ else if (age === 0 && t < w.length)
11045
+ trailing += CURSOR;
11046
+ else
11047
+ trailing += " ";
11048
+ }
11049
+ }
11050
+ printFrame(rainbowArt, buildTaglineLine(lockedPrefixChars, trailing));
11051
+ }
11052
+ if (wi < words.length - 1) {
11053
+ await delay(INTER_WORD_PAUSE_MS);
11054
+ }
11055
+ }
11056
+ }
11057
+ async function wipeSwapTransition(rainbowArt, from, to) {
11058
+ const delay = (ms) => new Promise((r) => setTimeout(r, ms));
11059
+ const up = (n) => outb(`\x1B[${n}F`);
11060
+ for (let pos = from.trailing.length - 1;pos >= -WIPE_TRAIL; pos--) {
11061
+ await delay(WIPE_TICK_MS);
11062
+ up(BANNER_LINE_COUNT);
11063
+ let trailing = "";
11064
+ for (let j = 0;j < from.trailing.length; j++) {
11065
+ const ch = from.trailing[j];
11066
+ if (ch === " ") {
11067
+ trailing += " ";
11068
+ continue;
11069
+ }
11070
+ const offset = j - pos;
11071
+ if (offset >= 0 && offset <= WIPE_TRAIL) {
11072
+ trailing += rgb(140, 255, 255, randGlyph());
11073
+ } else if (j > pos) {
11074
+ trailing += " ";
11075
+ } else {
11076
+ trailing += rgbStr(TRAILING_COLOR, ch);
11077
+ }
11078
+ }
11079
+ printFrame(rainbowArt, buildTaglineLine(PREFIX.length, trailing));
11080
+ }
11081
+ await delay(WIPE_PAUSE_MS);
11082
+ await decodeTrailing(rainbowArt, to, PREFIX.length);
11083
+ }
11084
+ async function lockShimmer(rainbowArt, s) {
11085
+ const delay = (ms) => new Promise((r) => setTimeout(r, ms));
11086
+ const up = (n) => outb(`\x1B[${n}F`);
11087
+ const SHIMMER_TICK_MS = 22;
11088
+ const TRAIL = 3;
11089
+ const STOPS = [
11090
+ [255, 255, 255],
11091
+ [200, 245, 255],
11092
+ [150, 235, 255]
11093
+ ];
11094
+ const fullText = PREFIX + s.trailing;
11095
+ const N = fullText.length;
11096
+ for (let pos = 0;pos <= N + TRAIL; pos++) {
11097
+ await delay(SHIMMER_TICK_MS);
11098
+ up(BANNER_LINE_COUNT);
11099
+ let line = TAGLINE_LEAD;
11100
+ for (let j = 0;j < N; j++) {
11101
+ const ch = fullText[j];
11102
+ if (ch === " ") {
11103
+ line += " ";
11104
+ continue;
11105
+ }
11106
+ const offset = pos - j;
11107
+ if (offset >= 0 && offset < TRAIL) {
11108
+ line += rgbStr(STOPS[offset], ch);
11109
+ } else if (offset >= TRAIL) {
11110
+ line += rgbStr(FINAL_COLOR, ch);
11111
+ } else {
11112
+ const baseColor = j < PREFIX.length ? PREFIX_COLOR : TRAILING_COLOR;
11113
+ line += rgbStr(baseColor, ch);
11114
+ }
11115
+ }
11116
+ line += "\x1B[K";
11117
+ printFrame(rainbowArt, line);
11118
+ }
11119
+ up(BANNER_LINE_COUNT);
11120
+ let finalLine = TAGLINE_LEAD;
11121
+ for (let j = 0;j < N; j++) {
11122
+ const ch = fullText[j];
11123
+ finalLine += ch === " " ? " " : rgbStr(FINAL_COLOR, ch);
11124
+ }
11125
+ finalLine += "\x1B[K";
11126
+ printFrame(rainbowArt, finalLine);
11127
+ await delay(150);
11128
+ }
10872
11129
  async function printBanner() {
10873
11130
  if (!process.stdout.isTTY)
10874
11131
  return;
10875
- const delay = (ms) => new Promise((r) => setTimeout(r, ms));
10876
- const up = (n) => outb(`\x1B[${n}F`);
10877
11132
  if (!supportsRgb()) {
10878
11133
  outb(`
10879
11134
  `);
@@ -10882,84 +11137,24 @@ async function printBanner() {
10882
11137
  `);
10883
11138
  outb(`
10884
11139
  `);
10885
- outb(` ${import_picocolors.default.bold(import_picocolors.default.magenta(TAGLINE))}
11140
+ outb(`${TAGLINE_LEAD}${import_picocolors.default.bold(import_picocolors.default.cyan(TAGLINE_FALLBACK))}
10886
11141
  `);
10887
11142
  outb(`
10888
11143
  `);
10889
11144
  return;
10890
11145
  }
10891
- outb("\x1B[?25l");
11146
+ const rainbowArt = ART_LINES.map((l, i) => neonLine(l, i));
11147
+ const whiteArt = ART_LINES.map((l) => whiteLine(l));
10892
11148
  try {
10893
- let diagFrame = function(highlight) {
10894
- return ART_LINES.map((line, row) => line.split("").map((ch, col) => {
10895
- if (ch === " ")
10896
- return ch;
10897
- const d = col - 3 * row;
10898
- if (Math.abs(d - highlight) <= 1)
10899
- return `\x1B[1;97m${ch}\x1B[0m`;
10900
- const hue = ((col * HUE_PER_CHAR - row * HUE_PER_ROW) % 360 + 360) % 360;
10901
- const [r, g, b] = hsvToRgb(hue);
10902
- return `\x1B[1;38;2;${r};${g};${b}m${ch}\x1B[0m`;
10903
- }).join(""));
10904
- };
10905
- printFrame(ART_LINES.map(dimLine), ` ${" ".repeat(TAGLINE.length)}`);
10906
- for (let scan = 0;scan < ART_LINES.length; scan++) {
10907
- await delay(65);
10908
- up(BANNER_LINE_COUNT);
10909
- printFrame(ART_LINES.map((l, i) => {
10910
- if (i < scan - 2)
10911
- return neonLine(l, i);
10912
- if (i === scan - 2)
10913
- return neonLine(l, i, 120);
10914
- if (i === scan - 1)
10915
- return neonLine(l, i, 200);
10916
- if (i === scan)
10917
- return scanLine(l);
10918
- return dimLine(l);
10919
- }), ` ${" ".repeat(TAGLINE.length)}`);
10920
- }
10921
- await delay(60);
10922
- up(BANNER_LINE_COUNT);
10923
- printFrame(ART_LINES.map((l, i) => neonLine(l, i)), ` ${" ".repeat(TAGLINE.length)}`);
10924
- let minD = 0;
10925
- let maxD = 0;
10926
- for (let row = 0;row < ART_LINES.length; row++) {
10927
- minD = Math.min(minD, -row * 3);
10928
- maxD = Math.max(maxD, ART_LINES[row].length - 1 - row * 3);
10929
- }
10930
- for (let d = minD;d <= maxD; d++) {
10931
- await delay(16);
10932
- up(BANNER_LINE_COUNT);
10933
- printFrame(diagFrame(d), ` ${" ".repeat(TAGLINE.length)}`);
10934
- }
10935
- for (let d = maxD;d >= minD; d--) {
10936
- await delay(16);
10937
- up(BANNER_LINE_COUNT);
10938
- printFrame(diagFrame(d), ` ${" ".repeat(TAGLINE.length)}`);
10939
- }
10940
- up(BANNER_LINE_COUNT);
10941
- printFrame(ART_LINES.map((l, i) => neonLine(l, i)), ` ${" ".repeat(TAGLINE.length)}`);
10942
- await delay(200);
10943
- const cursor = "\x1B[1;38;2;140;255;255m\u258C\x1B[0m";
10944
- for (let len = 1;len <= TAGLINE.length; len++) {
10945
- await delay(32);
10946
- up(BANNER_LINE_COUNT);
10947
- const hasCursor = len < TAGLINE.length;
10948
- printFrame(ART_LINES.map((l, i) => neonLine(l, i)), ` \x1B[1;97m${TAGLINE.slice(0, len)}\x1B[0m${hasCursor ? cursor : ""}${" ".repeat(Math.max(0, TAGLINE.length - len - 1))}`);
10949
- }
10950
- const tagWithCursor = ` \x1B[1;97m${TAGLINE}\x1B[0m${cursor}`;
10951
- const tagWhite = ` \x1B[1;97m${TAGLINE}\x1B[0m\x1B[K`;
10952
- for (let b = 0;b < 2; b++) {
10953
- await delay(600);
10954
- up(BANNER_LINE_COUNT);
10955
- printFrame(ART_LINES.map((l, i) => neonLine(l, i)), tagWhite);
10956
- await delay(600);
10957
- up(BANNER_LINE_COUNT);
10958
- printFrame(ART_LINES.map((l, i) => neonLine(l, i)), tagWithCursor);
10959
- }
10960
- await delay(600);
10961
- up(BANNER_LINE_COUNT);
10962
- printFrame(ART_LINES.map((l, i) => neonLine(l, i)), ` ${import_picocolors.default.bold(import_picocolors.default.magenta(TAGLINE))}\x1B[K`);
11149
+ outb("\x1B[?25l");
11150
+ await playBannerIntro(rainbowArt, whiteArt);
11151
+ await decodeFirstSentence(rainbowArt, SENTENCES[0]);
11152
+ await new Promise((r) => setTimeout(r, SENTENCE_HOLD_MS));
11153
+ await wipeSwapTransition(rainbowArt, SENTENCES[0], SENTENCES[1]);
11154
+ await new Promise((r) => setTimeout(r, SENTENCE_HOLD_MS));
11155
+ await wipeSwapTransition(rainbowArt, SENTENCES[1], SENTENCES[2]);
11156
+ await new Promise((r) => setTimeout(r, SENTENCE_HOLD_MS));
11157
+ await lockShimmer(rainbowArt, SENTENCES[2]);
10963
11158
  } finally {
10964
11159
  outb("\x1B[?25h");
10965
11160
  }
@@ -11072,7 +11267,12 @@ async function runDoctor(opts = {}) {
11072
11267
  const totalChecks = results.length;
11073
11268
  const errorCount = results.filter((r2) => r2.status === "error").length;
11074
11269
  const warningCount = results.filter((r2) => r2.status === "warn").length;
11075
- const fixableCount = results.filter((r2) => r2.status !== "ok" && getFix(r2) !== null).length;
11270
+ const fixableCount = results.filter((r2) => {
11271
+ if (r2.status === "ok")
11272
+ return false;
11273
+ const fix = getFix(r2);
11274
+ return fix !== null && !fix.advisory;
11275
+ }).length;
11076
11276
  const showFixHint = !opts.fix && fixableCount > 0;
11077
11277
  const summaryParts = [`${totalChecks} checks`];
11078
11278
  if (errorCount > 0)
@@ -11187,6 +11387,7 @@ function getFix(r2) {
11187
11387
  const pendingMatch = r2.message.match(/(\d+) unembedded/);
11188
11388
  const count = pendingMatch?.[1] ?? "some";
11189
11389
  return {
11390
+ advisory: true,
11190
11391
  fn: async (vaultDir) => {
11191
11392
  const { join: join5 } = await import("path");
11192
11393
  const { parse: parseYaml } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
@@ -11232,14 +11433,14 @@ async function applyFixes(vaultDir, results, isTTY, registerHooksFn) {
11232
11433
  const fixable = results.filter((r2) => r2.status !== "ok" && getFix(r2) !== null);
11233
11434
  if (fixable.length === 0) {
11234
11435
  if (isTTY)
11235
- barLine(`${import_picocolors5.default.green("\u25C6")} Nothing to fix`);
11436
+ writeLine(`${import_picocolors5.default.green("\u25C6")} Nothing to fix`);
11236
11437
  else
11237
11438
  writeLine("nothing to fix");
11238
11439
  return;
11239
11440
  }
11240
11441
  if (isTTY) {
11241
- barBlank();
11242
- barLine(import_picocolors5.default.bold(`${fixable.length} fix(es) to apply:`));
11442
+ writeLine("");
11443
+ barOpen(import_picocolors5.default.bold(`${fixable.length} fix(es) to apply:`));
11243
11444
  barBlank();
11244
11445
  for (const r2 of fixable) {
11245
11446
  barLine(` ${import_picocolors5.default.cyan("\u25C6")} ${getFix(r2).description}`);
@@ -11749,7 +11950,7 @@ async function runInit(opts = {}) {
11749
11950
  }
11750
11951
  if (sp6) {
11751
11952
  await randDelay();
11752
- sp6.stop(hooksOk ? undefined : "not registered \u2014 run onebrain update", hooksOk ? ["Stop \u2713 PostCompact \u2713", "Bash(onebrain *) \u2713"] : undefined);
11953
+ sp6.stop(hooksOk ? undefined : "not registered \u2014 run onebrain update", hooksOk ? ["Stop \u2713", "Bash(onebrain *) \u2713"] : undefined);
11753
11954
  } else {
11754
11955
  writeLine(`hooks: ${hooksOk ? "ok" : "warning \u2014 hooks not registered; run onebrain update"}`);
11755
11956
  }
@@ -11776,24 +11977,24 @@ async function initCommand(opts = {}) {
11776
11977
  }
11777
11978
 
11778
11979
  // src/commands/internal/checkpoint.ts
11779
- import { readFileSync, readdirSync, writeFileSync } from "fs";
11980
+ import { readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "fs";
11780
11981
  import { tmpdir as osTmpdir } from "os";
11781
11982
  import { join as join7 } from "path";
11782
11983
  var SKIP_WINDOW = 60;
11783
11984
  var MIN_ACTIVITY = 2;
11784
- var PRECOMPACT_RECENCY = 300;
11785
11985
  var DEFAULT_MESSAGES_THRESHOLD = 15;
11786
11986
  var DEFAULT_MINUTES_THRESHOLD = 30;
11787
11987
  function stateFilePath(token, tmpDir) {
11788
11988
  return join7(tmpDir, `onebrain-${token}.state`);
11789
11989
  }
11990
+ var FRESH_STATE_DISK = "0:0:00";
11790
11991
  function readState(token, tmpDir = osTmpdir()) {
11791
11992
  const path = stateFilePath(token, tmpDir);
11792
11993
  try {
11793
11994
  const raw = readFileSync(path, "utf8").trim();
11794
11995
  const parts = raw.split(":");
11795
- if (parts.length < 3) {
11796
- throw new Error("v1 state format");
11996
+ if (parts.length !== 3) {
11997
+ throw new Error("state file must be exactly 3 fields");
11797
11998
  }
11798
11999
  const count = Number(parts[0]);
11799
12000
  const last_ts = Number(parts[1]);
@@ -11804,7 +12005,7 @@ function readState(token, tmpDir = osTmpdir()) {
11804
12005
  return { count, last_ts, last_stop_nn };
11805
12006
  } catch {
11806
12007
  try {
11807
- writeFileSync(stateFilePath(token, tmpDir), "0:0:00", "utf8");
12008
+ writeFileSync(stateFilePath(token, tmpDir), FRESH_STATE_DISK, "utf8");
11808
12009
  } catch (writeErr) {
11809
12010
  process.stderr.write(`checkpoint: failed to rewrite state file for token ${token}: ${writeErr}
11810
12011
  `);
@@ -11818,12 +12019,17 @@ function readState(token, tmpDir = osTmpdir()) {
11818
12019
  }
11819
12020
  function writeState(token, state, tmpDir = osTmpdir()) {
11820
12021
  const path = stateFilePath(token, tmpDir);
12022
+ const tmpPath = `${path}.tmp.${process.pid}`;
11821
12023
  const content = `${state.count}:${state.last_ts}:${state.last_stop_nn}`;
11822
12024
  try {
11823
- writeFileSync(path, content, "utf8");
12025
+ writeFileSync(tmpPath, content, "utf8");
12026
+ renameSync(tmpPath, path);
11824
12027
  } catch (err) {
11825
12028
  process.stderr.write(`checkpoint: failed to write state file ${path}: ${err}
11826
12029
  `);
12030
+ try {
12031
+ unlinkSync(tmpPath);
12032
+ } catch {}
11827
12033
  }
11828
12034
  }
11829
12035
  var DEFAULT_LOGS_FOLDER = "07-logs";
@@ -11903,11 +12109,11 @@ function handleStop(token, vaultRoot, now = Math.floor(Date.now() / 1000), tmpDi
11903
12109
  const elapsed = state.last_ts === 0 ? 0 : now - state.last_ts;
11904
12110
  const thresholdMet = state.count >= messagesThreshold || elapsed >= timeThreshold;
11905
12111
  if (!thresholdMet) {
11906
- writeState(token, { count: state.count, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn }, tmpDir);
12112
+ writeState(token, { ...state }, tmpDir);
11907
12113
  return;
11908
12114
  }
11909
12115
  if (state.count < MIN_ACTIVITY) {
11910
- writeState(token, { count: state.count, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn }, tmpDir);
12116
+ writeState(token, { ...state }, tmpDir);
11911
12117
  return;
11912
12118
  }
11913
12119
  const date = formatDate(now);
@@ -11917,27 +12123,12 @@ function handleStop(token, vaultRoot, now = Math.floor(Date.now() / 1000), tmpDi
11917
12123
  emitBlock(`${nextNn}${since}`);
11918
12124
  writeState(token, { count: 0, last_ts: now, last_stop_nn: nextNn }, tmpDir);
11919
12125
  }
11920
- function handlePostcompact(token, _vaultRoot, now = Math.floor(Date.now() / 1000), tmpDir = osTmpdir()) {
11921
- const state = readState(token, tmpDir);
11922
- if (state.last_ts > 0 && now - state.last_ts < PRECOMPACT_RECENCY) {
11923
- writeState(token, { count: 0, last_ts: state.last_ts, last_stop_nn: state.last_stop_nn }, tmpDir);
11924
- return;
11925
- }
11926
- emitBlock("auto-wrapup");
11927
- writeState(token, { count: 0, last_ts: now, last_stop_nn: state.last_stop_nn }, tmpDir);
11928
- }
11929
- function postcompactFallback(token, vaultRoot, now = Math.floor(Date.now() / 1000), tmpDir = osTmpdir()) {
11930
- handlePostcompact(token, vaultRoot, now, tmpDir);
11931
- }
11932
12126
  async function checkpointCommand(mode, token, vaultRoot) {
11933
12127
  try {
11934
12128
  switch (mode) {
11935
12129
  case "stop":
11936
12130
  handleStop(token, vaultRoot);
11937
12131
  break;
11938
- case "postcompact":
11939
- postcompactFallback(token, vaultRoot);
11940
- break;
11941
12132
  case "reset":
11942
12133
  handleReset(token);
11943
12134
  break;
@@ -12142,12 +12333,6 @@ async function scanMonthDir(monthDir, currentToken, today, seenTokens) {
12142
12333
  continue;
12143
12334
  if (seenTokens.has(ftoken))
12144
12335
  continue;
12145
- try {
12146
- const content = await readFile5(join9(monthDir, fname), "utf8");
12147
- const fm = parseFrontmatter(content);
12148
- if (fm && (fm["merged"] === true || fm["merged"] === "true"))
12149
- continue;
12150
- } catch {}
12151
12336
  if (await hasManualSessionLog(monthDir, fdate))
12152
12337
  continue;
12153
12338
  seenTokens.add(ftoken);
@@ -12608,8 +12793,8 @@ function patchUtf8(stream) {
12608
12793
  }
12609
12794
 
12610
12795
  // src/index.ts
12611
- var VERSION = "2.1.4";
12612
- var RELEASE_DATE = "2026-04-28";
12796
+ var VERSION = "2.1.6";
12797
+ var RELEASE_DATE = "2026-04-30";
12613
12798
  patchUtf8(process.stdout);
12614
12799
  patchUtf8(process.stderr);
12615
12800
  var VERSION_STRING = `OneBrain v${VERSION} \u2014 released ${RELEASE_DATE}`;
@@ -12661,9 +12846,9 @@ program2.command("session-init", { hidden: true }).description("Emit session tok
12661
12846
  program2.command("orphan-scan", { hidden: true }).description("Scan for orphaned checkpoint files in logs folder").argument("<logs_folder>", "path to logs folder").argument("<session_token>", "current session token to exclude").action(async (logsFolder, sessionToken) => {
12662
12847
  await orphanScanCommand(logsFolder, sessionToken);
12663
12848
  });
12664
- program2.command("checkpoint", { hidden: true }).description("Handle checkpoint lifecycle (stop/postcompact/reset)").argument("<mode>", "stop | postcompact | reset").option("--vault-dir <path>", "vault root directory (default: auto-detect from cwd)").action(async (mode, opts) => {
12849
+ program2.command("checkpoint", { hidden: true }).description("Handle checkpoint lifecycle (stop/reset)").argument("<mode>", "stop | reset").option("--vault-dir <path>", "vault root directory (default: auto-detect from cwd)").action(async (mode, opts) => {
12665
12850
  const token = await resolveSessionToken();
12666
- const vaultRoot = opts.vaultDir ?? findVaultRoot(process.cwd());
12851
+ const vaultRoot = mode === "stop" ? opts.vaultDir ?? findVaultRoot(process.cwd()) : "";
12667
12852
  await checkpointCommand(mode, token, vaultRoot);
12668
12853
  });
12669
12854
  program2.command("qmd-reindex", { hidden: true }).description("Trigger qmd index rebuild").action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onebrain-ai/cli",
3
- "version": "2.1.4",
3
+ "version": "2.1.6",
4
4
  "description": "CLI for OneBrain — personal AI OS for Obsidian with persistent memory, 24+ skills, and Claude Code integration",
5
5
  "keywords": [
6
6
  "onebrain",