Nathan King136 downloadsA vault-scoped writing companion that lives in your sidebar — hatches an ASCII pet per vault and reacts to how you write.
A vault-scoped writing companion for Obsidian. Bestest Buddy hatches a persistent ASCII pet for each vault, watches how you write, and reacts with short, in-context observations — a companion that lives in your sidebar, not a chatbot you converse with.
It is built around three principles:
.----.
/ × × \ Capy Scribbles
| -- | Common Capybara
`-____-´
Each vault gets its own generated buddy with persistent traits:
· ✦ × ◉ @ °GRAMMARING, PATIENCE, CHAOS, WISDOM, SNARKThe buddy is derived deterministically from the vault seed, so it stays stable until reset.
Rarity is rolled by weight, so most buddies are common and a mythic is genuinely rare:
| Rarity | Weight | Notes |
|---|---|---|
| Common | 60 | |
| Uncommon | 25 | |
| Rare | 10 | |
| Epic | 4 | |
| Legendary | 1 | |
| Mythic | 0.1 | Animated rainbow sprite, background, and nameplate |
A dedicated right-sidebar view that opens automatically when the workspace is ready. It includes:
Enable Minimal mode to show only the sprite.
The pet is alive in the panel:
When enabled and not muted, the buddy occasionally reacts to vault activity: opening a daily note, creating a note, writing in a burst, sustaining a session, making a large revision cut, pausing, or returning after a pause. Reactions are deliberately selective — gated by cooldown, probability, session state, and your snark level — so they punctuate rather than interrupt.
Send the buddy a message from the panel, or use the command to ask about the current note. Replies are short, grounded in the buddy's personality and session state, and — when note context is enabled — informed by your current selection (or the full note if nothing is selected).
The plugin keeps a lightweight model of your session:
These influence the UI hints and the likelihood and tone of reactions.
Bestest Buddy is not yet in the community plugin directory, so install it manually.
1. Build it:
npm install
npm run build
2. Copy the build artifacts into your vault's plugin folder (id is bestest-buddy):
VAULT="/path/to/your/vault"
mkdir -p "$VAULT/.obsidian/plugins/bestest-buddy"
cp main.js manifest.json styles.css \
"$VAULT/.obsidian/plugins/bestest-buddy/"
Only those three files are needed — do not copy src/ or node_modules/.
3. Enable it: Obsidian → Settings → Community plugins → enable Bestest Buddy.
Updating an existing install? Obsidian doesn't hot-reload plugin files. After copying a new
main.js, toggle the plugin off/on or reload Obsidian (Cmd/Ctrl+R).
Requires Obsidian 1.5.0+. Works on desktop and mobile (isDesktopOnly: false).
All commands are available from the command palette under Bestest Buddy. A sparkles ribbon icon also opens the panel.
| Command | Action |
|---|---|
| Open panel | Reveal the sidebar view |
| Pet buddy | Trigger the petting animation and a reaction |
| Ask buddy about current note | Generate an observation about the active note |
| Mute / Unmute ambient reactions | Toggle automatic reactions |
| Reset buddy | New vault seed; clears companion, events, mood, session |
| Customize: choose species / eye style / hat / rarity / color | Override a generated trait |
| Customize: edit stats | Manually set stat values |
| Customize: toggle shiny | Toggle the shiny flag |
| Customize: reset appearance to generated | Clear all appearance overrides |
| Setting | Default | Description |
|---|---|---|
| LLM provider | OpenAI | OpenAI or Claude (Anthropic) |
| OpenAI API key | — | Used when provider is OpenAI (masked input) |
| Claude API key | — | Used when provider is Claude (masked input) |
| Model | gpt-4.1-mini |
Model name for the selected provider (e.g. gpt-4.1-mini, claude-haiku-4-5-20251001) |
| Ambient reactions | on | Allow the buddy to react to note activity |
| Reaction frequency | Normal | Quiet, Normal, or Chatty |
| Writing burst threshold | 50 | New words that trigger a writing-burst reaction (minimum 10) |
| Include current note context | on | Pass note title and excerpt to replies |
| Minimal mode | off | Show only the sprite in the panel |
| Snark level | 50 | 0 = rare and gentle, 100 = constant and merciless. Values above 90 can use significantly more tokens. |
The plugin listens to workspace and editor activity and records buddy events:
session_started, note_opened, new_note_created, writing_burst, steady_session, revision_spike, long_pause, returned_after_pause, daily_note_opened, direct_question, pet, manual_note_help_request, chatty_tick.
Writing-related events are inferred from editing behavior:
An ambient reaction only fires when all of these hold: a buddy exists, ambient reactions are enabled, the plugin is not muted, the cooldown has expired, the event type is reaction-eligible, and a probability roll passes. The cooldown and probability are modulated by reaction frequency, current session mode, detected patterns, and snark level.
When frequency is set to Chatty, a periodic chatty tick lets the buddy comment on the text near your cursor during active writing (subject to its own cooldown).
The buddy uses an LLM for two jobs: generating its name and personality at hatch time, and generating short reactions and direct replies.
| Provider | Endpoint | Example model |
|---|---|---|
| OpenAI | https://api.openai.com/v1/responses |
gpt-4.1-mini |
| Claude (Anthropic) | https://api.anthropic.com/v1/messages |
claude-haiku-4-5-20251001 |
Fallbacks: if no API key is configured or a request fails, hatching falls back to local name/personality generation and reactions fall back to local rule-based text. The plugin remains fully functional without any API key — responses are just less varied.
Knowing which you got, and why: a canned line is marked quietly — the speech bubble gets a dotted outline and nothing else, so running the plugin without a key stays a perfectly good way to use it. Hover the bubble (or focus it with the keyboard) and the reason appears: a rejected key, a model the provider does not recognize (quoted, so you can see the typo), an exhausted quota, rate limiting, a provider outage, an unreachable host, or a response with no usable output. The provider's own error message is included, since that is the part you can act on, and causes you can repair yourself carry an Open settings shortcut.
Beyond the generated buddy, you can override any visible trait via the Customize commands: species, eye style, hat, rarity, sprite color (presets or a custom #rrggbb hex), shiny, and individual stats. Overrides are stored separately from the generated companion, so Customize: reset appearance to generated restores the original at any time.
data.json): vault seed, hatched companion, appearance overrides, mute state, recent events (most recent 20), mood and session state, and your settings..obsidian data accordingly.These limits are intentional:
npm install
npm run dev # esbuild watch build
npm run build # production build
npm run typecheck # tsc --noEmit
npm test # vitest (unit tests)
The plugin is written in TypeScript and bundled with esbuild into main.js. The distributable consists of main.js, manifest.json, and styles.css.
Unit tests live in tests/ (vitest) and cover the pure generation and sprite logic. CI (.github/workflows/ci.yml) runs typecheck, tests, and build on every push and PR; tagging a commit X.Y.Z triggers release.yml to build and attach the release artifacts.
bestest-buddy/
├─ manifest.json plugin manifest (id, version, minAppVersion)
├─ versions.json plugin version → minimum app version map
├─ esbuild.config.mjs build configuration
├─ styles.css panel and sprite styling (incl. mythic animations)
├─ main.js bundled output (committed; also attached to releases)
├─ tests/ vitest unit tests
└─ src/
├─ main.ts plugin entry: lifecycle, commands, reaction logic
├─ view.ts sidebar panel rendering
├─ events.ts workspace/editor event detection
├─ store.ts persistence and companion state
├─ settings.ts settings tab
├─ customize.ts customization modals
├─ llm.ts OpenAI/Claude calls, prompts, local fallbacks
├─ constants.ts defaults and view type
├─ types.ts plugin/data/settings types
└─ lib/buddy/
├─ companion.ts seeded generation and rarity/stat rolls
├─ sprites.ts ASCII sprite frames per species
├─ reactions.ts local reaction text
└─ types.ts species, rarities, hats, eyes, stats
Author: Nathan King.
With thanks to Owen McGrath, a valued contributor.
Licensed under the MIT License — see LICENSE.