opencode-buddy 0.2.0
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/LICENSE +21 -0
- package/README.md +206 -0
- package/bin/opencode-buddy.js +45 -0
- package/package.json +61 -0
- package/src/buddy/species.js +399 -0
- package/src/buddy/state.js +161 -0
- package/src/index.js +302 -0
- package/src/install.js +157 -0
- package/src/persistence.js +28 -0
- package/src/plugin.js +178 -0
- package/src/tmux.js +90 -0
- package/src/tui.js +164 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 opencode-buddy contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# opencode-buddy
|
|
2
|
+
|
|
3
|
+
A virtual ASCII pet companion for [opencode](https://opencode.ai). Use it as a `/buddy` slash command inside opencode, or as a tmux side pane next to it.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
opencode TUI buddy pane
|
|
7
|
+
┌──────────────────────┐ ┌──────────────────────┐
|
|
8
|
+
│ │ │ ╭─ Quack the duck ─╮ │
|
|
9
|
+
│ your opencode │ │ __ │ │
|
|
10
|
+
│ TUI here │ │ <(o )___ │ │
|
|
11
|
+
│ │ │ ( ._> / │ │
|
|
12
|
+
│ │ │ `--' │ │
|
|
13
|
+
│ │ │ │ │
|
|
14
|
+
│ │ │ hunger ████████░░ 79 │ │
|
|
15
|
+
│ │ │ happy ████████░░ 79 │ │
|
|
16
|
+
│ │ │ energy ██████████ 100 │ │
|
|
17
|
+
│ │ │ keys: f feed · ... │ │
|
|
18
|
+
└──────────────────────┘ └──────────────────────┘
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The buddy reacts to what you're doing — idles when you idle, cheers when opencode finishes a turn, gets scared when a session errors, falls asleep when energy runs low.
|
|
22
|
+
|
|
23
|
+
## Why?
|
|
24
|
+
|
|
25
|
+
Claude Code v2.1.88 had a fun `/buddy` easter egg (an ASCII pet companion). When Anthropic removed it in v2.1.97 the community revolted. This is the opencode equivalent: your own buddy, open source, never going to be taken away.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
Requires Node.js ≥ 18. tmux is only needed for the side-pane mode (Mode B).
|
|
30
|
+
|
|
31
|
+
### Mode A: `/buddy` inside opencode (recommended)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g opencode-buddy
|
|
35
|
+
opencode-buddy install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This writes `~/.config/opencode/commands/buddy.md` and adds
|
|
39
|
+
`opencode-buddy` to your `opencode.json` `plugin` list. **Restart
|
|
40
|
+
opencode.** Then in the TUI:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
/buddy show current status
|
|
44
|
+
/buddy feed feed the buddy
|
|
45
|
+
/buddy play play with the buddy (+xp)
|
|
46
|
+
/buddy rest tuck the buddy in
|
|
47
|
+
/buddy switch cat become a cat
|
|
48
|
+
/buddy switch dragon become a dragon
|
|
49
|
+
/buddy rename Mia rename
|
|
50
|
+
/buddy hatch dragon Sparky start fresh
|
|
51
|
+
/buddy ascii render ASCII art inline
|
|
52
|
+
/buddy help list all actions
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The buddy's mood is updated automatically: `session.idle` makes it
|
|
56
|
+
celebrate, `session.error` makes it scared, low energy sends it to sleep.
|
|
57
|
+
|
|
58
|
+
### Mode B: tmux side pane (optional, for a permanent companion)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install -g opencode-buddy
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
In any tmux pane (next to your opencode pane):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
opencode-buddy start
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
A 28-column side pane splits off to the right and the buddy appears
|
|
71
|
+
inside it, rendered in full ASCII with color. Press keys in the buddy
|
|
72
|
+
pane to interact:
|
|
73
|
+
|
|
74
|
+
| Key | Action |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `f` | Feed (+25 hunger) |
|
|
77
|
+
| `p` | Play (+20 happiness, +5 xp) |
|
|
78
|
+
| `z` | Rest (+30 energy) |
|
|
79
|
+
| `r` | Rename (prompts in the pane) |
|
|
80
|
+
| `s` | Switch species (cycles through 6) |
|
|
81
|
+
| `n` | Hatch a new buddy (resets state) |
|
|
82
|
+
| `q` | Quit (kills the side pane) |
|
|
83
|
+
|
|
84
|
+
**Modes A and B share the same persisted state** at
|
|
85
|
+
`~/.config/opencode-buddy/state.json`, so feeding the buddy via
|
|
86
|
+
`/buddy feed` in the TUI will also be reflected in the side pane and
|
|
87
|
+
vice versa.
|
|
88
|
+
|
|
89
|
+
### Install from source
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone https://github.com/AMark-CS/opencode-buddy
|
|
93
|
+
cd opencode-buddy
|
|
94
|
+
npm install # only for plugin dev deps
|
|
95
|
+
npm link # adds `opencode-buddy` to PATH
|
|
96
|
+
opencode-buddy install
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Uninstall
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
opencode-buddy uninstall
|
|
103
|
+
npm uninstall -g opencode-buddy
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Headless commands
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
opencode-buddy stats # one-line status printout
|
|
110
|
+
opencode-buddy feed # feed from any terminal
|
|
111
|
+
opencode-buddy hatch dragon Sparky # start fresh as a dragon named Sparky
|
|
112
|
+
opencode-buddy switch cat # become a cat
|
|
113
|
+
opencode-buddy rename CaptainQuack # rename
|
|
114
|
+
opencode-buddy notify done # nudge the buddy from a hook
|
|
115
|
+
opencode-buddy path # print path to the state file
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
These work whether or not the TUI or tmux side pane is running.
|
|
119
|
+
| `n` | Hatch a new buddy (resets state) |
|
|
120
|
+
| `q` | Quit (kills the side pane) |
|
|
121
|
+
|
|
122
|
+
### Headless commands
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
opencode-buddy stats # one-line status printout
|
|
126
|
+
opencode-buddy feed # feed from any terminal
|
|
127
|
+
opencode-buddy hatch dragon Sparky # start fresh as a dragon named Sparky
|
|
128
|
+
opencode-buddy switch cat # become a cat
|
|
129
|
+
opencode-buddy rename CaptainQuack # rename
|
|
130
|
+
opencode-buddy notify done # nudge the buddy from a hook / hookup
|
|
131
|
+
opencode-buddy path # print path to the state file
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## The six species
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
duck cat dragon
|
|
138
|
+
__ /\\_/\ /\\_/\
|
|
139
|
+
<(o )___ ( o.o ) ( o o ) ~~
|
|
140
|
+
( ._> / > ^ < > ^ < /
|
|
141
|
+
`--' /| |\\ /| |\\
|
|
142
|
+
~ idle ~ (_| |_) (_| |_)
|
|
143
|
+
meow rawr
|
|
144
|
+
|
|
145
|
+
axolotl robot ghost
|
|
146
|
+
^___^ [ O . O ] .-"\"-.
|
|
147
|
+
(o . o) /|#####|\\ ( o . o )
|
|
148
|
+
\\|_|_|/ / |#####| \\ | ~ ~ |
|
|
149
|
+
\\| |/ | | | |
|
|
150
|
+
) ( /| | | |\\ \\uuuuu/
|
|
151
|
+
~ ambien beep boo
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Each has frames for `idle / working / celebrating / scared / sleeping`.
|
|
155
|
+
|
|
156
|
+
## How it works
|
|
157
|
+
|
|
158
|
+
* **As an opencode plugin** (`src/plugin.js`): registers a `buddy` tool the LLM can call, and listens to `session.idle` / `session.error` events to update the buddy's mood automatically. The `/buddy` slash command in `~/.config/opencode/commands/buddy.md` is a thin prompt that tells the LLM to use that tool.
|
|
159
|
+
* **As a tmux sidecar** (`bin/opencode-buddy.js start`): runs a separate process in a right-side tmux pane. Uses `tmux capture-pane` to peek at the opencode pane text and infer whether opencode is generating, errored, or idle.
|
|
160
|
+
* **Shared state** at `~/.config/opencode-buddy/state.json`. Both the plugin and the sidecar read and write the same file, so a `/buddy feed` in the TUI is immediately visible in the side pane and vice versa.
|
|
161
|
+
* **Zero runtime dependencies.** Pure Node.js + ANSI escapes for the renderer, `spawnSync("tmux", ...)` for pane control. The only dev-time dependency is `@opencode-ai/plugin` (used to define the opencode tool).
|
|
162
|
+
|
|
163
|
+
## Limitations
|
|
164
|
+
|
|
165
|
+
* **Activity inference is best-effort.** If opencode's prompt text changes, the heuristics may misclassify. The `notify` subcommand is the explicit way to push events.
|
|
166
|
+
* **The /buddy command is LLM-mediated.** The slash command is a prompt that tells opencode's LLM to call the `buddy` tool. It works only inside an active opencode session. For a direct terminal-friendly interface use `opencode-buddy <subcommand>` or the tmux side pane.
|
|
167
|
+
* **No animations yet.** Frames are static; the renderer just swaps the frame when the state changes. Adding 2-3 frame loops per state is straightforward (PRs welcome).
|
|
168
|
+
|
|
169
|
+
## Project layout
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
opencode-buddy/
|
|
173
|
+
├── bin/
|
|
174
|
+
│ └── opencode-buddy.js # CLI entry
|
|
175
|
+
├── src/
|
|
176
|
+
│ ├── plugin.js # opencode plugin (exports BuddyPlugin)
|
|
177
|
+
│ ├── install.js # one-shot installer for /buddy + opencode.json
|
|
178
|
+
│ ├── index.js # CLI runtime + headless commands
|
|
179
|
+
│ ├── tui.js # ANSI renderer for the side pane
|
|
180
|
+
│ ├── tmux.js # tmux capture / split helpers
|
|
181
|
+
│ ├── persistence.js # state file at ~/.config/opencode-buddy/
|
|
182
|
+
│ └── buddy/
|
|
183
|
+
│ ├── species.js # ASCII art + color palettes
|
|
184
|
+
│ └── state.js # state machine + attribute math
|
|
185
|
+
├── test/
|
|
186
|
+
│ ├── smoke.js # state / species unit tests
|
|
187
|
+
│ ├── plugin.smoke.js # plugin tool unit tests
|
|
188
|
+
│ └── e2e.js # full tmux roundtrip
|
|
189
|
+
├── package.json
|
|
190
|
+
├── LICENSE # MIT
|
|
191
|
+
├── README.md
|
|
192
|
+
├── PUSH.md # GitHub push guide
|
|
193
|
+
└── PUBLISH.md # npm publish guide
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Development
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm test # runs all unit tests (smoke + plugin)
|
|
200
|
+
npm run test:e2e # runs end-to-end tmux test (creates & kills a session)
|
|
201
|
+
npm run install-buddy # runs the opencode install command
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CLI entry point. Routes to either the headless command runner or the
|
|
3
|
+
// interactive TUI renderer.
|
|
4
|
+
|
|
5
|
+
import { runInteractive, runCommand } from "../src/index.js";
|
|
6
|
+
import { install, uninstall } from "../src/install.js";
|
|
7
|
+
|
|
8
|
+
const argv = process.argv.slice(2);
|
|
9
|
+
const [sub, ...rest] = argv;
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
if (!sub || sub === "help" || sub === "--help" || sub === "-h") {
|
|
13
|
+
await runCommand([]);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (sub === "start") {
|
|
18
|
+
await runInteractive({ autoSplit: true });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (sub === "render") {
|
|
23
|
+
// Re-entered inside the tmux side pane by `start`. Just render.
|
|
24
|
+
await runInteractive({ autoSplit: false });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (sub === "install") {
|
|
29
|
+
await install();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (sub === "uninstall") {
|
|
34
|
+
await uninstall();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Everything else: headless command
|
|
39
|
+
await runCommand(argv);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
main().catch((err) => {
|
|
43
|
+
console.error(`opencode-buddy: ${err.message || err}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-buddy",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A virtual ASCII pet companion for opencode. Works as a tmux sidecar or an in-TUI /buddy slash command.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/plugin.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/plugin.js",
|
|
9
|
+
"./cli": "./bin/opencode-buddy.js",
|
|
10
|
+
"./package.json": "./package.json"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"opencode-buddy": "bin/opencode-buddy.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin",
|
|
17
|
+
"src",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"start": "node bin/opencode-buddy.js start",
|
|
23
|
+
"hatch": "node bin/opencode-buddy.js hatch",
|
|
24
|
+
"stats": "node bin/opencode-buddy.js stats",
|
|
25
|
+
"install-buddy": "node bin/opencode-buddy.js install",
|
|
26
|
+
"test": "node --test test/smoke.js test/plugin.smoke.js",
|
|
27
|
+
"test:e2e": "node test/e2e.js"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"opencode",
|
|
31
|
+
"opencode-plugin",
|
|
32
|
+
"tmux",
|
|
33
|
+
"pet",
|
|
34
|
+
"tamagotchi",
|
|
35
|
+
"ascii",
|
|
36
|
+
"tui",
|
|
37
|
+
"companion",
|
|
38
|
+
"buddy",
|
|
39
|
+
"virtual-pet"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18"
|
|
43
|
+
},
|
|
44
|
+
"author": "AMark-CS",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/AMark-CS/opencode-buddy.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/AMark-CS/opencode-buddy/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/AMark-CS/opencode-buddy#readme",
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@opencode-ai/plugin": "*"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"registry": "https://registry.npmjs.org/",
|
|
59
|
+
"access": "public"
|
|
60
|
+
}
|
|
61
|
+
}
|