@onebrain-ai/cli 2.2.3 → 2.2.4

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 +36 -18
  2. package/dist/onebrain +67 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,7 +23,7 @@
23
23
  </p>
24
24
 
25
25
  <p align="center">
26
- <strong>Your personal AI OS</strong> — persistent memory, 24+ skills, and a full local stack<br>
26
+ <strong>Your personal AI OS</strong> — persistent memory, 25+ skills, and a full local stack<br>
27
27
  (Claude Code + Obsidian + tmux + Telegram), entirely on your own machine.
28
28
  </p>
29
29
 
@@ -35,7 +35,7 @@
35
35
 
36
36
  ## What is OneBrain?
37
37
 
38
- OneBrain is an AI operating system layer built on top of Obsidian. It gives your AI agent persistent memory, a structured knowledge vault, and 24+ pre-built skills — so every session picks up exactly where the last one left off.
38
+ OneBrain is an AI operating system layer built on top of Obsidian. It gives your AI agent persistent memory, a structured knowledge vault, and 25+ pre-built skills — so every session picks up exactly where the last one left off.
39
39
 
40
40
  Unlike chat-based AI tools, OneBrain lives in plain Markdown files you own forever. No cloud sync required. No proprietary format. Just your agent, your vault, your data.
41
41
 
@@ -70,7 +70,7 @@ OneBrain doesn't compete with Claude Code, Gemini CLI, or any other AI harness
70
70
 
71
71
  | # | Layer | Role | What lives here |
72
72
  |---|---|---|---|
73
- | 01 | **OneBrain** | OS layer (plugin + CLI) | 24+ skills · lifecycle hooks · vault sync · indexing · checkpoints · harness routing |
73
+ | 01 | **OneBrain** | OS layer (plugin + CLI) | 25+ skills · lifecycle hooks · vault sync · indexing · checkpoints · harness routing |
74
74
  | 02 | **Harness** | Agentic runtime | Bring your own — Claude Code · Gemini CLI · Codex · Qwen · ... |
75
75
  | 03 | **LLM** | Intelligence source | Local (mlx, ollama) · cloud (claude, gemini, gpt) · raw API |
76
76
  | 04 | **Obsidian Vault** | Source of truth | Plain Markdown — notes, memory, decisions, knowledge graph |
@@ -84,7 +84,7 @@ A great harness already knows how to talk to an LLM, edit files, and run shell c
84
84
  | | What OneBrain adds | Why it matters |
85
85
  |---|---|---|
86
86
  | 🧠 | **Memory** — Identity, preferences, decisions, project state — promoted across four tiers as it earns trust | The harness alone starts every session from zero. OneBrain doesn't. |
87
- | ⚡ | **Skills** — 24+ vault-aware verbs (`/braindump`, `/research`, `/distill`, `/learn`, `/wrapup`, …) | Pre-built workflows the harness would otherwise need you to script every time. |
87
+ | ⚡ | **Skills** — 25+ vault-aware verbs (`/braindump`, `/research`, `/distill`, `/learn`, `/wrapup`, …) | Pre-built workflows the harness would otherwise need you to script every time. |
88
88
  | 🎯 | **Calibration** — Every correction, every preference, every learned habit tunes the agent to *you* | The longer you use it, the sharper it gets — your vault is the training data. |
89
89
  | 🔀 | **Continuity** — Context lives in the vault, not the harness | Switch from Claude Code to Gemini CLI to Codex. Same memory. Same skills. Same agent. |
90
90
 
@@ -210,7 +210,7 @@ OneBrain doesn't just store markdown. Every feature exists to make you and the a
210
210
  |---|---|---|
211
211
  | 🧠 | **Persistent Memory** | Remembers your name, goals, preferences, and decisions across every session |
212
212
  | 🖥️ | **Personal AI OS** | Full local stack: Claude Code + Obsidian + tmux + Telegram — no cloud infra needed |
213
- | ⚡ | **24+ Skills** | Braindump, research, consolidate, bookmark, import files, daily briefing, and more |
213
+ | ⚡ | **25+ Skills** | Braindump, research, consolidate, bookmark, import files, daily briefing, and more |
214
214
  | 📂 | **Vault-native Markdown** | Plain Markdown, no lock-in. Your data stays yours forever |
215
215
  | 🔀 | **Multi-Harness OS** | Switch between Claude Code, Gemini CLI, Codex, Qwen, or BYO LLM — context never breaks. [See architecture ↑](#the-harness-os-architecture) |
216
216
  | 🔌 | **Zero Config** | Clone, open in Obsidian, run `/onboarding`. Ready in under 2 minutes |
@@ -340,37 +340,55 @@ Same vault. Same skills. Same memory. The LLM swaps; OneBrain doesn't notice.
340
340
 
341
341
  <a id="commands"></a>
342
342
 
343
- ## 📋 24+ Commands
343
+ ## 📋 25+ Commands
344
344
 
345
- The full skill surface, alphabetized by workflow. **Gemini CLI users:** prepend the `onebrain:` namespace, e.g. `/onebrain:braindump` instead of `/braindump` (avoids collisions with Gemini built-in commands like `/help` and `/tasks`).
345
+ Skills are organized by workflow phase. **Gemini CLI users:** prepend the `onebrain:` namespace, e.g. `/onebrain:braindump` instead of `/braindump` (avoids collisions with Gemini built-in commands like `/help` and `/tasks`).
346
+
347
+ ### 📥 INPUT — Capture & ingest
346
348
 
347
349
  | Command | What it does |
348
350
  |---------|-------------|
349
- | `/onboarding` | First-run setup — run this first |
351
+ | `/onboarding` | First-run setup — run this first · *first run only* |
350
352
  | `/braindump` | Dump everything on your mind — it gets classified and filed |
351
353
  | `/capture` | Quick note with auto-linking to related notes |
352
354
  | `/bookmark [url]` | Save a URL with AI-generated name, description, and category to Bookmarks.md |
353
- | `/consolidate` | Process inbox into permanent knowledge |
354
- | `/connect` | Find connections between notes, suggest wikilinks |
355
- | `/research [topic]` | Web research → structured note in your vault |
356
355
  | `/summarize [url]` | Fetch a URL and save a deep summary note |
357
356
  | `/import [path]` | Import local files (PDF, Word, images, scripts) into vault notes |
358
357
  | `/reading-notes` | Turn a book or article into structured notes |
358
+ | `/research [topic]` | Web research → structured note in your vault |
359
+
360
+ ### ⚙️ PROCESS — Synthesize & organize
361
+
362
+ | Command | What it does |
363
+ |---------|-------------|
364
+ | `/consolidate` | Process inbox into permanent knowledge |
365
+ | `/distill [topic]` | Crystallize a completed topic thread into a permanent knowledge note in `03-knowledge/` |
366
+ | `/connect` | Find connections between notes, suggest wikilinks |
367
+ | `/recap` | Cross-session synthesis — batch-promote recurring insights from session logs into `memory/` files (does NOT write to MEMORY.md) |
359
368
  | `/weekly` | Review the week, surface patterns, set intentions |
360
369
  | `/daily` | Daily briefing — surfaces tasks and last session context, then saves your focus as a daily note |
361
- | `/recap` | Cross-session synthesis batch-promote recurring insights from session logs into `memory/` files (does NOT write to MEMORY.md) |
362
- | `/distill [topic]` | Crystallize a completed topic thread into a permanent knowledge note in `03-knowledge/` |
370
+ | `/learn` | Teach the agent something facts about your world or behavioral preferences |
371
+
372
+ ### 🔍 RECALL — Retrieve & navigate
373
+
374
+ | Command | What it does |
375
+ |---------|-------------|
376
+ | `/search` | General vault retrieval — answers what + why questions across MEMORY, sessions, plans, decisions logs, notes |
363
377
  | `/tasks` | Live task dashboard in Obsidian — creates/updates `TASKS.md` with always-current query sections |
364
378
  | `/moc` | Vault portal in Obsidian — creates/updates `MOC.md` with projects, areas, knowledge, tasks, and pinned links |
365
- | `/wrapup` | Wrap up session — merges any auto-checkpoints and saves full summary to session log |
366
- | `/learn` | Teach the agent something — facts about your world or behavioral preferences |
367
379
  | `/memory-review` | Interactive review of memory files — keep, update, deprecate, or delete entries |
368
- | `/clone` | Package your agent context for transfer to a new vault |
380
+
381
+ ### 🔧 MAINTAIN — System housekeeping
382
+
383
+ | Command | What it does |
384
+ |---------|-------------|
385
+ | `/update` | Update skills, config, and plugins from GitHub |
386
+ | `/doctor` | Vault + config health check — broken links, orphan notes, stale memory entries, inbox backlog |
369
387
  | `/reorganize` | Migrate flat notes into organized subfolders |
388
+ | `/clone` | Package your agent context for transfer to a new vault |
370
389
  | `/qmd` | Set up fast vault search index — enables semantic search across all notes |
371
- | `/doctor` | Vault + config health check — broken links, orphan notes, stale memory entries, inbox backlog |
372
- | `/update` | Update skills, config, and plugins from GitHub |
373
390
  | `/help` | List all available commands with descriptions |
391
+ | `/wrapup` | Wrap up session — merges any auto-checkpoints and saves full summary to session log |
374
392
 
375
393
  <details>
376
394
  <summary><strong>📁 Vault Structure</strong></summary>
package/dist/onebrain CHANGED
@@ -9560,7 +9560,7 @@ var init_lib = __esm(() => {
9560
9560
  var require_package = __commonJS((exports, module) => {
9561
9561
  module.exports = {
9562
9562
  name: "@onebrain-ai/cli",
9563
- version: "2.2.3",
9563
+ version: "2.2.4",
9564
9564
  description: "CLI for OneBrain \u2014 personal AI OS for Obsidian with persistent memory, 24+ skills, and Claude Code integration",
9565
9565
  keywords: [
9566
9566
  "onebrain",
@@ -10060,13 +10060,34 @@ async function writeSettings(settingsPath, settings) {
10060
10060
  await writeFile2(tmpPath, JSON.stringify(settings, null, 4), "utf8");
10061
10061
  await rename2(tmpPath, settingsPath);
10062
10062
  }
10063
- function checkHookPresence(groups, targetCmd) {
10063
+ function matchesSpec(entry, spec) {
10064
+ const fullCmd = [spec.command, ...spec.args].join(" ");
10065
+ if (entry.command === spec.command && JSON.stringify(entry.args ?? []) === JSON.stringify(spec.args)) {
10066
+ return true;
10067
+ }
10068
+ if (entry.command === fullCmd && !entry.args) {
10069
+ return true;
10070
+ }
10071
+ return false;
10072
+ }
10073
+ function rewriteIfShellForm(entry, spec) {
10074
+ const fullCmd = [spec.command, ...spec.args].join(" ");
10075
+ if (entry.command === fullCmd && !entry.args) {
10076
+ entry.command = spec.command;
10077
+ entry.args = [...spec.args];
10078
+ if (!entry.type)
10079
+ entry.type = "command";
10080
+ return true;
10081
+ }
10082
+ return false;
10083
+ }
10084
+ function checkHookPresence(groups, spec) {
10064
10085
  let foundMigrate = false;
10065
10086
  for (const group of groups) {
10066
10087
  for (const entry of group.hooks ?? []) {
10067
- const cmd = entry.command ?? "";
10068
- if (cmd === targetCmd)
10088
+ if (matchesSpec(entry, spec))
10069
10089
  return "found";
10090
+ const cmd = entry.command ?? "";
10070
10091
  if (cmd.includes("checkpoint-hook.sh"))
10071
10092
  foundMigrate = true;
10072
10093
  }
@@ -10096,22 +10117,30 @@ function applyHooks(settings) {
10096
10117
  }
10097
10118
  }
10098
10119
  for (const event of HOOK_EVENTS) {
10099
- const cmd = HOOK_COMMANDS[event];
10100
- if (!cmd)
10120
+ const spec = HOOK_SPECS[event];
10121
+ if (!spec)
10101
10122
  continue;
10102
10123
  if (!hooks[event])
10103
10124
  hooks[event] = [];
10104
10125
  const groups = hooks[event];
10105
- const presence = checkHookPresence(groups, cmd);
10126
+ let rewroteShellForm = false;
10127
+ for (const group of groups) {
10128
+ for (const entry of group.hooks ?? []) {
10129
+ if (rewriteIfShellForm(entry, spec))
10130
+ rewroteShellForm = true;
10131
+ }
10132
+ }
10133
+ const presence = checkHookPresence(groups, spec);
10106
10134
  if (presence === "found") {
10107
- result[event] = "ok";
10135
+ result[event] = rewroteShellForm ? "migrated" : "ok";
10108
10136
  } else if (presence === "migrate") {
10109
10137
  for (const group of groups) {
10110
10138
  if (group.matcher === undefined)
10111
10139
  group.matcher = "";
10112
10140
  for (const entry of group.hooks ?? []) {
10113
10141
  if ((entry.command ?? "").includes("checkpoint-hook.sh")) {
10114
- entry.command = cmd;
10142
+ entry.command = spec.command;
10143
+ entry.args = [...spec.args];
10115
10144
  if (!entry.type)
10116
10145
  entry.type = "command";
10117
10146
  }
@@ -10119,12 +10148,18 @@ function applyHooks(settings) {
10119
10148
  }
10120
10149
  result[event] = "migrated";
10121
10150
  } else {
10122
- groups.push({ matcher: "", hooks: [{ type: "command", command: cmd }] });
10151
+ groups.push({
10152
+ matcher: "",
10153
+ hooks: [{ type: "command", command: spec.command, args: [...spec.args] }]
10154
+ });
10123
10155
  result[event] = "added";
10124
10156
  }
10125
10157
  }
10126
10158
  return result;
10127
10159
  }
10160
+ function isCanonicalQmdEntry(entry) {
10161
+ return matchesSpec(entry, QMD_SPEC);
10162
+ }
10128
10163
  function isLegacyQmdCmd(cmd) {
10129
10164
  return /\bqmd\s+update\b/.test(cmd);
10130
10165
  }
@@ -10137,7 +10172,8 @@ function migrateLegacyQmdEntries(groups, keepCanonical) {
10137
10172
  let groupTouched = false;
10138
10173
  for (const entry of group.hooks) {
10139
10174
  if (isLegacyQmdCmd(entry.command ?? "")) {
10140
- entry.command = QMD_CMD;
10175
+ entry.command = QMD_SPEC.command;
10176
+ entry.args = [...QMD_SPEC.args];
10141
10177
  if (!entry.type)
10142
10178
  entry.type = "command";
10143
10179
  groupTouched = true;
@@ -10151,20 +10187,26 @@ function migrateLegacyQmdEntries(groups, keepCanonical) {
10151
10187
  const before = group.hooks.length;
10152
10188
  group.hooks = group.hooks.filter((h) => {
10153
10189
  const cmd = h.command ?? "";
10154
- return !isLegacyQmdCmd(cmd) && cmd !== QMD_CMD;
10190
+ return !isLegacyQmdCmd(cmd) && !isCanonicalQmdEntry(h);
10155
10191
  });
10156
10192
  if (group.hooks.length !== before)
10157
10193
  touched = true;
10158
10194
  }
10159
10195
  }
10160
10196
  if (keepCanonical) {
10197
+ for (const group of groups) {
10198
+ for (const entry of group.hooks ?? []) {
10199
+ if (rewriteIfShellForm(entry, QMD_SPEC))
10200
+ touched = true;
10201
+ }
10202
+ }
10161
10203
  let seenCanonical = false;
10162
10204
  for (const group of groups) {
10163
10205
  if (!group.hooks)
10164
10206
  continue;
10165
10207
  const before = group.hooks.length;
10166
10208
  group.hooks = group.hooks.filter((h) => {
10167
- if (h.command !== QMD_CMD)
10209
+ if (!isCanonicalQmdEntry(h))
10168
10210
  return true;
10169
10211
  if (seenCanonical)
10170
10212
  return false;
@@ -10189,10 +10231,13 @@ function applyQmdHook(settings) {
10189
10231
  settings.hooks["PostToolUse"] = [];
10190
10232
  const groups = settings.hooks["PostToolUse"];
10191
10233
  const migrated = migrateLegacyQmdEntries(groups, true);
10192
- const already = groups.some((g) => g.hooks?.some((h) => h.command === QMD_CMD));
10234
+ const already = groups.some((g) => g.hooks?.some((h) => isCanonicalQmdEntry(h)));
10193
10235
  if (already)
10194
10236
  return migrated ? "migrated" : "ok";
10195
- groups.push({ matcher: QMD_MATCHER, hooks: [{ type: "command", command: QMD_CMD }] });
10237
+ groups.push({
10238
+ matcher: QMD_MATCHER,
10239
+ hooks: [{ type: "command", command: QMD_SPEC.command, args: [...QMD_SPEC.args] }]
10240
+ });
10196
10241
  return "added";
10197
10242
  }
10198
10243
  function applyPermissions(settings) {
@@ -10332,14 +10377,14 @@ async function registerHooksCommand(vaultDir) {
10332
10377
  process.exit(1);
10333
10378
  }
10334
10379
  }
10335
- 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";
10380
+ var import_picocolors4, HOOK_SPECS, HOOK_EVENTS, PERMISSIONS_TO_ADD, ONEBRAIN_MARKER = "# onebrain", PATH_EXPORT = 'export PATH="$HOME/.bun/bin:$HOME/.npm-global/bin:$PATH"', ALLOWED_HOOK_EVENTS2, QMD_SPEC, QMD_MATCHER = "Write|Edit";
10336
10381
  var init_register_hooks = __esm(() => {
10337
10382
  init_dist2();
10338
10383
  init_lib();
10339
10384
  init_harness();
10340
10385
  import_picocolors4 = __toESM(require_picocolors(), 1);
10341
- HOOK_COMMANDS = {
10342
- Stop: "onebrain checkpoint stop"
10386
+ HOOK_SPECS = {
10387
+ Stop: { command: "onebrain", args: ["checkpoint", "stop"] }
10343
10388
  };
10344
10389
  HOOK_EVENTS = ["Stop"];
10345
10390
  PERMISSIONS_TO_ADD = [
@@ -10359,6 +10404,7 @@ var init_register_hooks = __esm(() => {
10359
10404
  "WebSearch"
10360
10405
  ];
10361
10406
  ALLOWED_HOOK_EVENTS2 = new Set(["Stop", "PostToolUse"]);
10407
+ QMD_SPEC = { command: "onebrain", args: ["qmd-reindex"] };
10362
10408
  });
10363
10409
 
10364
10410
  // src/commands/internal/vault-sync.ts
@@ -10994,7 +11040,7 @@ var import_picocolors5 = __toESM(require_picocolors(), 1);
10994
11040
  var import_picocolors = __toESM(require_picocolors(), 1);
10995
11041
  function resolveBinaryVersion() {
10996
11042
  if (true)
10997
- return "2.2.3";
11043
+ return "2.2.4";
10998
11044
  try {
10999
11045
  const pkg = require_package();
11000
11046
  return pkg.version ?? "dev";
@@ -13292,8 +13338,8 @@ function patchUtf8(stream) {
13292
13338
  }
13293
13339
 
13294
13340
  // src/index.ts
13295
- var VERSION = "2.2.3";
13296
- var RELEASE_DATE = "2026-05-10";
13341
+ var VERSION = "2.2.4";
13342
+ var RELEASE_DATE = "2026-05-12";
13297
13343
  patchUtf8(process.stdout);
13298
13344
  patchUtf8(process.stderr);
13299
13345
  var VERSION_STRING = `OneBrain v${VERSION} \u2014 released ${RELEASE_DATE}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onebrain-ai/cli",
3
- "version": "2.2.3",
3
+ "version": "2.2.4",
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",