opencode-buddy 0.2.1 → 0.3.1
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 +77 -156
- package/package.json +11 -20
- package/src/persistence.js +7 -7
- package/src/server-plugin.js +133 -0
- package/src/species.js +541 -0
- package/src/state.js +120 -0
- package/src/tui-plugin.jsx +156 -0
- package/bin/opencode-buddy.js +0 -45
- package/src/buddy/species.js +0 -399
- package/src/buddy/state.js +0 -168
- package/src/index.js +0 -332
- package/src/install.js +0 -157
- package/src/plugin.js +0 -178
- package/src/tmux.js +0 -90
- package/src/tui.js +0 -164
package/README.md
CHANGED
|
@@ -1,204 +1,125 @@
|
|
|
1
1
|
# opencode-buddy
|
|
2
2
|
|
|
3
|
-
A virtual ASCII pet companion
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
3
|
+
A virtual ASCII pet companion that lives in the opencode TUI sidebar. Hatches, feeds, plays, and reacts to what you're coding — all without leaving opencode.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌──────────────────────────┐
|
|
7
|
+
│ opencode TUI │
|
|
8
|
+
│ │
|
|
9
|
+
│ > your prompt here │
|
|
10
|
+
│ │
|
|
11
|
+
│ sidebar │
|
|
12
|
+
│ ┌──────────────────────┐ │
|
|
13
|
+
│ │ Quack the duck │ │
|
|
14
|
+
│ │ __ │ │
|
|
15
|
+
│ │ <(o )___ │ │
|
|
16
|
+
│ │ ( ._> / │ │
|
|
17
|
+
│ │ `--' │ │
|
|
18
|
+
│ │ ──────────────────── │ │
|
|
19
|
+
│ │ hunger ████████░░ 79 │ │
|
|
20
|
+
│ │ happy ████████░░ 79 │ │
|
|
21
|
+
│ │ energy ██████████ 100│ │
|
|
22
|
+
│ │ idle · Lv 1 · xp 0 │ │
|
|
23
|
+
│ └──────────────────────┘ │
|
|
24
|
+
└──────────────────────────┘
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The buddy blinks every 600ms. Switch species, watch it cheer when opencode finishes a turn, and get scared when a session errors.
|
|
26
28
|
|
|
27
29
|
## Install
|
|
28
30
|
|
|
29
|
-
Requires Node.js ≥ 18
|
|
30
|
-
|
|
31
|
-
### Mode A: `/buddy` inside opencode (recommended)
|
|
31
|
+
Requires Node.js ≥ 18 and opencode ≥ 1.15.
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
npm install -g opencode-buddy
|
|
35
|
-
opencode-buddy install
|
|
36
35
|
```
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
`opencode-buddy` to your `opencode.json` `plugin` list. **Restart
|
|
40
|
-
opencode.** Then in the TUI:
|
|
37
|
+
Add to your `~/.config/opencode/opencode.json`:
|
|
41
38
|
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"plugin": ["opencode-buddy"]
|
|
42
|
+
}
|
|
53
43
|
```
|
|
54
44
|
|
|
55
|
-
The buddy
|
|
56
|
-
celebrate, `session.error` makes it scared, low energy sends it to sleep.
|
|
45
|
+
Restart opencode. The buddy appears in the sidebar.
|
|
57
46
|
|
|
58
|
-
|
|
47
|
+
## How to interact
|
|
59
48
|
|
|
60
|
-
|
|
61
|
-
npm install -g opencode-buddy
|
|
62
|
-
```
|
|
49
|
+
The buddy has a `buddy` tool the LLM can call. Just talk to it:
|
|
63
50
|
|
|
64
|
-
In any tmux pane (next to your opencode pane):
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
opencode-buddy start
|
|
68
51
|
```
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
52
|
+
/buddy
|
|
53
|
+
/feed the buddy
|
|
54
|
+
/switch to a dragon
|
|
55
|
+
/rename it to Sparky
|
|
56
|
+
/can you show me the buddy's ascii art
|
|
97
57
|
```
|
|
98
58
|
|
|
99
|
-
|
|
59
|
+
The LLM will call the `buddy` tool with the right action. Available actions:
|
|
100
60
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
```
|
|
61
|
+
| Action | Effect |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `status` | Print current stats |
|
|
64
|
+
| `feed` | +25 hunger, -5 energy |
|
|
65
|
+
| `play` | +20 happiness, -5 hunger, +5 xp |
|
|
66
|
+
| `rest` | +30 energy |
|
|
67
|
+
| `switch <species>` | Change to duck, cat, dragon, axolotl, robot, or ghost |
|
|
68
|
+
| `rename <name>` | Rename (max 20 chars) |
|
|
69
|
+
| `hatch [species] [name]` | Start a brand new buddy |
|
|
70
|
+
| `ascii [frame]` | Return rendered ASCII art |
|
|
71
|
+
| `path` | Path to the state file |
|
|
72
|
+
| `help` | List actions |
|
|
133
73
|
|
|
134
|
-
##
|
|
74
|
+
## Six species
|
|
135
75
|
|
|
136
76
|
```
|
|
137
77
|
duck cat dragon
|
|
138
|
-
__
|
|
78
|
+
__ /\_/\ /\_/\
|
|
139
79
|
<(o )___ ( o.o ) ( o o ) ~~
|
|
140
80
|
( ._> / > ^ < > ^ < /
|
|
141
|
-
`--' /|
|
|
142
|
-
~ idle ~ (_| |_)
|
|
81
|
+
`--' /| |\ /| |\
|
|
82
|
+
~ idle ~ (_| |_) (_| |_)
|
|
143
83
|
meow rawr
|
|
144
84
|
|
|
145
85
|
axolotl robot ghost
|
|
146
86
|
^___^ [ O . O ] .-"\"-.
|
|
147
|
-
(o . o)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
) ( /| | |
|
|
87
|
+
(o . o) /|#####|\ ( o . o )
|
|
88
|
+
\|_|_|/ / |#####| \ | ~ ~ |
|
|
89
|
+
\| |/ | | | |
|
|
90
|
+
) ( /| | | |\ \uuuuu/
|
|
151
91
|
~ ambien beep boo
|
|
152
92
|
```
|
|
153
93
|
|
|
154
|
-
Each has
|
|
94
|
+
Each species has a per-character color palette. Idle state has 3 frames (blink loop) at 600ms per frame.
|
|
155
95
|
|
|
156
96
|
## How it works
|
|
157
97
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
98
|
+
- **TUI plugin** (`src/tui-plugin.jsx`): registers `sidebar_content` slot, renders a Solid component that polls `~/.config/opencode-buddy/state.json` every 1.5s and animates at 6fps. Pure opencode-native — no tmux, no separate process.
|
|
99
|
+
- **Server plugin** (`src/server-plugin.js`): registers a `buddy` tool the LLM can call. The same package exports both entrypoints (`main` for server, `exports["./tui"]` for TUI).
|
|
100
|
+
- **State** at `~/.config/opencode-buddy/state.json` (~/.config is cross-platform via os.homedir()). Attributes decay over time. Sessions auto-celebrate on `session.idle` and auto-scare on `session.error`.
|
|
101
|
+
|
|
102
|
+
## Uninstall
|
|
162
103
|
|
|
163
|
-
|
|
104
|
+
```bash
|
|
105
|
+
npm uninstall -g opencode-buddy
|
|
106
|
+
```
|
|
164
107
|
|
|
165
|
-
|
|
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).
|
|
108
|
+
Then remove `"opencode-buddy"` from your `opencode.json` `plugin` array.
|
|
168
109
|
|
|
169
110
|
## Project layout
|
|
170
111
|
|
|
171
112
|
```
|
|
172
113
|
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
114
|
├── package.json
|
|
190
|
-
├── LICENSE # MIT
|
|
191
115
|
├── README.md
|
|
192
|
-
├──
|
|
193
|
-
└──
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
|
116
|
+
├── LICENSE
|
|
117
|
+
└── src/
|
|
118
|
+
├── tui-plugin.jsx # v2 TUI plugin, mounts to sidebar_content
|
|
119
|
+
├── server-plugin.js # v1 server plugin, exposes `buddy` tool
|
|
120
|
+
├── species.js # ASCII art + per-species color palettes
|
|
121
|
+
├── state.js # state machine (hunger/happiness/energy decay)
|
|
122
|
+
└── persistence.js # state file reader/writer
|
|
202
123
|
```
|
|
203
124
|
|
|
204
125
|
## License
|
package/package.json
CHANGED
|
@@ -1,42 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-buddy",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A virtual ASCII pet companion
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "A virtual ASCII pet companion that lives in the opencode TUI sidebar. Hatches, feeds, plays, and reacts to what you're coding.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "src/plugin.js",
|
|
6
|
+
"main": "src/server-plugin.js",
|
|
7
7
|
"exports": {
|
|
8
|
-
".": "./src/plugin.js",
|
|
9
|
-
"./
|
|
8
|
+
".": "./src/server-plugin.js",
|
|
9
|
+
"./tui": "./src/tui-plugin.jsx",
|
|
10
10
|
"./package.json": "./package.json"
|
|
11
11
|
},
|
|
12
|
-
"bin": {
|
|
13
|
-
"opencode-buddy": "bin/opencode-buddy.js"
|
|
14
|
-
},
|
|
15
12
|
"files": [
|
|
16
|
-
"bin",
|
|
17
13
|
"src",
|
|
18
14
|
"README.md",
|
|
19
15
|
"LICENSE"
|
|
20
16
|
],
|
|
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
17
|
"keywords": [
|
|
30
18
|
"opencode",
|
|
31
19
|
"opencode-plugin",
|
|
32
|
-
"
|
|
20
|
+
"tui",
|
|
33
21
|
"pet",
|
|
34
22
|
"tamagotchi",
|
|
35
23
|
"ascii",
|
|
36
|
-
"tui",
|
|
37
24
|
"companion",
|
|
38
25
|
"buddy",
|
|
39
|
-
"virtual-pet"
|
|
26
|
+
"virtual-pet",
|
|
27
|
+
"sidebar"
|
|
40
28
|
],
|
|
41
29
|
"engines": {
|
|
42
30
|
"node": ">=18"
|
|
@@ -51,6 +39,9 @@
|
|
|
51
39
|
"url": "https://github.com/AMark-CS/opencode-buddy/issues"
|
|
52
40
|
},
|
|
53
41
|
"homepage": "https://github.com/AMark-CS/opencode-buddy#readme",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@opentui/solid": "^0.3.1"
|
|
44
|
+
},
|
|
54
45
|
"peerDependencies": {
|
|
55
46
|
"@opencode-ai/plugin": "*"
|
|
56
47
|
},
|
package/src/persistence.js
CHANGED
|
@@ -16,6 +16,13 @@ export async function load() {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export async function save(state) {
|
|
20
|
+
await fs.mkdir(DIR, { recursive: true });
|
|
21
|
+
const tmp = FILE + ".tmp";
|
|
22
|
+
await fs.writeFile(tmp, JSON.stringify(state, null, 2));
|
|
23
|
+
await fs.rename(tmp, FILE);
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
export async function mtime() {
|
|
20
27
|
try {
|
|
21
28
|
const st = await fs.stat(FILE);
|
|
@@ -26,13 +33,6 @@ export async function mtime() {
|
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
export async function save(state) {
|
|
30
|
-
await fs.mkdir(DIR, { recursive: true });
|
|
31
|
-
const tmp = FILE + ".tmp";
|
|
32
|
-
await fs.writeFile(tmp, JSON.stringify(state, null, 2));
|
|
33
|
-
await fs.rename(tmp, FILE);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
36
|
export function pathForDisplay() {
|
|
37
37
|
return FILE;
|
|
38
38
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// v1 server plugin. Provides the `buddy` tool the LLM can call.
|
|
2
|
+
// Loaded by opencode via package.json `main` (server-kind entrypoint).
|
|
3
|
+
|
|
4
|
+
import { tool } from "@opencode-ai/plugin";
|
|
5
|
+
import * as state from "./state.js";
|
|
6
|
+
import * as persistence from "./persistence.js";
|
|
7
|
+
import { SPECIES, renderFrame, maxFrameHeight } from "./species.js";
|
|
8
|
+
|
|
9
|
+
const SPECIES_LIST = SPECIES.join(", ");
|
|
10
|
+
|
|
11
|
+
const DESCRIPTION = `Interact with your virtual buddy companion. Pass an "action" argument.
|
|
12
|
+
|
|
13
|
+
Actions:
|
|
14
|
+
status - print current stats as a one-liner
|
|
15
|
+
feed - feed the buddy (+25 hunger, -5 energy)
|
|
16
|
+
play - play with the buddy (+20 happiness, +5 xp)
|
|
17
|
+
rest - let the buddy rest (+30 energy)
|
|
18
|
+
switch <species> - change species (${SPECIES_LIST})
|
|
19
|
+
rename <name> - rename the buddy (max 20 chars)
|
|
20
|
+
hatch [species] [name] - start a brand new buddy
|
|
21
|
+
ascii [frame] - render the current buddy as ASCII art
|
|
22
|
+
help - list available actions
|
|
23
|
+
|
|
24
|
+
The buddy lives in the opencode sidebar. Use this tool whenever the
|
|
25
|
+
user wants to interact with their buddy.`;
|
|
26
|
+
|
|
27
|
+
function summary(s) {
|
|
28
|
+
return `${s.name} the ${s.species} | Lv ${s.level} | ${s.state} | hunger ${Math.floor(s.hunger)} happiness ${Math.floor(s.happiness)} energy ${Math.floor(s.energy)} | xp ${s.xp}/${s.level * 50}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function loadOrInit() {
|
|
32
|
+
const s = await persistence.load();
|
|
33
|
+
if (s) return s;
|
|
34
|
+
const fresh = state.hatch();
|
|
35
|
+
await persistence.save(fresh);
|
|
36
|
+
return fresh;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function BuddyPlugin() {
|
|
40
|
+
return {
|
|
41
|
+
tool: {
|
|
42
|
+
buddy: tool({
|
|
43
|
+
description: DESCRIPTION,
|
|
44
|
+
args: {
|
|
45
|
+
action: tool.schema
|
|
46
|
+
.string()
|
|
47
|
+
.describe(
|
|
48
|
+
`One of: status, feed, play, rest, switch, rename, hatch, ascii, help. For switch/rename/hatch, also pass the "species" or "name" argument.`,
|
|
49
|
+
),
|
|
50
|
+
species: tool.schema
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe(`Species for switch/hatch: ${SPECIES_LIST}`),
|
|
54
|
+
name: tool.schema
|
|
55
|
+
.string()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe(`Name for rename/hatch (max 20 chars).`),
|
|
58
|
+
frame: tool.schema
|
|
59
|
+
.number()
|
|
60
|
+
.int()
|
|
61
|
+
.min(0)
|
|
62
|
+
.max(10)
|
|
63
|
+
.optional()
|
|
64
|
+
.describe(`Frame index for ascii action.`),
|
|
65
|
+
},
|
|
66
|
+
async execute(args) {
|
|
67
|
+
let s = await loadOrInit();
|
|
68
|
+
const action = (args.action || "").toLowerCase();
|
|
69
|
+
const species = (args.species || "").toLowerCase();
|
|
70
|
+
const name = args.name;
|
|
71
|
+
const frame = args.frame ?? 0;
|
|
72
|
+
|
|
73
|
+
switch (action) {
|
|
74
|
+
case "":
|
|
75
|
+
case "status":
|
|
76
|
+
return summary(s);
|
|
77
|
+
case "feed":
|
|
78
|
+
s = state.feed(s);
|
|
79
|
+
await persistence.save(s);
|
|
80
|
+
return `${s.name} munches happily. ${summary(s)}`;
|
|
81
|
+
case "play":
|
|
82
|
+
s = state.play(s);
|
|
83
|
+
await persistence.save(s);
|
|
84
|
+
return `${s.name} plays! ${summary(s)}`;
|
|
85
|
+
case "rest":
|
|
86
|
+
s = state.rest(s);
|
|
87
|
+
await persistence.save(s);
|
|
88
|
+
return `${s.name} curls up. ${summary(s)}`;
|
|
89
|
+
case "switch": {
|
|
90
|
+
if (!species) return `Pick a species: ${SPECIES_LIST}`;
|
|
91
|
+
if (!SPECIES.includes(species))
|
|
92
|
+
return `Unknown species "${species}". Valid: ${SPECIES_LIST}`;
|
|
93
|
+
s = state.switchSpecies(s, species);
|
|
94
|
+
await persistence.save(s);
|
|
95
|
+
return `${s.name} transformed into a ${species}. ${summary(s)}`;
|
|
96
|
+
}
|
|
97
|
+
case "rename": {
|
|
98
|
+
if (!name) return `Provide a name with the "name" argument.`;
|
|
99
|
+
s = state.rename(s, name);
|
|
100
|
+
await persistence.save(s);
|
|
101
|
+
return `Renamed to ${s.name}. ${summary(s)}`;
|
|
102
|
+
}
|
|
103
|
+
case "hatch": {
|
|
104
|
+
const sp = species && SPECIES.includes(species) ? species : "duck";
|
|
105
|
+
s = state.hatch({ species: sp, name: name || "Buddy" });
|
|
106
|
+
await persistence.save(s);
|
|
107
|
+
return `Hatched a new ${sp} named ${s.name}. ${summary(s)}`;
|
|
108
|
+
}
|
|
109
|
+
case "ascii": {
|
|
110
|
+
const lines = renderFrame(s.species, s.state, frame);
|
|
111
|
+
return [
|
|
112
|
+
"```",
|
|
113
|
+
...lines,
|
|
114
|
+
"```",
|
|
115
|
+
`${s.name} the ${s.species} · ${s.state}`,
|
|
116
|
+
`hunger ${Math.floor(s.hunger)} happy ${Math.floor(s.happiness)} energy ${Math.floor(s.energy)} xp ${s.xp}/${s.level * 50}`,
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
case "help":
|
|
120
|
+
return DESCRIPTION;
|
|
121
|
+
default:
|
|
122
|
+
return `Unknown action "${args.action}". Try: action=help`;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default {
|
|
131
|
+
id: "opencode-buddy",
|
|
132
|
+
server: BuddyPlugin,
|
|
133
|
+
};
|