@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.
- package/README.md +216 -99
- package/dist/onebrain +150 -95
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<
|
|
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://
|
|
7
|
-
<a href="
|
|
8
|
-
<a href="https://github.com/onebrain-ai/onebrain/stargazers"><img src="https://img.shields.io/github/stars/onebrain-ai/onebrain?style=
|
|
9
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
##
|
|
60
|
+
## The Harness OS Architecture
|
|
66
61
|
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
+
### Extend, don't replace
|
|
81
81
|
|
|
82
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
93
|
+
---
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
## One Vault, All Projects — The Command Center
|
|
95
96
|
|
|
96
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
10774
|
-
"
|
|
10775
|
-
"
|
|
10776
|
-
"
|
|
10777
|
-
|
|
10773
|
+
" ____ ____ _ ",
|
|
10774
|
+
" / __ \\ | _ \\ (_) ",
|
|
10775
|
+
"| | | |_ __ ___| |_) |_ __ __ _ _ _ __ ",
|
|
10776
|
+
"| | | | '_ \\ / _ \\ _ <| '__/ _` | | '_ \\ ",
|
|
10777
|
+
"| |__| | | | | __/ |_) | | | (_| | | | | |",
|
|
10778
|
+
" \\____/|_| |_|\\___|____/|_| \\__,_|_|_| |_|"
|
|
10778
10779
|
];
|
|
10779
|
-
var PREFIX = "
|
|
10780
|
-
var TAGLINE_LEAD = "
|
|
10781
|
-
var TAGLINE_FALLBACK = `${PREFIX}
|
|
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 = [
|
|
10784
|
-
var TRAILING_COLOR = [255,
|
|
10785
|
-
var FINAL_COLOR = [
|
|
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: "
|
|
10788
|
-
{ trailing: "
|
|
10789
|
-
{ trailing: "
|
|
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
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
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
|
-
|
|
10822
|
-
|
|
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
|
|
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;
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
10952
|
+
return whiteCell(ch);
|
|
10926
10953
|
}).join(""));
|
|
10927
10954
|
}
|
|
10928
|
-
for (let 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(
|
|
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
|
|
10943
|
-
const
|
|
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 =
|
|
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(
|
|
10980
|
+
printFrame(brandArt, blankTagline());
|
|
10955
10981
|
await delay(80);
|
|
10956
10982
|
}
|
|
10957
|
-
async function decodeFirstSentence(
|
|
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 = ["
|
|
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(
|
|
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(
|
|
11022
|
+
await decodeTrailing(brandArt, s, PREFIX.length);
|
|
10997
11023
|
}
|
|
10998
|
-
async function decodeTrailing(
|
|
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(
|
|
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(
|
|
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(
|
|
11105
|
+
printFrame(brandArt, buildTaglineLine(PREFIX.length, trailing));
|
|
11080
11106
|
}
|
|
11081
11107
|
await delay(WIPE_PAUSE_MS);
|
|
11082
|
-
await decodeTrailing(
|
|
11108
|
+
await decodeTrailing(brandArt, to, PREFIX.length);
|
|
11083
11109
|
}
|
|
11084
|
-
async function lockShimmer(
|
|
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
|
-
[
|
|
11092
|
-
[
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
11151
|
-
await decodeFirstSentence(
|
|
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(
|
|
11209
|
+
await wipeSwapTransition(brandArt, SENTENCES[0], SENTENCES[1]);
|
|
11154
11210
|
await new Promise((r) => setTimeout(r, SENTENCE_HOLD_MS));
|
|
11155
|
-
await wipeSwapTransition(
|
|
11211
|
+
await wipeSwapTransition(brandArt, SENTENCES[1], SENTENCES[2]);
|
|
11156
11212
|
await new Promise((r) => setTimeout(r, SENTENCE_HOLD_MS));
|
|
11157
|
-
await lockShimmer(
|
|
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("-
|
|
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) &&
|
|
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.
|
|
12797
|
-
var RELEASE_DATE = "2026-
|
|
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}`;
|