@onebrain-ai/cli 2.1.11 → 2.1.13

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 (3) hide show
  1. package/README.md +6 -6
  2. package/dist/onebrain +119 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -111,20 +111,20 @@ The agent reaches outward FROM the vault to every surface where the work actuall
111
111
 
112
112
  ---
113
113
 
114
- ## The Path to Co-Evolution
114
+ ## Every Session Sharpens Both
115
115
 
116
- A tightening 3-step loop that sharpens with every cycle.
116
+ OneBrain runs as a tight 3-step loop. Each cycle, both sides sharpen.
117
117
 
118
118
  <p align="center">
119
119
  <picture>
120
120
  <source media="(prefers-color-scheme: dark)" srcset="assets/diagrams/coevo-loop-dark.svg">
121
- <img alt="Co-Evolution loop — three nodes (01 INITIATE at top, 02 CAPTURE_INTENT at bottom-right, 03 MUTUAL_EVOLUTION at bottom-left) connected by curved arrows flowing clockwise" src="assets/diagrams/coevo-loop-light.svg" width="540">
121
+ <img alt="Co-Evolution loop — three nodes (01 CAPTURE at top, 02 EVOLVE at bottom-right, 03 WRAPUP at bottom-left) connected by curved arrows flowing clockwise" src="assets/diagrams/coevo-loop-light.svg" width="540">
122
122
  </picture>
123
123
  </p>
124
124
 
125
- 1. **Initiate** — Install the CLI, run `/onboarding`. The agent learns your name, vault, and identity. → `npm install -g @onebrain-ai/cli`
126
- 2. **Capture intent** — Talk in natural language. The agent writes, classifies, and links in real time. → `/braindump` · `/capture` · `/bookmark`
127
- 3. **Mutual evolution** — `/research` and `/distill` expand your knowledge. `/learn` deepens the agent. The loop tightens. → `/research` · `/distill` · `/learn`
125
+ 1. **Capture** — Talk to the agent in natural language. It writes, classifies, and links your thoughts in real time. → `/braindump` · `/capture` · `/bookmark`
126
+ 2. **Evolve** — `/research` and `/distill` expand your knowledge. `/learn` deepens the agent. The loop tightens. → `/research` · `/distill` · `/learn`
127
+ 3. **Wrapup** — `/wrapup` consolidates the session log. `/recap` promotes lessons to memory. → `/wrapup` · `/recap`
128
128
 
129
129
  ### Behind the loop
130
130
 
package/dist/onebrain CHANGED
@@ -9521,7 +9521,7 @@ var init_lib = __esm(() => {
9521
9521
  var require_package = __commonJS((exports, module) => {
9522
9522
  module.exports = {
9523
9523
  name: "@onebrain-ai/cli",
9524
- version: "2.1.11",
9524
+ version: "2.1.13",
9525
9525
  description: "CLI for OneBrain \u2014 personal AI OS for Obsidian with persistent memory, 24+ skills, and Claude Code integration",
9526
9526
  keywords: [
9527
9527
  "onebrain",
@@ -10280,7 +10280,11 @@ __export(exports_vault_sync, {
10280
10280
  });
10281
10281
  import { mkdir as mkdir2, mkdtemp, readFile as readFile2, readdir, rm, stat as stat3, unlink as unlink2, writeFile as writeFile3 } from "fs/promises";
10282
10282
  import { homedir as homedir2, tmpdir } from "os";
10283
- import { dirname as dirname2, join as join5, relative } from "path";
10283
+ import { dirname as dirname2, join as join5, sep as pathSep, relative, resolve as resolvePath } from "path";
10284
+ function normalizePath(p2) {
10285
+ const resolved = resolvePath(p2);
10286
+ return resolved.endsWith(pathSep) && resolved.length > pathSep.length ? resolved.slice(0, -pathSep.length) : resolved;
10287
+ }
10284
10288
  function resolveBranch(updateChannel) {
10285
10289
  return updateChannel === "stable" ? "main" : "next";
10286
10290
  }
@@ -10484,9 +10488,12 @@ async function pinToVault(vaultRoot, installedPluginsPath, installedPluginsCache
10484
10488
  return { skipped: true };
10485
10489
  }
10486
10490
  const vaultPluginDir = join5(vaultRoot, ".claude", "plugins", "onebrain");
10491
+ const normalizedVaultRoot = normalizePath(vaultRoot);
10492
+ const normalizedVaultPluginDir = normalizePath(vaultPluginDir);
10487
10493
  const { version: pluginVersion, lastUpdated: pluginLastUpdated } = await readPluginMetadata(vaultRoot);
10488
10494
  const updatedAt = pluginLastUpdated ?? now().toISOString();
10489
10495
  const cacheDir = installedPluginsCacheDir ?? join5(dirname2(installedPluginsPath), "cache");
10496
+ const normalizedCacheDir = normalizePath(cacheDir);
10490
10497
  const hasMarketplace = onebrainKeys.some((k2) => {
10491
10498
  const entries = plugins[k2];
10492
10499
  return entries.some((e2) => e2["source"] === "marketplace");
@@ -10525,22 +10532,28 @@ async function pinToVault(vaultRoot, installedPluginsPath, installedPluginsCache
10525
10532
  for (const key of onebrainKeys) {
10526
10533
  const entries = plugins[key];
10527
10534
  for (const entry of entries) {
10528
- const installPath = entry["installPath"];
10529
- if (typeof installPath !== "string") {
10535
+ const installPathRaw = entry["installPath"];
10536
+ const projectPathRaw = entry["projectPath"];
10537
+ const installPathBad = installPathRaw !== undefined && typeof installPathRaw !== "string";
10538
+ const projectPathBad = projectPathRaw !== undefined && typeof projectPathRaw !== "string";
10539
+ if (installPathBad || projectPathBad) {
10540
+ process.stderr.write(`vault-sync: pin warning: malformed entry \u2014 non-string installPath/projectPath in installed_plugins.json[${key}]
10541
+ `);
10530
10542
  continue;
10531
10543
  }
10532
- let inCache = false;
10533
- try {
10534
- inCache = installPath.startsWith(`${cacheDir}/`) || installPath === cacheDir;
10535
- } catch {
10536
- inCache = false;
10544
+ const installPath = typeof installPathRaw === "string" ? installPathRaw : undefined;
10545
+ const projectPath = typeof projectPathRaw === "string" ? projectPathRaw : undefined;
10546
+ const normalizedInstallPath = installPath !== undefined ? normalizePath(installPath) : undefined;
10547
+ const normalizedProjectPath = projectPath !== undefined ? normalizePath(projectPath) : undefined;
10548
+ const inCache = normalizedInstallPath !== undefined && (normalizedInstallPath === normalizedCacheDir || normalizedInstallPath.startsWith(`${normalizedCacheDir}${pathSep}`));
10549
+ const isThisVault = normalizedInstallPath === normalizedVaultPluginDir;
10550
+ const isThisProject = normalizedProjectPath === normalizedVaultRoot;
10551
+ if (!isThisVault && !inCache && !isThisProject) {
10552
+ continue;
10537
10553
  }
10538
- const isThisVault = installPath === vaultPluginDir;
10539
- if (inCache) {
10554
+ if (normalizedInstallPath !== normalizedVaultPluginDir) {
10540
10555
  entry["installPath"] = vaultPluginDir;
10541
10556
  changed = true;
10542
- } else if (!isThisVault) {
10543
- continue;
10544
10557
  }
10545
10558
  if (entry["version"] !== pluginVersion) {
10546
10559
  entry["version"] = pluginVersion;
@@ -10845,7 +10858,7 @@ var import_picocolors5 = __toESM(require_picocolors(), 1);
10845
10858
  var import_picocolors = __toESM(require_picocolors(), 1);
10846
10859
  function resolveBinaryVersion() {
10847
10860
  if (true)
10848
- return "2.1.11";
10861
+ return "2.1.13";
10849
10862
  try {
10850
10863
  const pkg = require_package();
10851
10864
  return pkg.version ?? "dev";
@@ -12619,7 +12632,74 @@ function formatDatetime(date) {
12619
12632
  const mm = String(date.getMinutes()).padStart(2, "0");
12620
12633
  return `${dow} \xB7 ${day} ${mon} ${year} \xB7 ${hh}:${mm}`;
12621
12634
  }
12622
- async function resolveSessionToken(tmpDir = osTmpdir2()) {
12635
+ function commBasenameOf(raw) {
12636
+ return raw.trim().replace(/^.*[/\\]/, "").replace(/\.exe$/i, "");
12637
+ }
12638
+ var defaultProcLookup = (pid) => {
12639
+ if (process.platform === "win32")
12640
+ return null;
12641
+ let result;
12642
+ try {
12643
+ result = Bun.spawnSync(["ps", "-o", "ppid=,comm=", "-p", String(pid)], {
12644
+ stdout: "pipe",
12645
+ stderr: "pipe"
12646
+ });
12647
+ } catch (err) {
12648
+ const code = err?.code;
12649
+ process.stderr.write(`onebrain: ps spawn failed for pid ${pid} (${code ?? "unknown"})
12650
+ `);
12651
+ return null;
12652
+ }
12653
+ if (!result.success) {
12654
+ process.stderr.write(`onebrain: ps -p ${pid} exited ${result.exitCode}
12655
+ `);
12656
+ return null;
12657
+ }
12658
+ const out2 = new TextDecoder().decode(result.stdout).trim();
12659
+ if (!out2) {
12660
+ process.stderr.write(`onebrain: ps -p ${pid} returned empty output
12661
+ `);
12662
+ return null;
12663
+ }
12664
+ if (out2.includes(`
12665
+ `)) {
12666
+ process.stderr.write(`onebrain: ps -p ${pid} returned multi-line output: ${out2.replace(/\n/g, " | ").slice(0, 120)}
12667
+ `);
12668
+ return null;
12669
+ }
12670
+ const match = out2.match(/^\s*(\d+)\s+(.+)$/);
12671
+ if (!match) {
12672
+ process.stderr.write(`onebrain: ps -p ${pid} unparseable: ${out2.slice(0, 120)}
12673
+ `);
12674
+ return null;
12675
+ }
12676
+ const ppid = Number(match[1]);
12677
+ if (Number.isNaN(ppid))
12678
+ return null;
12679
+ return { ppid, commBasename: commBasenameOf(match[2] ?? "") };
12680
+ };
12681
+ function findClaudeAncestorPid(startPid, lookup = defaultProcLookup, maxDepth = 12) {
12682
+ let current = startPid;
12683
+ for (let i = 0;i < maxDepth; i++) {
12684
+ if (current <= 1)
12685
+ return null;
12686
+ const info = lookup(current);
12687
+ if (!info)
12688
+ return null;
12689
+ if (info.commBasename === "claude")
12690
+ return current;
12691
+ if (info.ppid <= 1) {
12692
+ process.stderr.write(`onebrain: walk-up reached init from pid ${startPid} without finding claude (last comm=${info.commBasename})
12693
+ `);
12694
+ return null;
12695
+ }
12696
+ current = info.ppid;
12697
+ }
12698
+ process.stderr.write(`onebrain: walk-up exhausted ${maxDepth} hops from pid ${startPid} without finding claude
12699
+ `);
12700
+ return null;
12701
+ }
12702
+ async function resolveSessionToken(tmpDir = osTmpdir2(), procLookup = defaultProcLookup) {
12623
12703
  const wtSession = process.env["WT_SESSION"];
12624
12704
  if (wtSession) {
12625
12705
  const stripped = wtSession.replace(/[^a-zA-Z0-9]/g, "").slice(0, 8);
@@ -12638,6 +12718,13 @@ async function resolveSessionToken(tmpDir = osTmpdir2()) {
12638
12718
  if (stripped.length > 0)
12639
12719
  return stripped;
12640
12720
  }
12721
+ const startPpid = process.ppid;
12722
+ if (startPpid !== undefined && startPpid > 1) {
12723
+ const claudePid = findClaudeAncestorPid(startPpid, procLookup);
12724
+ if (claudePid !== null && claudePid > 1) {
12725
+ return String(claudePid);
12726
+ }
12727
+ }
12641
12728
  const today = new Date;
12642
12729
  const yyyymmdd = [
12643
12730
  today.getFullYear(),
@@ -12706,9 +12793,21 @@ async function cleanStaleStateFile(token, tmpDir) {
12706
12793
  if (mtimeMs < processStartMs) {
12707
12794
  try {
12708
12795
  await unlink3(stateFile);
12709
- } catch {}
12796
+ } catch (err) {
12797
+ const code = err?.code;
12798
+ if (code !== "ENOENT") {
12799
+ process.stderr.write(`onebrain: cannot remove stale state file ${stateFile} (${code ?? "unknown"})
12800
+ `);
12801
+ }
12802
+ }
12710
12803
  }
12711
- } catch {}
12804
+ } catch (err) {
12805
+ const code = err?.code;
12806
+ if (code && code !== "ENOENT") {
12807
+ process.stderr.write(`onebrain: cleanStaleStateFile failed (${code})
12808
+ `);
12809
+ }
12810
+ }
12712
12811
  }
12713
12812
  async function queryQmdUnembedded() {
12714
12813
  try {
@@ -12739,13 +12838,13 @@ async function queryQmdUnembedded() {
12739
12838
  return 0;
12740
12839
  }
12741
12840
  }
12742
- async function runSessionInit(vaultRoot, tmpDir = osTmpdir2()) {
12841
+ async function runSessionInit(vaultRoot, tmpDir = osTmpdir2(), procLookup = defaultProcLookup) {
12743
12842
  try {
12744
12843
  await loadVaultConfig(vaultRoot);
12745
12844
  } catch {
12746
12845
  return { decision: "block", reason: "onebrain-init-required" };
12747
12846
  }
12748
- const sessionToken = await resolveSessionToken(tmpDir);
12847
+ const sessionToken = await resolveSessionToken(tmpDir, procLookup);
12749
12848
  await cleanStaleStateFile(sessionToken, tmpDir);
12750
12849
  const datetime = formatDatetime(new Date);
12751
12850
  const qmdUnembedded = await queryQmdUnembedded();
@@ -12989,7 +13088,7 @@ function patchUtf8(stream) {
12989
13088
  }
12990
13089
 
12991
13090
  // src/index.ts
12992
- var VERSION = "2.1.11";
13091
+ var VERSION = "2.1.13";
12993
13092
  var RELEASE_DATE = "2026-05-06";
12994
13093
  patchUtf8(process.stdout);
12995
13094
  patchUtf8(process.stderr);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onebrain-ai/cli",
3
- "version": "2.1.11",
3
+ "version": "2.1.13",
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",