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.
Files changed (54) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE.md +7 -0
  4. package/README.md +353 -0
  5. package/examples/README.md +15 -0
  6. package/examples/custom-pets/wumpus-template/README.md +21 -0
  7. package/examples/custom-pets/wumpus-template/pet.json +14 -0
  8. package/examples/petdex-downloads/.gitkeep +0 -0
  9. package/extensions/index.ts +104 -0
  10. package/package.json +77 -0
  11. package/skills/pi-sprite-authoring/SKILL.md +330 -0
  12. package/skills/pi-sprite-authoring/assets/wumpus-template/pet.json +14 -0
  13. package/skills/pi-sprite-authoring/references/character-cohesion-review.md +65 -0
  14. package/skills/pi-sprite-authoring/references/gpt-image-sprite-workflow.md +181 -0
  15. package/skills/pi-sprite-authoring/references/petdex-reference-to-custom-pet.md +155 -0
  16. package/skills/pi-sprite-authoring/references/wumpus-sprite-prompts.md +54 -0
  17. package/skills/pi-sprite-authoring/scripts/assemble_sprite_strip.py +98 -0
  18. package/skills/pi-sprite-authoring/scripts/create-pet-template.mjs +51 -0
  19. package/skills/pi-sprite-authoring/scripts/create_motion_strip.py +110 -0
  20. package/skills/pi-sprite-authoring/scripts/download-petdex-examples.mjs +79 -0
  21. package/skills/pi-sprite-authoring/scripts/openai_sprite_image.py +223 -0
  22. package/skills/pi-sprite-authoring/scripts/remove_sprite_background.py +207 -0
  23. package/src/agent/session-entries.ts +28 -0
  24. package/src/agent/side-completion.ts +94 -0
  25. package/src/agent/side-session-text.ts +20 -0
  26. package/src/agent/side-session-types.ts +12 -0
  27. package/src/agent/side-session.ts +107 -0
  28. package/src/btw/completion.ts +15 -0
  29. package/src/btw/format.ts +30 -0
  30. package/src/btw/index.ts +306 -0
  31. package/src/btw/prompt.ts +35 -0
  32. package/src/btw/recap.ts +56 -0
  33. package/src/btw/session.ts +37 -0
  34. package/src/btw/thread-store.ts +37 -0
  35. package/src/context/index.ts +343 -0
  36. package/src/recap/conversation.ts +22 -0
  37. package/src/recap/direct.ts +15 -0
  38. package/src/recap/format.ts +25 -0
  39. package/src/recap/generation.ts +58 -0
  40. package/src/recap/index.ts +86 -0
  41. package/src/sprite/commands.ts +205 -0
  42. package/src/sprite/download.ts +75 -0
  43. package/src/sprite/kitty-placeholder.ts +441 -0
  44. package/src/sprite/live-status-format.ts +67 -0
  45. package/src/sprite/live-status.ts +48 -0
  46. package/src/sprite/loader.ts +134 -0
  47. package/src/sprite/manifest.ts +87 -0
  48. package/src/sprite/paths.ts +8 -0
  49. package/src/sprite/petdex.ts +133 -0
  50. package/src/sprite/renderer.ts +491 -0
  51. package/src/sprite/runtime.ts +524 -0
  52. package/src/sprite/turn-status-format.ts +112 -0
  53. package/src/sprite/turn-status.ts +88 -0
  54. 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
+ ![pi-sprite WendyBot3000 demo](https://safurrier.github.io/pi-sprite/assets/wendybot3000-demo.gif)
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
+ }