@onebrain-ai/cli 2.1.8 → 2.1.10

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 +216 -99
  2. package/dist/onebrain +150 -95
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,15 +1,20 @@
1
1
  <p align="center">
2
- <img src="assets/banner.png" alt="OneBrain — Your personal AI OS" width="100%" />
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="assets/header-dark.png">
4
+ <img alt="OneBrain — Your AI Thinking Partner" src="assets/header-light.png" width="640">
5
+ </picture>
3
6
  </p>
4
7
 
5
8
  <p align="center">
6
- <a href="https://github.com/onebrain-ai/onebrain/releases"><img src="https://img.shields.io/badge/dynamic/json?url=https://raw.githubusercontent.com/onebrain-ai/onebrain/main/.claude/plugins/onebrain/.claude-plugin/plugin.json&query=%24.version&label=version&style=flat-square&color=blue" alt="Version" /></a>
7
- <a href="LICENSE"><img src="https://img.shields.io/github/license/onebrain-ai/onebrain?style=flat-square" alt="License" /></a>
8
- <a href="https://github.com/onebrain-ai/onebrain/stargazers"><img src="https://img.shields.io/github/stars/onebrain-ai/onebrain?style=flat-square" alt="GitHub Stars" /></a>
9
- <a href="https://github.com/onebrain-ai/onebrain/commits/main"><img src="https://img.shields.io/github/last-commit/onebrain-ai/onebrain?style=flat-square" alt="Last Commit" /></a>
9
+ <a href="https://onebrain.run"><img alt="Website" src="https://img.shields.io/badge/onebrain.run-0a0a14?style=for-the-badge&labelColor=ff2d92"></a>
10
+ <a href="https://x.com/onebrain_run"><img alt="@onebrain_run on X" src="https://img.shields.io/badge/follow-@onebrain__run-000000?style=for-the-badge&logo=x&logoColor=white"></a>
11
+ <a href="https://github.com/onebrain-ai/onebrain/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/onebrain-ai/onebrain?style=for-the-badge&color=00f3ff&logo=github"></a>
12
+ </p>
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/@onebrain-ai/cli"><img alt="npm" src="https://img.shields.io/npm/v/@onebrain-ai/cli?style=for-the-badge&logo=npm&color=cb3837&label=%40onebrain-ai%2Fcli"></a>
15
+ <a href="PLUGIN-CHANGELOG.md"><img alt="Plugin version" src="https://img.shields.io/github/package-json/v/onebrain-ai/onebrain?filename=.claude%2Fplugins%2Fonebrain%2F.claude-plugin%2Fplugin.json&style=for-the-badge&label=plugin&color=ff2d92"></a>
16
+ <a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-7c3aed?style=for-the-badge"></a>
10
17
  </p>
11
-
12
- <h1 align="center">OneBrain</h1>
13
18
 
14
19
  <p align="center">
15
20
  <em>Your AI forgets everything when the session ends.<br>
@@ -34,70 +39,94 @@ OneBrain is an AI operating system layer built on top of Obsidian. It gives your
34
39
 
35
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.
36
41
 
37
- **Works with:** Claude Code · Gemini CLI · any agent that reads Markdown
42
+ > Most tools ask you to query an AI. OneBrain **co-evolves** with you every preference you teach sharpens the agent, every link it surfaces sharpens you.
38
43
 
39
- ---
44
+ **The bidirectional flow:**
40
45
 
41
- ## Features
46
+ - **Human → Agent** — Every preference, decision, and correction becomes persistent memory. The agent calibrates to you with every interaction.
47
+ - **Agent → Human** — Captures, classifies, links, and synthesizes the noise of your day — so your attention stays on what only you can do.
42
48
 
43
- | | Feature | Description |
44
- |---|---|---|
45
- | 🧠 | **Persistent Memory** | Remembers your name, goals, preferences, and decisions across every session |
46
- | 🖥️ | **Personal AI OS** | Full local stack: Claude Code + Obsidian + tmux + Telegram no cloud infra needed |
47
- | ⚡ | **24+ Skills** | Braindump, research, consolidate, bookmark, import files, daily briefing, and more |
48
- | 📂 | **Vault-native Markdown** | Plain Markdown, no lock-in. Your data stays yours forever |
49
- | 🤖 | **Multi-agent** | Works with Claude Code, Gemini CLI, or any agent that reads Markdown |
50
- | 🔌 | **Zero Config** | Clone, open in Obsidian, run `/onboarding`. Ready in under 2 minutes |
51
- | 📓 | **Session Logs & Checkpoints** | Every conversation saved with summaries and action items. Auto-checkpoints fire every 15 messages or 30 min so nothing is lost mid-session *(auto-checkpoint requires Claude Code)* |
52
- | 💾 | **Auto Session Summary** | When you say "bye", the agent silently saves a complete session log — no `/wrapup` needed |
53
- | 🔗 | **Knowledge Synthesis** | `/consolidate` turns inbox captures into permanent connected knowledge |
54
- | 🔬 | **Confidence-scored Memory** | Every insight carries `[conf:high/medium/low]` + `[verified:YYYY-MM-DD]` — knowledge that grows more reliable with use |
55
- | 💎 | **Knowledge Distillation** | `/distill` crystallizes a completed research thread into a permanent structured note in your knowledge base |
56
- | 🩺 | **Vault Doctor** | `/doctor` audits broken links, orphan notes, stale memory, and inbox backlog; `--fix` auto-repairs confidence scores and wikilinks |
57
- | 🎓 | **Teachable AI** | `/learn` permanently shapes how your agent thinks and responds |
58
- | 🪄 | **Smart Memory Review** | `/memory-review` lets you interactively prune, update, or archive memory entries one by one |
59
- | 🔒 | **Concurrent-session Safe** | Each session generates an isolated 6-char token — multiple parallel sessions never mix checkpoints |
60
- | 📱 | **Mobile Access** | Send instructions and receive briefings from anywhere via Telegram |
61
- | ⚙️ | **CLI Binary** | `onebrain` binary handles checkpoints, session init, doctor, vault-sync, and updates — no Bun, Python, or Node.js required |
49
+ <p align="center">
50
+ <picture>
51
+ <source media="(prefers-color-scheme: dark)" srcset="assets/diagrams/bidir-flow-dark.svg">
52
+ <img alt="Bidirectional flow Human sends preferences, decisions, and corrections to Agent; Agent returns captures, links, and synthesis. Every interaction sharpens both." src="assets/diagrams/bidir-flow-light.svg" width="640">
53
+ </picture>
54
+ </p>
55
+
56
+ **Harness-agnostic** Claude Code · Gemini CLI · OpenAI Codex · Qwen · or BYO LLM via API key. [See the architecture ↓](#the-harness-os-architecture)
62
57
 
63
58
  ---
64
59
 
65
- ## Use Cases
60
+ ## The Harness OS Architecture
66
61
 
67
- ### 🖥️ Personal AI OS
62
+ OneBrain doesn't compete with Claude Code, Gemini CLI, or any other AI harness — **it extends them**. Whichever harness you drive, OneBrain adds the persistent memory, skill surface, and personal calibration that harnesses don't ship with. Same harness; suddenly it remembers who you are, what you're working on, and how you like to work — all while your Obsidian vault stays the durable source of truth underneath.
68
63
 
69
- Run OneBrain as your personal AI operating system — a complete AI environment that runs locally with no cloud infrastructure required.
64
+ <p align="center">
65
+ <picture>
66
+ <source media="(prefers-color-scheme: dark)" srcset="assets/diagrams/harness-os-stack-dark.svg">
67
+ <img alt="OneBrain Harness OS — 4-layer architecture: OneBrain (plugin + CLI) on top, Harness, LLM, Obsidian Vault as the source of truth at the base" src="assets/diagrams/harness-os-stack-light.svg" width="780">
68
+ </picture>
69
+ </p>
70
70
 
71
- **Recommended stack:**
71
+ | # | Layer | Role | What lives here |
72
+ |---|---|---|---|
73
+ | 01 | **OneBrain** | OS layer (plugin + CLI) | 24+ skills · lifecycle hooks · vault sync · indexing · checkpoints · harness routing |
74
+ | 02 | **Harness** | Agentic runtime | Bring your own — Claude Code · Gemini CLI · Codex · Qwen · ... |
75
+ | 03 | **LLM** | Intelligence source | Local (mlx, ollama) · cloud (claude, gemini, gpt) · raw API |
76
+ | 04 | **Obsidian Vault** | Source of truth | Plain Markdown — notes, memory, decisions, knowledge graph |
72
77
 
73
- | Tool | Role |
74
- |------|------|
75
- | [Claude Code](https://claude.ai/code) | Your AI agent, running in the terminal |
76
- | [Obsidian](https://obsidian.md) | Your vault — single source of truth for memory and knowledge |
77
- | [tmux](https://github.com/tmux/tmux) | Persistent sessions that survive disconnects and reboots |
78
- | [Telegram](https://telegram.org) | Mobile access: send instructions, receive briefings from anywhere |
78
+ The **Harness** layer is where most AI tools pick a fight with each other. We don't — pick whichever harness you love. By familiarity, by task, or by cost. Your vault stays the same.
79
79
 
80
- **Setting up the full stack:**
80
+ ### Extend, don't replace
81
81
 
82
- 1. Install OneBrain and open your vault in Obsidian ([Get Started](#installation))
83
- 2. Start a tmux session: `tmux new -s onebrain`
84
- 3. Start Claude Code in your vault directory: `claude`
85
- 4. Run `/telegram:configure` to connect Claude Code's built-in Telegram channel — no custom bot or external infra needed
86
- 5. From any device, open Telegram and send instructions directly to your OneBrain agent
82
+ A great harness already knows how to talk to an LLM, edit files, and run shell commands. It does **not** know who you are, what you've decided last week, or how you prefer to work. OneBrain fills exactly that gap.
87
83
 
88
- Your agent, your vault, your data forever.
84
+ | | What OneBrain adds | Why it matters |
85
+ |---|---|---|
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. |
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
+ | 🔀 | **Continuity** — Context lives in the vault, not the harness | Switch from Claude Code to Gemini CLI to Codex. Same memory. Same skills. Same agent. |
89
90
 
90
- ### 🧠 Thinking Partner
91
+ > Pick a harness for **how it lets you work** (CLI, IDE, mobile, API). Pick OneBrain for **how it remembers you** across all of them.
91
92
 
92
- Use OneBrain as a daily thinking partner. Capture ideas with `/braindump`, research topics with `/research`, synthesize knowledge with `/consolidate`, and surface connections you'd never find manually with `/connect`.
93
+ ---
93
94
 
94
- ### 📚 Knowledge Base Builder
95
+ ## One Vault, All Projects — The Command Center
95
96
 
96
- Turn your AI into a knowledge curator: research, summarize, import files, and build a connected Markdown knowledge base that grows smarter over time.
97
+ Obsidian becomes your dispatch hub for everything you do:
98
+
99
+ - **Read once, understand all** — agent context lives in one place, never re-explained.
100
+ - **Code in repos, orchestration in vault** — agent dispatches from here to wherever the work actually lives.
101
+ - **Markdown replaces Slack / Linear / Notion** — version-controlled, AI-readable, yours forever.
102
+
103
+ <p align="center">
104
+ <picture>
105
+ <source media="(prefers-color-scheme: dark)" srcset="assets/diagrams/vault-hub-dark.svg">
106
+ <img alt="Obsidian as command center — eight spokes radiate from the vault to CLI/repo, website, cloud infra, social media, office docs, project notes, research, and MCP server" src="assets/diagrams/vault-hub-light.svg" width="640">
107
+ </picture>
108
+ </p>
109
+
110
+ The agent reaches outward FROM the vault to every surface where the work actually lives. No tab juggling. No tool sprawl.
97
111
 
98
112
  ---
99
113
 
100
- ## How It Works
114
+ ## The Path to Co-Evolution
115
+
116
+ A tightening 3-step loop that sharpens with every cycle.
117
+
118
+ <p align="center">
119
+ <picture>
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">
122
+ </picture>
123
+ </p>
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`
128
+
129
+ ### Behind the loop
101
130
 
102
131
  After `/onboarding`, your AI agent:
103
132
 
@@ -106,9 +135,18 @@ After `/onboarding`, your AI agent:
106
135
  3. **Remembers everything** — decisions, preferences, and insights accumulate over time
107
136
  4. **Suggests next actions** — based on what's in your vault, not what it can infer from scratch
108
137
 
109
- ### Memory System
138
+ ---
139
+
140
+ ## Memory System
141
+
142
+ OneBrain uses a four-tier memory system — knowledge sinks downward as it gets validated, while the agent recalls upward on demand. The Semantic tier has two loading modes (always-loaded and lazy-loaded).
110
143
 
111
- OneBrain uses a four-tier memory system — each tier is more compressed and longer-lived than the one below. The Semantic tier has two loading modes (always-loaded and lazy-loaded):
144
+ <p align="center">
145
+ <picture>
146
+ <source media="(prefers-color-scheme: dark)" srcset="assets/diagrams/memory-tiers-dark.svg">
147
+ <img alt="Memory tiers — four-stage persistence stack: WORKING (00-inbox + current session) at top, EPISODIC (07-logs), SEMANTIC (05-agent/MEMORY.md + memory/), and KNOWLEDGE (03-knowledge) at the base" src="assets/diagrams/memory-tiers-light.svg" width="780">
148
+ </picture>
149
+ </p>
112
150
 
113
151
  | Tier | Location | What it stores | Promoted by |
114
152
  |------|----------|---------------|-------------|
@@ -118,11 +156,9 @@ OneBrain uses a four-tier memory system — each tier is more compressed and lon
118
156
  | **Semantic** (lazy-loaded) | `05-agent/memory/` | Behavioral patterns, domain facts — loaded on demand via MEMORY-INDEX.md | `/learn`, `/recap`, `/memory-review` |
119
157
  | **Knowledge** | `03-knowledge/` | Permanent synthesized notes | `/distill` |
120
158
 
121
- ---
122
-
123
- ## Memory Promotion
159
+ ### Memory Promotion
124
160
 
125
- OneBrain organizes agent memory across three layers. Each layer has specific skills responsible for writing to it.
161
+ Each tier has specific skills responsible for writing to it. Knowledge moves down the stack only as fast as it earns trust.
126
162
 
127
163
  | Layer | Storage | Written by |
128
164
  |---|---|---|
@@ -143,11 +179,9 @@ session → session log (`/wrapup`) → `memory/` files (`/recap`) → `MEMORY.m
143
179
  - Only behaviors applying every session with high-impact failure if missed → MEMORY.md Critical Behaviors
144
180
  - `MEMORY-INDEX.md` is loaded every session alongside `MEMORY.md` — it is the registry that enables lazy-loading of `memory/` files; updated automatically by any skill that writes to `memory/`
145
181
 
146
- ---
182
+ ### Automatic Session Saving
147
183
 
148
- ## Automatic Session Saving
149
-
150
- OneBrain has three automatic behaviors that run without you doing anything:
184
+ OneBrain has automatic behaviors that run without you doing anything:
151
185
 
152
186
  | Behavior | Trigger | What it does |
153
187
  |----------|---------|-------------|
@@ -168,9 +202,83 @@ OneBrain has three automatic behaviors that run without you doing anything:
168
202
 
169
203
  ---
170
204
 
205
+ ## Built for Synergetic Thinking
206
+
207
+ OneBrain doesn't just store markdown. Every feature exists to make you and the agent better at each other's job.
208
+
209
+ | | Feature | Description |
210
+ |---|---|---|
211
+ | 🧠 | **Persistent Memory** | Remembers your name, goals, preferences, and decisions across every session |
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 |
214
+ | 📂 | **Vault-native Markdown** | Plain Markdown, no lock-in. Your data stays yours forever |
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
+ | 🔌 | **Zero Config** | Clone, open in Obsidian, run `/onboarding`. Ready in under 2 minutes |
217
+ | 📓 | **Session Logs & Checkpoints** | Every conversation saved with summaries and action items. Auto-checkpoints fire every 15 messages or 30 min so nothing is lost mid-session *(auto-checkpoint requires Claude Code)* |
218
+ | 💾 | **Auto Session Summary** | When you say "bye", the agent silently saves a complete session log — no `/wrapup` needed |
219
+ | 🔗 | **Knowledge Synthesis** | `/consolidate` turns inbox captures into permanent connected knowledge |
220
+ | 🔬 | **Confidence-scored Memory** | Every insight carries `[conf:high/medium/low]` + `[verified:YYYY-MM-DD]` — knowledge that grows more reliable with use |
221
+ | 💎 | **Knowledge Distillation** | `/distill` crystallizes a completed research thread into a permanent structured note in your knowledge base |
222
+ | 🩺 | **Vault Doctor** | `/doctor` audits broken links, orphan notes, stale memory, and inbox backlog; `--fix` auto-repairs confidence scores and wikilinks |
223
+ | 🎓 | **Teachable AI** | `/learn` permanently shapes how your agent thinks and responds |
224
+ | 🪄 | **Smart Memory Review** | `/memory-review` lets you interactively prune, update, or archive memory entries one by one |
225
+ | 🔒 | **Concurrent-session Safe** | Each session generates an isolated 6-char token — multiple parallel sessions never mix checkpoints |
226
+ | 📱 | **Mobile Access** | Send instructions and receive briefings from anywhere via Telegram |
227
+ | ⚙️ | **CLI Binary** | `onebrain` binary handles checkpoints, session init, doctor, vault-sync, and updates — no Bun, Python, or Node.js required |
228
+
229
+ ---
230
+
231
+ ## Use Cases
232
+
233
+ ### 🖥️ Personal AI OS
234
+
235
+ Run OneBrain as your personal AI operating system — a complete AI environment that runs locally with no cloud infrastructure required.
236
+
237
+ **Recommended stack:**
238
+
239
+ | Tool | Role |
240
+ |------|------|
241
+ | [Claude Code](https://claude.ai/code) | Your AI agent, running in the terminal |
242
+ | [Obsidian](https://obsidian.md) | Your vault — single source of truth for memory and knowledge |
243
+ | [tmux](https://github.com/tmux/tmux) | Persistent sessions that survive disconnects and reboots |
244
+ | [Telegram](https://telegram.org) | Mobile access: send instructions, receive briefings from anywhere |
245
+
246
+ **Setting up the full stack:**
247
+
248
+ 1. Install OneBrain and open your vault in Obsidian ([Get Started](#installation))
249
+ 2. Start a tmux session: `tmux new -s onebrain`
250
+ 3. Start Claude Code in your vault directory: `claude`
251
+ 4. Run `/telegram:configure` to connect Claude Code's built-in Telegram channel — no custom bot or external infra needed
252
+ 5. From any device, open Telegram and send instructions directly to your OneBrain agent
253
+
254
+ Your agent, your vault, your data — forever.
255
+
256
+ ### 🧠 Thinking Partner
257
+
258
+ Use OneBrain as a daily thinking partner. Capture ideas with `/braindump`, research topics with `/research`, synthesize knowledge with `/consolidate`, and surface connections you'd never find manually with `/connect`.
259
+
260
+ ### 📚 Knowledge Base Builder
261
+
262
+ Turn your AI into a knowledge curator: research, summarize, import files, and build a connected Markdown knowledge base that grows smarter over time.
263
+
264
+ ---
265
+
171
266
  ## Installation
172
267
 
173
- ### 1. Install the CLI
268
+ ### Pick Your Harness
269
+
270
+ Each harness reads OneBrain's instruction file automatically. Install it, run it inside your vault, and the plugin loads on first prompt.
271
+
272
+ | Harness | Install | Run | Reads |
273
+ |---|---|---|---|
274
+ | **Claude Code** *(recommended)* | `npm install -g @anthropic-ai/claude-code` | `claude` | `CLAUDE.md` |
275
+ | **Gemini CLI** | `npm install -g @google/gemini-cli` | `gemini` | `GEMINI.md` |
276
+ | **OpenAI Codex** | `npm install -g @openai/codex` | `codex` | `AGENTS.md` |
277
+ | **Qwen Code** | `npm install -g @qwen-code/qwen-code` | `qwen` | `AGENTS.md` |
278
+
279
+ > Auto-checkpoint and the Stop hook are wired up for Claude Code today. The other harnesses get the rest of the skill surface (24+ commands) immediately, and gain hook coverage as upstream support lands.
280
+
281
+ ### 1. Install the OneBrain CLI
174
282
 
175
283
  ```bash
176
284
  npm install -g @onebrain-ai/cli
@@ -192,31 +300,49 @@ File → Open Folder as Vault → select this folder
192
300
 
193
301
  ### 4. Personalize your vault
194
302
 
195
- In Claude Code: `/onboarding`
303
+ In your harness: `/onboarding`
196
304
 
197
305
  > **Adding OneBrain to an existing vault?** `cd` into it and run `onebrain init`
198
306
 
199
- ---
307
+ ### Bring Your Own LLM (via Claude Code)
200
308
 
201
- > **After `/update`:** Run `/reload-plugins` to pick up changes in your current session, or simply start a new session.
309
+ Already love Claude Code? Use it as a universal frontend. Point `ANTHROPIC_BASE_URL` at any OpenAI-compatible endpoint Claude Code stays the harness, the LLM behind it changes per task.
202
310
 
203
- ---
311
+ ```bash
312
+ # Recommended: claude-code-router handles Anthropic ↔ provider translation
313
+ npm install -g @musistudio/claude-code-router
314
+ ccr code # first-run config, then launches Claude Code via the router
315
+ # (later) ccr stop # tear down the router before going native again
316
+
317
+ # Or direct: point ANTHROPIC_BASE_URL at any Anthropic-protocol endpoint
318
+ export ANTHROPIC_BASE_URL=https://your-router-or-anthropic-compatible-host
319
+ export ANTHROPIC_API_KEY=sk-byok-key
320
+ cd vault && claude
321
+
322
+ # Switch back to native Claude any time (manual-export route)
323
+ unset ANTHROPIC_BASE_URL ANTHROPIC_API_KEY
324
+ claude
325
+ ```
204
326
 
205
- ## Supported Agents
327
+ | Route | What it gets you |
328
+ |---|---|
329
+ | **Local** (mlx, ollama, llama.cpp) | Cost-free routine work, full privacy. Pair with [`litellm`](https://github.com/BerriAI/litellm) or [`claude-code-router`](https://github.com/musistudio/claude-code-router). |
330
+ | **Cloud BYOK** (Claude, Gemini, GPT, Groq, OpenRouter) | Pay-as-you-go premium reasoning. One env-var swap, no code changes. |
331
+ | **Hybrid** (route by task or by cost) | Cheap models for routine, premium when it counts. |
206
332
 
207
- | Agent | Instruction file | Setup |
208
- |-------|-----------------|-------|
209
- | Claude Code | `CLAUDE.md` | Loaded automatically |
210
- | Gemini CLI | `GEMINI.md` | Loaded automatically |
211
- | Any agent | `AGENTS.md` | Read manually or via system prompt |
333
+ Same vault. Same skills. Same memory. The LLM swaps; OneBrain doesn't notice.
334
+
335
+ ---
336
+
337
+ > **After `/update`:** Run `/reload-plugins` to pick up changes in your current session, or simply start a new session.
212
338
 
213
339
  ---
214
340
 
215
341
  <a id="commands"></a>
216
342
 
217
- <details>
218
- <summary><strong>📋 24+ Commands</strong></summary>
219
- <br>
343
+ ## 📋 24+ Commands
344
+
345
+ The full skill surface, alphabetized by workflow.
220
346
 
221
347
  | Command | What it does |
222
348
  |---------|-------------|
@@ -246,8 +372,6 @@ In Claude Code: `/onboarding`
246
372
  | `/update` | Update skills, config, and plugins from GitHub |
247
373
  | `/help` | List all available commands with descriptions |
248
374
 
249
- </details>
250
-
251
375
  <details>
252
376
  <summary><strong>📁 Vault Structure</strong></summary>
253
377
  <br>
@@ -315,28 +439,9 @@ Checkpoints: `07-logs/YYYY/MM/YYYY-MM-DD-{session_token}-checkpoint-NN.md` — a
315
439
 
316
440
  </details>
317
441
 
318
- <details>
319
- <summary><strong>🧠 Memory System</strong></summary>
320
- <br>
321
-
322
- OneBrain uses a four-tier memory system, where knowledge flows upward as it gets validated. The Semantic tier has two loading modes (always-loaded and lazy-loaded):
323
-
324
- **Tier 1 — Working memory** (`00-inbox/` + current session)
325
- Everything that hasn't been processed yet. Captures from `/braindump`, `/capture`, and quick notes land here. Process with `/consolidate` to move into the knowledge base.
326
-
327
- **Tier 2 — Episodic memory** (`07-logs/`)
328
- Session logs: `YYYY-MM-DD-session-NN.md` in `YYYY/MM/` subfolders. Contains summaries, decisions, insights, and action items from each session. Generated by `/wrapup`.
329
- Checkpoints: `YYYY-MM-DD-{session_token}-checkpoint-NN.md` — auto-generated mid-session by hooks. Incorporated and deleted by `/wrapup`.
330
-
331
- **Tier 3 — Semantic memory** (`05-agent/MEMORY.md` + `05-agent/MEMORY-INDEX.md` + `05-agent/memory/`)
332
- Always loaded at session start: `MEMORY.md` holds Identity, Active Projects, and Critical Behaviors (~55 lines target). `MEMORY-INDEX.md` is the registry of all `memory/` files — loaded every session, enables lazy-loading. Individual `memory/` files are lazy-loaded on demand via MEMORY-INDEX.md. Only `/learn` writes to MEMORY.md Critical Behaviors. Use `/doctor --fix` to audit and repair stale entries.
442
+ ## Task Syntax
333
443
 
334
- **Tier 4 Knowledge base** (`03-knowledge/`)
335
- Permanent, synthesized notes. `/distill` crystallizes a completed topic thread into a structured note in `03-knowledge/`.
336
-
337
- ### Task Syntax
338
-
339
- OneBrain uses the [Obsidian Tasks](https://publish.obsidian.md/tasks/) plugin format:
444
+ OneBrain creates tasks in [Obsidian Tasks](https://publish.obsidian.md/tasks/) plugin format:
340
445
 
341
446
  ```
342
447
  - [ ] Task description 📅 2026-03-25
@@ -345,7 +450,19 @@ OneBrain uses the [Obsidian Tasks](https://publish.obsidian.md/tasks/) plugin fo
345
450
 
346
451
  Tasks live inline in your notes — the Tasks plugin surfaces them across the vault. Run `/tasks` to open a live dashboard in Obsidian (`TASKS.md` at vault root) with sections for overdue, due this week, unscheduled, due later, and recently completed.
347
452
 
348
- </details>
453
+ ---
454
+
455
+ ## OneBrain Cloud
456
+
457
+ Multi-device sync and hosted agent runtimes. Your unified intelligence travels with you.
458
+
459
+ | Tier | What you get | Status |
460
+ |---|---|---|
461
+ | **FREE** | Local vault · OSS skills · BYOK | ✅ Available now |
462
+ | **PRO** | Sync · mobile · hosted runtime | 🟡 [Join waitlist](https://onebrain.run) |
463
+ | **TEAM** | Shared intelligence · team mesh | 🟡 Coming soon |
464
+
465
+ ---
349
466
 
350
467
  <details>
351
468
  <summary><strong>⚙️ Prerequisites & Detailed Setup</strong></summary>
package/dist/onebrain CHANGED
@@ -9459,7 +9459,7 @@ var init_lib = __esm(() => {
9459
9459
  var require_package = __commonJS((exports, module) => {
9460
9460
  module.exports = {
9461
9461
  name: "@onebrain-ai/cli",
9462
- version: "2.1.8",
9462
+ version: "2.1.10",
9463
9463
  description: "CLI for OneBrain \u2014 personal AI OS for Obsidian with persistent memory, 24+ skills, and Claude Code integration",
9464
9464
  keywords: [
9465
9465
  "onebrain",
@@ -10761,7 +10761,7 @@ var import_picocolors5 = __toESM(require_picocolors(), 1);
10761
10761
  var import_picocolors = __toESM(require_picocolors(), 1);
10762
10762
  function resolveBinaryVersion() {
10763
10763
  if (true)
10764
- return "2.1.8";
10764
+ return "2.1.10";
10765
10765
  try {
10766
10766
  const pkg = require_package();
10767
10767
  return pkg.version ?? "dev";
@@ -10770,62 +10770,91 @@ function resolveBinaryVersion() {
10770
10770
  }
10771
10771
  }
10772
10772
  var ART_LINES = [
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`
10773
+ " ____ ____ _ ",
10774
+ " / __ \\ | _ \\ (_) ",
10775
+ "| | | |_ __ ___| |_) |_ __ __ _ _ _ __ ",
10776
+ "| | | | '_ \\ / _ \\ _ <| '__/ _` | | '_ \\ ",
10777
+ "| |__| | | | | __/ |_) | | | (_| | | | | |",
10778
+ " \\____/|_| |_|\\___|____/|_| \\__,_|_|_| |_|"
10778
10779
  ];
10779
- var PREFIX = "Your AI ";
10780
- var TAGLINE_LEAD = " ";
10781
- var TAGLINE_FALLBACK = `${PREFIX}Thinking Partner`;
10780
+ var PREFIX = "YOUR AI ";
10781
+ var TAGLINE_LEAD = " ";
10782
+ var TAGLINE_FALLBACK = `${PREFIX}THINKING PARTNER`;
10783
+ var SUBTITLE = "A unified intelligence in your Obsidian vault";
10782
10784
  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];
10785
+ var PREFIX_COLOR = [0, 243, 255];
10786
+ var TRAILING_COLOR = [255, 45, 146];
10787
+ var FINAL_COLOR = [0, 243, 255];
10788
+ var SUBTITLE_COLOR = [0, 170, 178];
10786
10789
  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
+ { trailing: "REMEMBERS YOU", trailingWords: ["REMEMBERS", "YOU"], wordTicks: [24, 32] },
10791
+ { trailing: "CATCHES INSIGHTS", trailingWords: ["CATCHES", "INSIGHTS"], wordTicks: [27, 26] },
10792
+ { trailing: "THINKING PARTNER", trailingWords: ["THINKING", "PARTNER"], wordTicks: [26, 31] }
10790
10793
  ];
10791
10794
  function supportsRgb() {
10795
+ if (process.env["FORCE_COLOR"] === "3")
10796
+ return true;
10792
10797
  const c = process.env["COLORTERM"] ?? "";
10793
10798
  return c === "truecolor" || c === "24bit";
10794
10799
  }
10800
+ function isInteractiveStdout() {
10801
+ if (process.env["ONEBRAIN_FORCE_TTY"] === "1")
10802
+ return true;
10803
+ if (process.env["FORCE_COLOR"] === "3")
10804
+ return true;
10805
+ return Boolean(process.stdout.isTTY);
10806
+ }
10795
10807
  function rgb(r, g, b, ch) {
10796
10808
  return `\x1B[1;38;2;${r};${g};${b}m${ch}\x1B[0m`;
10797
10809
  }
10798
10810
  function rgbStr(c, ch) {
10799
10811
  return rgb(c[0], c[1], c[2], ch);
10800
10812
  }
10801
- function hsvToRgb(h, floor = 80) {
10802
- const c = 255;
10803
- const x = Math.round(c * (1 - Math.abs(h / 60 % 2 - 1)));
10804
- let r = 0;
10805
- let g = 0;
10806
- let b = 0;
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];
10819
- return [Math.min(255, r + floor), Math.min(255, g + floor), Math.min(255, b + floor)];
10813
+ var BRAND_STOPS = [
10814
+ { t: 0, rgb: [255, 45, 146] },
10815
+ { t: 0.55, rgb: [255, 90, 163] },
10816
+ { t: 1, rgb: [0, 243, 255] }
10817
+ ];
10818
+ function brandGradient(t) {
10819
+ const tt = Math.max(0, Math.min(1, t));
10820
+ for (let i = 0;i < BRAND_STOPS.length - 1; i++) {
10821
+ const a = BRAND_STOPS[i];
10822
+ const b = BRAND_STOPS[i + 1];
10823
+ if (tt <= b.t) {
10824
+ const local = (tt - a.t) / (b.t - a.t);
10825
+ return [
10826
+ Math.round(a.rgb[0] + (b.rgb[0] - a.rgb[0]) * local),
10827
+ Math.round(a.rgb[1] + (b.rgb[1] - a.rgb[1]) * local),
10828
+ Math.round(a.rgb[2] + (b.rgb[2] - a.rgb[2]) * local)
10829
+ ];
10830
+ }
10831
+ }
10832
+ return BRAND_STOPS[BRAND_STOPS.length - 1].rgb;
10833
+ }
10834
+ var [DIAG_MIN, DIAG_MAX] = (() => {
10835
+ let min = 0;
10836
+ let max = 0;
10837
+ for (let row = 0;row < ART_LINES.length; row++) {
10838
+ min = Math.min(min, -row * 3);
10839
+ max = Math.max(max, ART_LINES[row].length - 1 - row * 3);
10840
+ }
10841
+ return [min, max];
10842
+ })();
10843
+ var DIAG_RANGE = DIAG_MAX - DIAG_MIN;
10844
+ function gradientForCell(row, col) {
10845
+ const d = col - 3 * row;
10846
+ return brandGradient((d - DIAG_MIN) / DIAG_RANGE);
10847
+ }
10848
+ var WHITE_SGR = "\x1B[1;97m";
10849
+ var SGR_RESET = "\x1B[0m";
10850
+ function whiteCell(ch) {
10851
+ return `${WHITE_SGR}${ch}${SGR_RESET}`;
10820
10852
  }
10821
- var HUE_PER_CHAR = 10;
10822
- var HUE_PER_ROW = 30;
10823
- function neonLine(line, lineIndex = 0, floor = 80) {
10824
- return line.split("").map((ch, i) => {
10853
+ function neonLine(line, lineIndex = 0) {
10854
+ return line.split("").map((ch, col) => {
10825
10855
  if (ch === " ")
10826
10856
  return ch;
10827
- const hue = ((i * HUE_PER_CHAR - lineIndex * HUE_PER_ROW) % 360 + 360) % 360;
10828
- const [r, g, b] = hsvToRgb(hue, floor);
10857
+ const [r, g, b] = gradientForCell(lineIndex, col);
10829
10858
  return rgb(r, g, b, ch);
10830
10859
  }).join("");
10831
10860
  }
@@ -10835,13 +10864,18 @@ function whiteLine(line) {
10835
10864
  function whiteGlowLine(line, alpha) {
10836
10865
  return line.split("").map((ch) => ch === " " ? ch : `\x1B[1;38;2;${alpha};${alpha};${alpha}m${ch}\x1B[0m`).join("");
10837
10866
  }
10867
+ function renderSubtitle() {
10868
+ const [r, g, b] = SUBTITLE_COLOR;
10869
+ return `\x1B[2;38;2;${r};${g};${b}m${SUBTITLE}\x1B[0m`;
10870
+ }
10838
10871
  function dimLine(line) {
10839
- return line.split("").map((ch) => ch === " " ? ch : `\x1B[2;38;2;50;50;70m${ch}\x1B[0m`).join("");
10872
+ return line.split("").map((ch) => ch === " " ? ch : `\x1B[2;38;2;30;60;70m${ch}\x1B[0m`).join("");
10840
10873
  }
10841
10874
  function scanLineCh(line) {
10842
10875
  return line.split("").map((ch) => ch === " " ? ch : rgb(140, 255, 255, ch)).join("");
10843
10876
  }
10844
- var CURSOR = rgb(140, 255, 255, "\u258C");
10877
+ var SCAN_CYAN = [140, 255, 255];
10878
+ var CURSOR = rgb(SCAN_CYAN[0], SCAN_CYAN[1], SCAN_CYAN[2], "\u258C");
10845
10879
  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
10880
  var randGlyph = () => GLYPHS[Math.floor(Math.random() * GLYPHS.length)] ?? "?";
10847
10881
  var glitchWhite = (g) => `\x1B[1;97m${g}\x1B[0m`;
@@ -10883,7 +10917,7 @@ var SENTENCE_HOLD_MS = 500;
10883
10917
  var WIPE_TICK_MS = 22;
10884
10918
  var WIPE_TRAIL = 3;
10885
10919
  var WIPE_PAUSE_MS = 80;
10886
- async function playBannerIntro(rainbowArt, whiteArt) {
10920
+ async function playBannerIntro(brandArt, whiteArt) {
10887
10921
  const delay = (ms) => new Promise((r) => setTimeout(r, ms));
10888
10922
  const up = (n) => outb(`\x1B[${n}F`);
10889
10923
  printFrame(ART_LINES.map(dimLine), blankTagline());
@@ -10906,32 +10940,25 @@ async function playBannerIntro(rainbowArt, whiteArt) {
10906
10940
  up(BANNER_LINE_COUNT);
10907
10941
  printFrame(whiteArt, blankTagline());
10908
10942
  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
10943
  function flowFrame(frontD) {
10916
10944
  return ART_LINES.map((line, row) => line.split("").map((ch, col) => {
10917
10945
  if (ch === " ")
10918
10946
  return ch;
10919
10947
  const d = col - 3 * row;
10920
10948
  if (d <= frontD) {
10921
- const hue = ((col * HUE_PER_CHAR - row * HUE_PER_ROW) % 360 + 360) % 360;
10922
- const [r, g, b] = hsvToRgb(hue);
10949
+ const [r, g, b] = gradientForCell(row, col);
10923
10950
  return rgb(r, g, b, ch);
10924
10951
  }
10925
- return `\x1B[1;97m${ch}\x1B[0m`;
10952
+ return whiteCell(ch);
10926
10953
  }).join(""));
10927
10954
  }
10928
- for (let d = minD;d <= maxD; d++) {
10955
+ for (let d = DIAG_MIN;d <= DIAG_MAX; d++) {
10929
10956
  await delay(9);
10930
10957
  up(BANNER_LINE_COUNT);
10931
10958
  printFrame(flowFrame(d), blankTagline());
10932
10959
  }
10933
10960
  up(BANNER_LINE_COUNT);
10934
- printFrame(rainbowArt, blankTagline());
10961
+ printFrame(brandArt, blankTagline());
10935
10962
  await delay(180);
10936
10963
  function shimmerArtFrame(highlight) {
10937
10964
  return ART_LINES.map((line, row) => line.split("").map((ch, col) => {
@@ -10939,25 +10966,24 @@ async function playBannerIntro(rainbowArt, whiteArt) {
10939
10966
  return ch;
10940
10967
  const d = col - 3 * row;
10941
10968
  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);
10969
+ return whiteCell(ch);
10970
+ const [r, g, b] = gradientForCell(row, col);
10945
10971
  return rgb(r, g, b, ch);
10946
10972
  }).join(""));
10947
10973
  }
10948
- for (let d = minD;d <= maxD; d++) {
10974
+ for (let d = DIAG_MIN;d <= DIAG_MAX; d++) {
10949
10975
  await delay(9);
10950
10976
  up(BANNER_LINE_COUNT);
10951
10977
  printFrame(shimmerArtFrame(d), blankTagline());
10952
10978
  }
10953
10979
  up(BANNER_LINE_COUNT);
10954
- printFrame(rainbowArt, blankTagline());
10980
+ printFrame(brandArt, blankTagline());
10955
10981
  await delay(80);
10956
10982
  }
10957
- async function decodeFirstSentence(rainbowArt, s) {
10983
+ async function decodeFirstSentence(brandArt, s) {
10958
10984
  const delay = (ms) => new Promise((r) => setTimeout(r, ms));
10959
10985
  const up = (n) => outb(`\x1B[${n}F`);
10960
- const prefixWords = ["Your", "AI"];
10986
+ const prefixWords = ["YOUR", "AI"];
10961
10987
  for (let wi = 0;wi < prefixWords.length; wi++) {
10962
10988
  const w = prefixWords[wi];
10963
10989
  const tickMs = PREFIX_TICK_MS[wi];
@@ -10986,16 +11012,16 @@ async function decodeFirstSentence(rainbowArt, s) {
10986
11012
  }
10987
11013
  }
10988
11014
  const trailingBlank = " ".repeat(s.trailing.length);
10989
- printFrame(rainbowArt, `${prefixPart}${trailingBlank}\x1B[K`);
11015
+ printFrame(brandArt, `${prefixPart}${trailingBlank}\x1B[K`);
10990
11016
  }
10991
11017
  if (wi < prefixWords.length - 1) {
10992
11018
  await delay(INTER_WORD_PAUSE_MS);
10993
11019
  }
10994
11020
  }
10995
11021
  await delay(INTER_WORD_PAUSE_MS);
10996
- await decodeTrailing(rainbowArt, s, PREFIX.length);
11022
+ await decodeTrailing(brandArt, s, PREFIX.length);
10997
11023
  }
10998
- async function decodeTrailing(rainbowArt, s, lockedPrefixChars) {
11024
+ async function decodeTrailing(brandArt, s, lockedPrefixChars) {
10999
11025
  const delay = (ms) => new Promise((r) => setTimeout(r, ms));
11000
11026
  const up = (n) => outb(`\x1B[${n}F`);
11001
11027
  const words = s.trailingWords;
@@ -11047,14 +11073,14 @@ async function decodeTrailing(rainbowArt, s, lockedPrefixChars) {
11047
11073
  trailing += " ";
11048
11074
  }
11049
11075
  }
11050
- printFrame(rainbowArt, buildTaglineLine(lockedPrefixChars, trailing));
11076
+ printFrame(brandArt, buildTaglineLine(lockedPrefixChars, trailing));
11051
11077
  }
11052
11078
  if (wi < words.length - 1) {
11053
11079
  await delay(INTER_WORD_PAUSE_MS);
11054
11080
  }
11055
11081
  }
11056
11082
  }
11057
- async function wipeSwapTransition(rainbowArt, from, to) {
11083
+ async function wipeSwapTransition(brandArt, from, to) {
11058
11084
  const delay = (ms) => new Promise((r) => setTimeout(r, ms));
11059
11085
  const up = (n) => outb(`\x1B[${n}F`);
11060
11086
  for (let pos = from.trailing.length - 1;pos >= -WIPE_TRAIL; pos--) {
@@ -11076,20 +11102,20 @@ async function wipeSwapTransition(rainbowArt, from, to) {
11076
11102
  trailing += rgbStr(TRAILING_COLOR, ch);
11077
11103
  }
11078
11104
  }
11079
- printFrame(rainbowArt, buildTaglineLine(PREFIX.length, trailing));
11105
+ printFrame(brandArt, buildTaglineLine(PREFIX.length, trailing));
11080
11106
  }
11081
11107
  await delay(WIPE_PAUSE_MS);
11082
- await decodeTrailing(rainbowArt, to, PREFIX.length);
11108
+ await decodeTrailing(brandArt, to, PREFIX.length);
11083
11109
  }
11084
- async function lockShimmer(rainbowArt, s) {
11110
+ async function lockShimmer(brandArt, s) {
11085
11111
  const delay = (ms) => new Promise((r) => setTimeout(r, ms));
11086
11112
  const up = (n) => outb(`\x1B[${n}F`);
11087
11113
  const SHIMMER_TICK_MS = 22;
11088
11114
  const TRAIL = 3;
11089
11115
  const STOPS = [
11090
11116
  [255, 255, 255],
11091
- [200, 245, 255],
11092
- [150, 235, 255]
11117
+ [180, 220, 255],
11118
+ [0, 243, 255]
11093
11119
  ];
11094
11120
  const fullText = PREFIX + s.trailing;
11095
11121
  const N = fullText.length;
@@ -11114,7 +11140,7 @@ async function lockShimmer(rainbowArt, s) {
11114
11140
  }
11115
11141
  }
11116
11142
  line += "\x1B[K";
11117
- printFrame(rainbowArt, line);
11143
+ printFrame(brandArt, line);
11118
11144
  }
11119
11145
  up(BANNER_LINE_COUNT);
11120
11146
  let finalLine = TAGLINE_LEAD;
@@ -11123,38 +11149,68 @@ async function lockShimmer(rainbowArt, s) {
11123
11149
  finalLine += ch === " " ? " " : rgbStr(FINAL_COLOR, ch);
11124
11150
  }
11125
11151
  finalLine += "\x1B[K";
11126
- printFrame(rainbowArt, finalLine);
11152
+ const subtitleLine = `${TAGLINE_LEAD}${renderSubtitle()}\x1B[K`;
11153
+ outb(`
11154
+ `);
11155
+ for (const l of brandArt)
11156
+ outb(`${l}
11157
+ `);
11158
+ outb(`
11159
+ `);
11160
+ outb(`${finalLine}
11161
+ `);
11162
+ outb(`${subtitleLine}
11163
+ `);
11164
+ outb(`
11165
+ `);
11127
11166
  await delay(150);
11128
11167
  }
11129
- async function printBanner() {
11130
- if (!process.stdout.isTTY)
11131
- return;
11132
- if (!supportsRgb()) {
11133
- outb(`
11168
+ function printStaticBanner() {
11169
+ const truecolor = supportsRgb();
11170
+ outb(`
11134
11171
  `);
11172
+ if (truecolor) {
11173
+ for (let i = 0;i < ART_LINES.length; i++)
11174
+ outb(`${neonLine(ART_LINES[i], i)}
11175
+ `);
11176
+ } else {
11135
11177
  for (const l of ART_LINES)
11136
11178
  outb(`${import_picocolors.default.bold(import_picocolors.default.cyan(l))}
11137
11179
  `);
11138
- outb(`
11180
+ }
11181
+ outb(`
11182
+ `);
11183
+ if (truecolor) {
11184
+ outb(`${TAGLINE_LEAD}${rgbStr(FINAL_COLOR, TAGLINE_FALLBACK)}
11139
11185
  `);
11186
+ outb(`${TAGLINE_LEAD}${renderSubtitle()}
11187
+ `);
11188
+ } else {
11140
11189
  outb(`${TAGLINE_LEAD}${import_picocolors.default.bold(import_picocolors.default.cyan(TAGLINE_FALLBACK))}
11141
11190
  `);
11142
- outb(`
11191
+ outb(`${TAGLINE_LEAD}${import_picocolors.default.dim(import_picocolors.default.cyan(SUBTITLE))}
11143
11192
  `);
11193
+ }
11194
+ outb(`
11195
+ `);
11196
+ }
11197
+ async function printBanner() {
11198
+ if (!isInteractiveStdout() || !supportsRgb()) {
11199
+ printStaticBanner();
11144
11200
  return;
11145
11201
  }
11146
- const rainbowArt = ART_LINES.map((l, i) => neonLine(l, i));
11202
+ const brandArt = ART_LINES.map((l, i) => neonLine(l, i));
11147
11203
  const whiteArt = ART_LINES.map((l) => whiteLine(l));
11148
11204
  try {
11149
11205
  outb("\x1B[?25l");
11150
- await playBannerIntro(rainbowArt, whiteArt);
11151
- await decodeFirstSentence(rainbowArt, SENTENCES[0]);
11206
+ await playBannerIntro(brandArt, whiteArt);
11207
+ await decodeFirstSentence(brandArt, SENTENCES[0]);
11152
11208
  await new Promise((r) => setTimeout(r, SENTENCE_HOLD_MS));
11153
- await wipeSwapTransition(rainbowArt, SENTENCES[0], SENTENCES[1]);
11209
+ await wipeSwapTransition(brandArt, SENTENCES[0], SENTENCES[1]);
11154
11210
  await new Promise((r) => setTimeout(r, SENTENCE_HOLD_MS));
11155
- await wipeSwapTransition(rainbowArt, SENTENCES[1], SENTENCES[2]);
11211
+ await wipeSwapTransition(brandArt, SENTENCES[1], SENTENCES[2]);
11156
11212
  await new Promise((r) => setTimeout(r, SENTENCE_HOLD_MS));
11157
- await lockShimmer(rainbowArt, SENTENCES[2]);
11213
+ await lockShimmer(brandArt, SENTENCES[2]);
11158
11214
  } finally {
11159
11215
  outb("\x1B[?25h");
11160
11216
  }
@@ -12202,7 +12258,7 @@ async function runBackfillRecapped(logsFolder, cutoffDate) {
12202
12258
  const files = await listMdFiles(monthPath);
12203
12259
  for (const fname of files) {
12204
12260
  const fpath = join8(monthPath, fname);
12205
- if (fname.includes("-checkpoint-")) {
12261
+ if (!fname.includes("-session-")) {
12206
12262
  continue;
12207
12263
  }
12208
12264
  if (cutoffDate) {
@@ -12299,7 +12355,7 @@ async function listMdFiles2(dir) {
12299
12355
  }
12300
12356
  async function hasManualSessionLog(monthDir, date) {
12301
12357
  const files = await listMdFiles2(monthDir);
12302
- const sessionLogs = files.filter((f2) => f2.startsWith(date) && !f2.includes("-checkpoint-") && f2.endsWith(".md"));
12358
+ const sessionLogs = files.filter((f2) => f2.startsWith(date) && f2.includes("-session-") && f2.endsWith(".md"));
12303
12359
  for (const logName of sessionLogs) {
12304
12360
  try {
12305
12361
  const content = await readFile5(join9(monthDir, logName), "utf8");
@@ -12340,10 +12396,9 @@ async function scanMonthDir(monthDir, currentToken, today, seenTokens) {
12340
12396
  }
12341
12397
  return count;
12342
12398
  }
12343
- async function runOrphanScan(logsFolder, sessionToken) {
12344
- const now = new Date;
12399
+ async function runOrphanScan(logsFolder, sessionToken, now) {
12345
12400
  const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
12346
- const { thisYear, thisMonth, prevYear, prevMonth } = getMonthParts();
12401
+ const { thisYear, thisMonth, prevYear, prevMonth } = getMonthParts(now);
12347
12402
  const monthDirs = [
12348
12403
  { year: thisYear, month: thisMonth },
12349
12404
  { year: prevYear, month: prevMonth }
@@ -12357,7 +12412,7 @@ async function runOrphanScan(logsFolder, sessionToken) {
12357
12412
  return { orphan_count: totalOrphans };
12358
12413
  }
12359
12414
  async function orphanScanCommand(logsFolder, sessionToken) {
12360
- const result = await runOrphanScan(logsFolder, sessionToken);
12415
+ const result = await runOrphanScan(logsFolder, sessionToken, new Date);
12361
12416
  process.stdout.write(`${JSON.stringify(result)}
12362
12417
  `);
12363
12418
  }
@@ -12793,8 +12848,8 @@ function patchUtf8(stream) {
12793
12848
  }
12794
12849
 
12795
12850
  // src/index.ts
12796
- var VERSION = "2.1.8";
12797
- var RELEASE_DATE = "2026-04-30";
12851
+ var VERSION = "2.1.10";
12852
+ var RELEASE_DATE = "2026-05-05";
12798
12853
  patchUtf8(process.stdout);
12799
12854
  patchUtf8(process.stderr);
12800
12855
  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.1.8",
3
+ "version": "2.1.10",
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",