pi-sprite 1.0.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/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/NOTICE.md +7 -0
- package/README.md +353 -0
- package/examples/README.md +15 -0
- package/examples/custom-pets/wumpus-template/README.md +21 -0
- package/examples/custom-pets/wumpus-template/pet.json +14 -0
- package/examples/petdex-downloads/.gitkeep +0 -0
- package/extensions/index.ts +104 -0
- package/package.json +77 -0
- package/skills/pi-sprite-authoring/SKILL.md +330 -0
- package/skills/pi-sprite-authoring/assets/wumpus-template/pet.json +14 -0
- package/skills/pi-sprite-authoring/references/character-cohesion-review.md +65 -0
- package/skills/pi-sprite-authoring/references/gpt-image-sprite-workflow.md +181 -0
- package/skills/pi-sprite-authoring/references/petdex-reference-to-custom-pet.md +155 -0
- package/skills/pi-sprite-authoring/references/wumpus-sprite-prompts.md +54 -0
- package/skills/pi-sprite-authoring/scripts/assemble_sprite_strip.py +98 -0
- package/skills/pi-sprite-authoring/scripts/create-pet-template.mjs +51 -0
- package/skills/pi-sprite-authoring/scripts/create_motion_strip.py +110 -0
- package/skills/pi-sprite-authoring/scripts/download-petdex-examples.mjs +79 -0
- package/skills/pi-sprite-authoring/scripts/openai_sprite_image.py +223 -0
- package/skills/pi-sprite-authoring/scripts/remove_sprite_background.py +207 -0
- package/src/agent/session-entries.ts +28 -0
- package/src/agent/side-completion.ts +94 -0
- package/src/agent/side-session-text.ts +20 -0
- package/src/agent/side-session-types.ts +12 -0
- package/src/agent/side-session.ts +107 -0
- package/src/btw/completion.ts +15 -0
- package/src/btw/format.ts +30 -0
- package/src/btw/index.ts +306 -0
- package/src/btw/prompt.ts +35 -0
- package/src/btw/recap.ts +56 -0
- package/src/btw/session.ts +37 -0
- package/src/btw/thread-store.ts +37 -0
- package/src/context/index.ts +343 -0
- package/src/recap/conversation.ts +22 -0
- package/src/recap/direct.ts +15 -0
- package/src/recap/format.ts +25 -0
- package/src/recap/generation.ts +58 -0
- package/src/recap/index.ts +86 -0
- package/src/sprite/commands.ts +205 -0
- package/src/sprite/download.ts +75 -0
- package/src/sprite/kitty-placeholder.ts +441 -0
- package/src/sprite/live-status-format.ts +67 -0
- package/src/sprite/live-status.ts +48 -0
- package/src/sprite/loader.ts +134 -0
- package/src/sprite/manifest.ts +87 -0
- package/src/sprite/paths.ts +8 -0
- package/src/sprite/petdex.ts +133 -0
- package/src/sprite/renderer.ts +491 -0
- package/src/sprite/runtime.ts +524 -0
- package/src/sprite/turn-status-format.ts +112 -0
- package/src/sprite/turn-status.ts +88 -0
- package/src/ui/overlay.ts +308 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 - First pi-sprite release
|
|
4
|
+
|
|
5
|
+
- Added the `pi-sprite` Pi package with `/pet`, `/sprite`, `/context`, `/recap`, and `/btw` commands.
|
|
6
|
+
- Added passive terminal sprite rendering with ANSI fallback and Kitty/Ghostty/WezTerm native placeholder support.
|
|
7
|
+
- Added Petdex and local pet workflows: `/pet gallery`, `/pet preview`, `/pet install`, `/pet import`, and `/pet import-url`.
|
|
8
|
+
- Added the packaged `pi-sprite-authoring` skill and starter scripts for custom pet creation.
|
|
9
|
+
- Added explicit side-session generation for recap, BTW replies, turn status, and live status, reusing Pi's active model/provider where possible.
|
|
10
|
+
- Added README, MkDocs, WendyBot3000 release demo, package smoke tests, and release validation guidance.
|
|
11
|
+
|
|
12
|
+
This project began as a slimmed derivative of `pi-pokepet`; `NOTICE.md` retains attribution for the original MIT-licensed work.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 djdiptayan
|
|
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/NOTICE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Notices
|
|
2
|
+
|
|
3
|
+
`pi-sprite` began as a slimmed derivative of [`djdiptayan1/pi-pokepet`](https://github.com/djdiptayan1/pi-pokepet), which is licensed under the MIT License.
|
|
4
|
+
|
|
5
|
+
The original MIT copyright notice is preserved in `LICENSE` and must remain in all substantial copies or distributions that include derived `pi-pokepet` code.
|
|
6
|
+
|
|
7
|
+
References used for design research include `pi-btw`, `pi-recap`, `pi-context`, `pi-context-viz`, and `pi-pompom`; those projects are not vendored unless explicitly noted in source files or package metadata.
|
package/README.md
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# pi-sprite
|
|
2
|
+
|
|
3
|
+
`pi-sprite` is a small Pi package that adds a terminal sprite, a context visualizer, a recap bubble, and a side-question thread to [Pi](https://pi.dev).
|
|
4
|
+
|
|
5
|
+
It is intentionally not a pet simulator or desktop companion. The sprite is there to make agent state easier to read without adding another dashboard.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
The demo shows real Pi slash commands: importing a pet, opening `/context`, asking `/btw`, running `/recap`, and watching the bottom footer status update.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
Install from GitHub today:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pi install git:github.com/safurrier/pi-sprite@main
|
|
17
|
+
pi
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
After the npm release, the install path becomes:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pi install npm:pi-sprite
|
|
24
|
+
pi
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
If you are developing from a checkout, run the local package without installing it:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd /path/to/pi-sprite
|
|
31
|
+
pi -e .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Once Pi opens, get to a useful first sprite:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
/pet status
|
|
38
|
+
/pet gallery
|
|
39
|
+
/pet preview <id-from-gallery>
|
|
40
|
+
/pet install <id-from-gallery>
|
|
41
|
+
/context
|
|
42
|
+
/btw what should I look at next?
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`/pet install` selects the installed Petdex sprite automatically. If you already imported pets, use `/pet list` and `/pet choose <id>` instead.
|
|
46
|
+
|
|
47
|
+
If you already have a local pet folder, import it with an absolute path:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
/pet import /absolute/path/to/my-pet
|
|
51
|
+
/pet choose my-pet
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To author a new sprite with agent help, start the guided workflow:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
/pet create tiny desk cat with cozy pixel-art vibes
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For deeper custom-pet guidance, read the hosted [Sprite Authoring Guide](https://safurrier.github.io/pi-sprite/tutorials/authoring-sprites/).
|
|
61
|
+
|
|
62
|
+
If native images ever get stuck after changing renderers or restarting tmux, clean the terminal image layer and redraw:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
/pet clear-native
|
|
66
|
+
/pet show
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## What you get
|
|
70
|
+
|
|
71
|
+
| Command | Use it for |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `/pet` | Show, hide, choose, import, author, and configure the sprite. |
|
|
74
|
+
| `/sprite` | Package-specific alias for `/pet`, useful when another package also owns pet-like commands. |
|
|
75
|
+
| `/context` | Open a Claude-style context usage visualizer. |
|
|
76
|
+
| `/recap` | Generate a compact recap of the current session in a speech bubble. |
|
|
77
|
+
| `/btw` | Ask side questions without adding normal messages to the main thread, or generate a recap into the side thread. |
|
|
78
|
+
|
|
79
|
+
The extension also updates the sprite automatically during agent turns:
|
|
80
|
+
|
|
81
|
+
- `thinking` while the agent is reasoning
|
|
82
|
+
- `working` while tools run
|
|
83
|
+
- `success` or `error` after turn/tool outcomes
|
|
84
|
+
- compact footer status after turns, with provisional live status during long-running turns
|
|
85
|
+
|
|
86
|
+
## Sprite behavior
|
|
87
|
+
|
|
88
|
+
By default the sprite is compact, right-aligned, and label-free. The pet/state label lives in Pi's footer status line instead of taking more space in the widget.
|
|
89
|
+
|
|
90
|
+
Useful `/pet` commands:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
/pet status
|
|
94
|
+
/pet list
|
|
95
|
+
/pet choose <id>
|
|
96
|
+
/pet import <path>
|
|
97
|
+
/pet import-url <url>
|
|
98
|
+
/pet create [brief]
|
|
99
|
+
/pet author [brief]
|
|
100
|
+
/pet hide
|
|
101
|
+
/pet show
|
|
102
|
+
/pet size tiny|small|medium|large
|
|
103
|
+
/pet label on|off
|
|
104
|
+
/pet align left|right
|
|
105
|
+
/pet turn-status on|off|clear
|
|
106
|
+
/pet live-status on|off|clear
|
|
107
|
+
/pet clear-native
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Pets live under:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
~/.pi/agent/pi-sprite/pets/<id>/
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`turn-status` and `live-status` are both on by default. Turn status is final and replaces provisional live status when the agent turn ends. Live status waits five minutes into a long-running turn before showing a compact in-progress footer such as `🟣 running tests…`.
|
|
117
|
+
|
|
118
|
+
## Native image rendering
|
|
119
|
+
|
|
120
|
+
In Kitty/Ghostty-capable terminals, `pi-sprite` uses Kitty Unicode placeholders by default. Frames are uploaded quietly, while the visible sprite is rendered as placeholder text cells. That keeps tmux in charge of moving and clearing the pane grid, which avoids the ghosted native image placements caused by direct Kitty/Ghostty passthrough.
|
|
121
|
+
|
|
122
|
+
For tmux, allow passthrough:
|
|
123
|
+
|
|
124
|
+
```tmux
|
|
125
|
+
set -g allow-passthrough on
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
To force the ANSI half-block fallback:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
PI_SPRITE_NATIVE_IMAGES=0 pi
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Custom pets
|
|
135
|
+
|
|
136
|
+
The simplest local pet has one image per state:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
pet.json
|
|
140
|
+
idle.png
|
|
141
|
+
thinking.png
|
|
142
|
+
working.png
|
|
143
|
+
success.png
|
|
144
|
+
error.png
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Minimal `pet.json`:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"id": "boba",
|
|
152
|
+
"name": "Boba",
|
|
153
|
+
"sprites": {
|
|
154
|
+
"idle": "idle.png",
|
|
155
|
+
"thinking": "thinking.png",
|
|
156
|
+
"working": "working.png",
|
|
157
|
+
"success": "success.png",
|
|
158
|
+
"error": "error.png"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Optional `personality` text gives the selected pet a bounded voice in explicit `/btw` side conversations:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"id": "boba",
|
|
168
|
+
"name": "Boba",
|
|
169
|
+
"personality": "Warm, concise, lightly mischievous, and practical. Keep answers short.",
|
|
170
|
+
"sprites": {
|
|
171
|
+
"idle": "idle.png"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The personality is not injected into normal main-agent turns. It only guides `/btw` and `/btw:ask` answers.
|
|
177
|
+
|
|
178
|
+
Import and select a local pet folder:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
/pet import /path/to/pet-folder
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Codex/Petdex `pet.json + spritesheet.webp` compatibility is also supported. `pi-sprite` cycles multi-frame spritesheets and infers standard Petdex 8x9 atlases for `spritesheet.*`.
|
|
185
|
+
|
|
186
|
+
### Author a sprite effectively
|
|
187
|
+
|
|
188
|
+
This package ships the `pi-sprite-authoring` skill. Use it when you want an agent to turn references, generated art, or hand-drawn frames into an importable pet without losing character consistency across states.
|
|
189
|
+
|
|
190
|
+
Start the guided flow from Pi:
|
|
191
|
+
|
|
192
|
+
```text
|
|
193
|
+
/pet create tiny desk cat with cozy pixel-art vibes
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
You can also invoke the skill directly:
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
/skill:pi-sprite-authoring
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The recommended authoring loop is:
|
|
203
|
+
|
|
204
|
+
1. Write a short character brief and gather any local references.
|
|
205
|
+
2. Pick a direction card before generating images.
|
|
206
|
+
3. Create or choose one canonical `idle` image as the identity anchor.
|
|
207
|
+
4. Generate `thinking`, `working`, `success`, and `error` from that anchor.
|
|
208
|
+
5. Review all states for shared silhouette, face, palette, outline, canvas size, and scale.
|
|
209
|
+
6. Add optional simple motion strips only after the static states work.
|
|
210
|
+
7. Add bounded BTW-only `personality` metadata if the pet should affect explicit side replies.
|
|
211
|
+
8. Import the expanded folder with `/pet import <path>`.
|
|
212
|
+
|
|
213
|
+
Create a starter folder:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
node skills/pi-sprite-authoring/scripts/create-pet-template.mjs --id desk-cat --name "Desk Cat" --out /tmp/desk-cat-sprite
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Add bounded BTW-only personality metadata when desired:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
node skills/pi-sprite-authoring/scripts/create-pet-template.mjs \
|
|
223
|
+
--id desk-cat \
|
|
224
|
+
--name "Desk Cat" \
|
|
225
|
+
--personality "Warm, concise, lightly mischievous, and practical. Keep BTW answers short." \
|
|
226
|
+
--out /tmp/desk-cat-sprite
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Third-party reference sprites should stay local unless their licenses are verified. This helper downloads Petdex examples into a gitignored folder with provenance notes:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
node skills/pi-sprite-authoring/scripts/download-petdex-examples.mjs --limit 12 --out /tmp/petdex-downloads
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
For the full workflow, read the hosted [Sprite Authoring Guide](https://safurrier.github.io/pi-sprite/tutorials/authoring-sprites/). For a deterministic release-demo pet, see the [WendyBot3000 demo guide](https://safurrier.github.io/pi-sprite/tutorials/wendybot3000-demo/) and the repo-only [demo source](https://github.com/safurrier/pi-sprite/tree/main/demos/wendybot3000).
|
|
236
|
+
|
|
237
|
+
## `/context`
|
|
238
|
+
|
|
239
|
+
```text
|
|
240
|
+
/context
|
|
241
|
+
/context all
|
|
242
|
+
/sprite:context
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
`/context` opens a TUI overlay with the active model, context window, token total, estimated category breakdown, and remaining free space. `/sprite:context` is the package-specific alias for setups that already have another `/context` command.
|
|
246
|
+
|
|
247
|
+
## `/recap`
|
|
248
|
+
|
|
249
|
+
```text
|
|
250
|
+
/recap
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`/recap` generates a short executive-summary recap near the sprite:
|
|
254
|
+
|
|
255
|
+
```text
|
|
256
|
+
TL;DR: ...
|
|
257
|
+
Recent work: ...
|
|
258
|
+
Current status: ...
|
|
259
|
+
Next: ...
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Recap generation first uses an isolated, no-tools Pi side session with the current model, so it does not add messages to the main thread. Direct API-key completion is only a fallback. Use arrow keys, `j/k`, `space/d`, or `u` to scroll longer recaps.
|
|
263
|
+
|
|
264
|
+
## `/btw`
|
|
265
|
+
|
|
266
|
+
```text
|
|
267
|
+
/btw <message>
|
|
268
|
+
/btw
|
|
269
|
+
/btw:ask <question>
|
|
270
|
+
/btw:new [message]
|
|
271
|
+
/btw:clear
|
|
272
|
+
/btw:recap
|
|
273
|
+
/btw recap
|
|
274
|
+
/btw:inject
|
|
275
|
+
/btw:summarize
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`/btw` is a continuing side conversation outside the main thread. Use `/btw <message>` for follow-ups and `/btw` to reopen the current side thread. Use `/btw:ask <question>` for a one-off aside that does not append to the thread. Use `/btw:recap` or `/btw recap` to generate the normal session recap inside the BTW thread.
|
|
279
|
+
|
|
280
|
+
Answers appear in an interactive speech bubble that points toward the sprite. Nothing is injected back into the main conversation unless you explicitly run `/btw:inject` or `/btw:summarize`.
|
|
281
|
+
|
|
282
|
+
## Documentation
|
|
283
|
+
|
|
284
|
+
Start with the hosted docs; these links work from GitHub, npm, and installed package readers:
|
|
285
|
+
|
|
286
|
+
- [Docs home](https://safurrier.github.io/pi-sprite/) for the user and contributor index
|
|
287
|
+
- [Sprite Authoring Guide](https://safurrier.github.io/pi-sprite/tutorials/authoring-sprites/) for custom pet authoring
|
|
288
|
+
- [Configuration Reference](https://safurrier.github.io/pi-sprite/reference/configuration/) for default pet state and sprite home setup
|
|
289
|
+
|
|
290
|
+
From a source checkout, durable docs live under `docs/` and are published with MkDocs Material through GitHub Pages. Build them locally with:
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
uvx --with mkdocs-material mkdocs build --strict
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Development
|
|
297
|
+
|
|
298
|
+
Install dependencies:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
mise run setup
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Run the usual local gate:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
mise run check
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Run the full verification path, including e2e smoke helpers:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
mise run verify
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Release publishing uses the hosted [Release Checklist](https://safurrier.github.io/pi-sprite/reference/release/).
|
|
317
|
+
|
|
318
|
+
Equivalent npm commands:
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
npm run check
|
|
322
|
+
npm run test:e2e
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Optional e2e variants:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
PI_SPRITE_E2E_TUI=1 npm run test:e2e
|
|
329
|
+
PI_SPRITE_E2E_MODEL=1 npm run test:e2e
|
|
330
|
+
node tests/e2e/package-smoke.mjs --isolated
|
|
331
|
+
node tests/e2e/package-smoke.mjs --full-config
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
TUI smoke artifacts are written under:
|
|
335
|
+
|
|
336
|
+
```text
|
|
337
|
+
artifacts/e2e/
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Non-features
|
|
341
|
+
|
|
342
|
+
`pi-sprite` deliberately does not include:
|
|
343
|
+
|
|
344
|
+
- Electron or native floating windows
|
|
345
|
+
- voice, TTS, sounds, songs, or ambient weather
|
|
346
|
+
- hunger, feeding, bonding, XP, accessories, treats, or pet economy
|
|
347
|
+
- autonomous pet commentary or main-thread personality injection
|
|
348
|
+
- 3D/raymarched rendering
|
|
349
|
+
- large always-visible dashboards
|
|
350
|
+
|
|
351
|
+
## Attribution
|
|
352
|
+
|
|
353
|
+
`pi-sprite` began as a slimmed derivative of [`djdiptayan1/pi-pokepet`](https://github.com/djdiptayan1/pi-pokepet), licensed under MIT. See `NOTICE.md` and `LICENSE`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# pi-sprite examples
|
|
2
|
+
|
|
3
|
+
## Custom pet template
|
|
4
|
+
|
|
5
|
+
`custom-pets/wumpus-template/` contains a ready-to-fill `pet.json` for a Wumpus sprite set.
|
|
6
|
+
|
|
7
|
+
## Petdex reference downloads
|
|
8
|
+
|
|
9
|
+
Do not commit third-party sprite downloads without license review. To fetch temporary local references:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
node skills/pi-sprite-authoring/scripts/download-petdex-examples.mjs --limit 12 --out examples/petdex-downloads
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The download directory is gitignored and includes provenance notes.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Wumpus custom pet template
|
|
2
|
+
|
|
3
|
+
Add transparent pixel-art images next to `pet.json`:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
idle.png
|
|
7
|
+
thinking.png
|
|
8
|
+
working.png
|
|
9
|
+
success.png
|
|
10
|
+
error.png
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then import in Pi:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
/pet import examples/custom-pets/wumpus-template
|
|
17
|
+
/pet choose wumpus
|
|
18
|
+
/pet show
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
See `skills/pi-sprite-authoring/references/wumpus-sprite-prompts.md` for image-generation prompts.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "wumpus",
|
|
3
|
+
"name": "Wumpus",
|
|
4
|
+
"author": "Alex",
|
|
5
|
+
"description": "A tiny Wumpus companion for pi-sprite.",
|
|
6
|
+
"personality": "Warm, concise, lightly mischievous, and practical. Keep BTW answers short.",
|
|
7
|
+
"sprites": {
|
|
8
|
+
"idle": "idle.png",
|
|
9
|
+
"thinking": "thinking.png",
|
|
10
|
+
"working": "working.png",
|
|
11
|
+
"success": "success.png",
|
|
12
|
+
"error": "error.png"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { registerPiSpriteContextFilter } from "../src/agent/session-entries.ts";
|
|
3
|
+
import { registerBtwCommands } from "../src/btw/index.ts";
|
|
4
|
+
import { registerContextCommand } from "../src/context/index.ts";
|
|
5
|
+
import { registerRecapCommand } from "../src/recap/index.ts";
|
|
6
|
+
import { classifyLiveTurnStatus } from "../src/sprite/live-status.ts";
|
|
7
|
+
import { createSpriteRuntime } from "../src/sprite/runtime.ts";
|
|
8
|
+
import { classifyTurnStatus } from "../src/sprite/turn-status.ts";
|
|
9
|
+
|
|
10
|
+
export const LIVE_STATUS_INTERVAL_MS = 5 * 60 * 1000;
|
|
11
|
+
|
|
12
|
+
export default function piSpriteExtension(pi: ExtensionAPI) {
|
|
13
|
+
registerPiSpriteContextFilter(pi);
|
|
14
|
+
const sprite = createSpriteRuntime();
|
|
15
|
+
let turnStatusRun = 0;
|
|
16
|
+
let liveStatusTimer: ReturnType<typeof setTimeout> | undefined;
|
|
17
|
+
let liveStatusInFlight = false;
|
|
18
|
+
|
|
19
|
+
function clearLiveStatusTimer(): void {
|
|
20
|
+
if (liveStatusTimer) clearTimeout(liveStatusTimer);
|
|
21
|
+
liveStatusTimer = undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function scheduleLiveStatus(ctx: ExtensionContext, run: number): void {
|
|
25
|
+
clearLiveStatusTimer();
|
|
26
|
+
if (!sprite.isLiveStatusEnabled()) return;
|
|
27
|
+
liveStatusTimer = setTimeout(() => {
|
|
28
|
+
void updateLiveStatus(ctx, run);
|
|
29
|
+
}, LIVE_STATUS_INTERVAL_MS);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function updateLiveStatus(ctx: ExtensionContext, run: number): Promise<void> {
|
|
33
|
+
liveStatusTimer = undefined;
|
|
34
|
+
if (run !== turnStatusRun || !sprite.isLiveStatusEnabled()) return;
|
|
35
|
+
if (liveStatusInFlight) return scheduleLiveStatus(ctx, run);
|
|
36
|
+
liveStatusInFlight = true;
|
|
37
|
+
const liveGeneration = sprite.setLiveStatusPending();
|
|
38
|
+
let status: Awaited<ReturnType<typeof classifyLiveTurnStatus>>;
|
|
39
|
+
try {
|
|
40
|
+
status = await classifyLiveTurnStatus(ctx);
|
|
41
|
+
} catch {
|
|
42
|
+
status = undefined;
|
|
43
|
+
} finally {
|
|
44
|
+
liveStatusInFlight = false;
|
|
45
|
+
}
|
|
46
|
+
if (run === turnStatusRun) sprite.setLiveStatus(status, liveGeneration);
|
|
47
|
+
if (run === turnStatusRun) scheduleLiveStatus(ctx, run);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pi.on("session_start", async (_event: unknown, ctx: ExtensionContext) => {
|
|
51
|
+
await sprite.start(ctx);
|
|
52
|
+
});
|
|
53
|
+
pi.on("agent_start", async (_event: unknown, ctx: ExtensionContext) => {
|
|
54
|
+
const run = ++turnStatusRun;
|
|
55
|
+
clearLiveStatusTimer();
|
|
56
|
+
sprite.clearTurnStatus();
|
|
57
|
+
sprite.clearLiveStatus();
|
|
58
|
+
sprite.setState("thinking");
|
|
59
|
+
scheduleLiveStatus(ctx, run);
|
|
60
|
+
});
|
|
61
|
+
pi.on("message_update", async (event: unknown) => {
|
|
62
|
+
const type = (event as { assistantMessageEvent?: { type?: string } })?.assistantMessageEvent?.type ?? "";
|
|
63
|
+
if (/^thinking/u.test(type)) sprite.setState("thinking");
|
|
64
|
+
else if (/^toolcall/u.test(type)) sprite.setState("working");
|
|
65
|
+
});
|
|
66
|
+
pi.on("tool_execution_start", async () => sprite.setState("working"));
|
|
67
|
+
pi.on("tool_result", async (event: unknown) => {
|
|
68
|
+
const e = event as { isError?: boolean; result?: { isError?: boolean }; error?: unknown };
|
|
69
|
+
if (e?.isError || e?.result?.isError || e?.error) sprite.setState("error", { resetMs: 2500 });
|
|
70
|
+
});
|
|
71
|
+
pi.on("agent_end", async (event: unknown, ctx: ExtensionContext) => {
|
|
72
|
+
const run = ++turnStatusRun;
|
|
73
|
+
clearLiveStatusTimer();
|
|
74
|
+
sprite.clearLiveStatus();
|
|
75
|
+
sprite.setState("success", { resetMs: 1800 });
|
|
76
|
+
if (!sprite.isTurnStatusEnabled()) return;
|
|
77
|
+
sprite.setTurnStatusPending();
|
|
78
|
+
const status = await classifyTurnStatus(ctx, (event as { messages?: unknown[] }).messages ?? []);
|
|
79
|
+
if (run === turnStatusRun) sprite.setTurnStatus(status);
|
|
80
|
+
});
|
|
81
|
+
pi.on("session_shutdown", async () => {
|
|
82
|
+
turnStatusRun++;
|
|
83
|
+
clearLiveStatusTimer();
|
|
84
|
+
sprite.clearLiveStatus();
|
|
85
|
+
sprite.shutdown();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
sprite.registerCommands(pi);
|
|
89
|
+
registerContextCommand(pi);
|
|
90
|
+
registerRecapCommand(pi, {
|
|
91
|
+
setState: (state, options) => sprite.setState(state, options),
|
|
92
|
+
setRecapStatus: (status) => sprite.setRecapStatus(status),
|
|
93
|
+
getBubblePlacement: () => sprite.getBubblePlacement(),
|
|
94
|
+
getSpriteName: () => sprite.getSpriteName(),
|
|
95
|
+
});
|
|
96
|
+
registerBtwCommands(pi, {
|
|
97
|
+
setState: (state, options) => sprite.setState(state, options),
|
|
98
|
+
setBtwStatus: (status, count) => sprite.setBtwStatus(status, count),
|
|
99
|
+
setRecapStatus: (status) => sprite.setRecapStatus(status),
|
|
100
|
+
getBubblePlacement: () => sprite.getBubblePlacement(),
|
|
101
|
+
getSpriteName: () => sprite.getSpriteName(),
|
|
102
|
+
getSpritePersonality: () => sprite.getSpritePersonality(),
|
|
103
|
+
});
|
|
104
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-sprite",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A slim Codex-style sprite companion for Pi with /context, /recap, and /btw.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"pi",
|
|
8
|
+
"pi-extension",
|
|
9
|
+
"sprite",
|
|
10
|
+
"petdex",
|
|
11
|
+
"codex-pets",
|
|
12
|
+
"context",
|
|
13
|
+
"recap",
|
|
14
|
+
"btw"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Alex Furrier",
|
|
18
|
+
"homepage": "https://github.com/safurrier/pi-sprite#readme",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/safurrier/pi-sprite.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/safurrier/pi-sprite/issues"
|
|
25
|
+
},
|
|
26
|
+
"pi": {
|
|
27
|
+
"extensions": [
|
|
28
|
+
"./extensions/index.ts"
|
|
29
|
+
],
|
|
30
|
+
"skills": [
|
|
31
|
+
"./skills"
|
|
32
|
+
],
|
|
33
|
+
"image": "https://safurrier.github.io/pi-sprite/assets/wendybot3000-demo.gif"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@earendil-works/pi-ai": "*",
|
|
37
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
38
|
+
"@earendil-works/pi-tui": "*"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"setup": "npm install",
|
|
42
|
+
"fmt": "biome check --write .",
|
|
43
|
+
"fmt:check": "biome check .",
|
|
44
|
+
"lint": "biome lint .",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"test": "mkdir -p test-results && node --test --import tsx tests/*.test.ts",
|
|
47
|
+
"test:e2e": "node tests/e2e/run-e2e.mjs",
|
|
48
|
+
"build": "npm pack --dry-run",
|
|
49
|
+
"check": "npm run fmt:check && npm run lint && npm run typecheck && npm test && npm run build",
|
|
50
|
+
"ci": "npm run check",
|
|
51
|
+
"verify": "npm run check && npm run test:e2e"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@biomejs/biome": "2.4.16",
|
|
55
|
+
"@earendil-works/pi-ai": "^0.80.3",
|
|
56
|
+
"@earendil-works/pi-coding-agent": "^0.80.3",
|
|
57
|
+
"@earendil-works/pi-tui": "^0.80.3",
|
|
58
|
+
"@types/adm-zip": "^0.5.8",
|
|
59
|
+
"@types/node": "^22.0.0",
|
|
60
|
+
"tsx": "^4.22.3",
|
|
61
|
+
"typescript": "^5.9.0"
|
|
62
|
+
},
|
|
63
|
+
"files": [
|
|
64
|
+
"extensions",
|
|
65
|
+
"src",
|
|
66
|
+
"skills",
|
|
67
|
+
"examples",
|
|
68
|
+
"README.md",
|
|
69
|
+
"LICENSE",
|
|
70
|
+
"NOTICE.md",
|
|
71
|
+
"CHANGELOG.md"
|
|
72
|
+
],
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"adm-zip": "^0.5.17",
|
|
75
|
+
"sharp": "^0.33.5"
|
|
76
|
+
}
|
|
77
|
+
}
|