@pi-extensions/pi-pacman 0.1.0 → 0.1.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 +64 -45
- package/index.ts +150 -48
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -1,68 +1,87 @@
|
|
|
1
|
-
# pi-pacman
|
|
1
|
+
# @pi-extensions/pi-pacman
|
|
2
2
|
|
|
3
|
-
Pac-Man working indicator for [pi](https://github.com/earendil-works/pi).
|
|
3
|
+
Pac-Man **working indicator** for [pi](https://github.com/earendil-works/pi): replaces the streaming spinner with pellet runs, ghost chases, arcade tunnels, and fruit bonuses.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Part of the [pi-extensions](https://github.com/smarzban/pi-extensions) monorepo.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
- **Drop-in working indicator**: uses `setWorkingIndicator` / `setWorkingMessage` (normal streaming only)
|
|
10
|
+
- **Five looks**: full-width `classic` & `chase`, plus fixed-width `mini`, `arcade`, `fruit` (default 10 cells, configurable)
|
|
11
|
+
- **Rotate mode**: cycle short looks every agent message (`/pacman rotate`)
|
|
12
|
+
- **Random working blurbs**: arcade + AI/token-flavored lines each run (or lock your own)
|
|
13
|
+
- **Remembers your choice**: look, rotate, cells, and custom message in `~/.pi/agent/pacman-thinking.json`
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
8
16
|
|
|
9
17
|
```bash
|
|
10
|
-
# from npm (after publish)
|
|
11
18
|
pi install npm:@pi-extensions/pi-pacman
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Restart pi (or start a new session), send a message, and you should see a yellow `ᗧ` chomping pellets next to the working line:
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
```text
|
|
24
|
+
ᗧ······ waka waka...
|
|
15
25
|
```
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
Default look is **classic** (full-width pellet run). Footer status shows `ᗧ classic` while the extension is active.
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
The indicator only appears while the agent is **streaming a normal response**, not during compaction/retry loaders.
|
|
20
30
|
|
|
21
|
-
|
|
22
|
-
# after version bump on main:
|
|
23
|
-
git tag v0.1.0 && git push origin v0.1.0
|
|
24
|
-
```
|
|
31
|
+
## Install
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
| Method | Loads | Command |
|
|
34
|
+
|--------|-------|---------|
|
|
35
|
+
| **npm** (recommended) | This package only | `pi install npm:@pi-extensions/pi-pacman` |
|
|
36
|
+
| **local path** | This package only | `pi install /absolute/path/to/pi-extensions/packages/pi-pacman` |
|
|
37
|
+
| **git** (whole monorepo) | All packages in the repo | `pi install git:github.com/smarzban/pi-extensions` |
|
|
27
38
|
|
|
28
|
-
|
|
39
|
+
More detail: [docs/install](../../docs/install/README.md) (one package vs whole monorepo).
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
pi install npm:@pi-extensions/pi-pacman
|
|
32
|
-
pi install npm:@pi-extensions/pi-pacman@0.1.0
|
|
33
|
-
```
|
|
41
|
+
## Usage
|
|
34
42
|
|
|
35
|
-
|
|
43
|
+
```text
|
|
44
|
+
/pacman list
|
|
45
|
+
/pacman chase
|
|
46
|
+
/pacman rotate
|
|
47
|
+
/pacman cells 12
|
|
48
|
+
/pacman message chomping tokens...
|
|
49
|
+
/pacman off
|
|
50
|
+
```
|
|
36
51
|
|
|
37
|
-
| Command |
|
|
52
|
+
| Command | Result |
|
|
38
53
|
|---------|--------|
|
|
39
|
-
| `/pacman` |
|
|
40
|
-
| `/pacman list` | Catalog
|
|
41
|
-
| `/pacman <look>` | Lock
|
|
42
|
-
| `/pacman rotate` | Cycle
|
|
43
|
-
| `/pacman
|
|
44
|
-
| `/pacman message
|
|
45
|
-
| `/pacman
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
| `/pacman` | Current look, rotate, cells, message, strip width |
|
|
55
|
+
| `/pacman list` | Catalog under the editor |
|
|
56
|
+
| `/pacman <look>` | Lock a look (stops rotate) |
|
|
57
|
+
| `/pacman rotate` | Cycle short looks every message |
|
|
58
|
+
| `/pacman cells [n]` | Fixed-look width (default 10, range 4–40) |
|
|
59
|
+
| `/pacman message …` | Lock custom working text (empty = auto-random) |
|
|
60
|
+
| `/pacman off` | Hide indicator |
|
|
61
|
+
|
|
62
|
+
Full reference: [docs/usage/commands.md](../../docs/usage/commands.md).
|
|
63
|
+
|
|
64
|
+
## Looks
|
|
65
|
+
|
|
66
|
+
| Look | Notes |
|
|
67
|
+
|------|--------|
|
|
52
68
|
| `classic` | Full-width pellet run (default) |
|
|
53
|
-
| `chase` | Full-width Blinky hunt → revenge |
|
|
54
|
-
| `mini` |
|
|
55
|
-
| `arcade` | 7-cell maze tunnel |
|
|
56
|
-
| `fruit` | 7-cell cherry bonus |
|
|
69
|
+
| `chase` | Full-width Blinky hunt → power pellet → revenge |
|
|
70
|
+
| `mini` / `arcade` / `fruit` | Fixed-width animations (**in rotate**); width via `cells` config |
|
|
57
71
|
|
|
58
|
-
|
|
72
|
+
Details: [docs/usage/looks.md](../../docs/usage/looks.md). Persistence: [docs/usage/persistence.md](../../docs/usage/persistence.md).
|
|
59
73
|
|
|
60
|
-
##
|
|
74
|
+
## Documentation
|
|
61
75
|
|
|
62
|
-
|
|
76
|
+
| Doc | What |
|
|
77
|
+
|-----|------|
|
|
78
|
+
| [Quickstart](../../docs/quickstart.md) | Fastest path to first chomp |
|
|
79
|
+
| [Commands](../../docs/usage/commands.md) | Every `/pacman` subcommand |
|
|
80
|
+
| [Looks](../../docs/usage/looks.md) | Frame / width notes |
|
|
81
|
+
| [Persistence](../../docs/usage/persistence.md) | State file fields |
|
|
82
|
+
| [Architecture](../../docs/architecture.md) | How the extension hooks pi |
|
|
83
|
+
| [Releases](../../docs/releases.md) | Maintainer publish flow |
|
|
63
84
|
|
|
64
|
-
##
|
|
85
|
+
## License
|
|
65
86
|
|
|
66
|
-
|
|
67
|
-
pi --extension ./index.ts
|
|
68
|
-
```
|
|
87
|
+
[MIT](LICENSE)
|
package/index.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Replaces pi's streaming working indicator with a Pac-Man animation.
|
|
5
5
|
*
|
|
6
6
|
* Install:
|
|
7
|
-
* pi install /path/to/pi-
|
|
7
|
+
* pi install /path/to/pi-extensions/packages/pi-pacman
|
|
8
8
|
* pi --extension ./index.ts
|
|
9
9
|
*
|
|
10
10
|
* Commands:
|
|
@@ -33,16 +33,19 @@ const PELLET = "\x1b[38;2;255;184;174m";
|
|
|
33
33
|
const POWER = "\x1b[38;2;255;184;255m";
|
|
34
34
|
const WALL = "\x1b[38;2;33;33;255m";
|
|
35
35
|
const CHERRY = "\x1b[38;2;255;0;0m";
|
|
36
|
-
const SCORE = "\x1b[38;2;255;255;255m";
|
|
37
36
|
const GHOSTS = {
|
|
38
37
|
blinky: "\x1b[38;2;255;0;0m",
|
|
39
38
|
scared: "\x1b[38;2;33;33;255m",
|
|
40
39
|
} as const;
|
|
41
40
|
|
|
42
|
-
/** Full-width looks (classic / chase)
|
|
41
|
+
/** Full-width looks (classic / chase): snappier chomp. */
|
|
43
42
|
const FRAME_MS_FULL = 80;
|
|
44
|
-
/** Fixed
|
|
43
|
+
/** Fixed-width looks (mini / arcade / fruit): a bit slower. */
|
|
45
44
|
const FRAME_MS_FIXED = 110;
|
|
45
|
+
/** Default strip length for mini / arcade / fruit (cells). Overridable via config. */
|
|
46
|
+
const DEFAULT_CELLS = 10;
|
|
47
|
+
const CELLS_MIN = 4;
|
|
48
|
+
const CELLS_MAX = 40;
|
|
46
49
|
|
|
47
50
|
type Facing = "right" | "left";
|
|
48
51
|
|
|
@@ -127,13 +130,15 @@ function runTrack(track: number): string[] {
|
|
|
127
130
|
|
|
128
131
|
// ── looks ────────────────────────────────────────────────────────────
|
|
129
132
|
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
function clampCells(n: number): number {
|
|
134
|
+
if (!Number.isFinite(n)) return DEFAULT_CELLS;
|
|
135
|
+
return Math.max(CELLS_MIN, Math.min(CELLS_MAX, Math.round(n)));
|
|
136
|
+
}
|
|
132
137
|
|
|
133
138
|
const LOOKS: Look[] = [
|
|
134
139
|
{
|
|
135
140
|
id: "classic",
|
|
136
|
-
blurb: "Full-width pellet run
|
|
141
|
+
blurb: "Full-width pellet run, dots span the terminal",
|
|
137
142
|
message: "waka waka...",
|
|
138
143
|
fullWidth: true,
|
|
139
144
|
frames: (track) => runTrack(track),
|
|
@@ -193,16 +198,16 @@ const LOOKS: Look[] = [
|
|
|
193
198
|
},
|
|
194
199
|
{
|
|
195
200
|
id: "mini",
|
|
196
|
-
blurb: "
|
|
201
|
+
blurb: "fixed-width pellet run",
|
|
197
202
|
message: "chomp chomp...",
|
|
198
|
-
frames: () => runTrack(
|
|
203
|
+
frames: (track) => runTrack(track),
|
|
199
204
|
},
|
|
200
205
|
{
|
|
201
206
|
id: "arcade",
|
|
202
|
-
blurb: "
|
|
207
|
+
blurb: "fixed-width maze tunnel with blue walls",
|
|
203
208
|
message: "insert coin...",
|
|
204
|
-
frames: () => {
|
|
205
|
-
const last =
|
|
209
|
+
frames: (track) => {
|
|
210
|
+
const last = track - 1;
|
|
206
211
|
const frames: string[] = [];
|
|
207
212
|
const wall = paint(WALL, "│");
|
|
208
213
|
for (let i = 0; i <= last; i++) {
|
|
@@ -216,7 +221,7 @@ const LOOKS: Look[] = [
|
|
|
216
221
|
frames.push(`${wall}${mid}${wall}`);
|
|
217
222
|
}
|
|
218
223
|
}
|
|
219
|
-
frames.push(`${wall}${paint(WALL, "≈".repeat(
|
|
224
|
+
frames.push(`${wall}${paint(WALL, "≈".repeat(track))}${wall}`);
|
|
220
225
|
for (let i = last; i >= 0; i--) {
|
|
221
226
|
for (const open of [true, false]) {
|
|
222
227
|
let mid = "";
|
|
@@ -233,10 +238,10 @@ const LOOKS: Look[] = [
|
|
|
233
238
|
},
|
|
234
239
|
{
|
|
235
240
|
id: "fruit",
|
|
236
|
-
blurb: "
|
|
241
|
+
blurb: "fixed-width cherry bonus run",
|
|
237
242
|
message: "fruit bonus...",
|
|
238
|
-
frames: () => {
|
|
239
|
-
const last =
|
|
243
|
+
frames: (track) => {
|
|
244
|
+
const last = track - 1;
|
|
240
245
|
const frames: string[] = [];
|
|
241
246
|
|
|
242
247
|
// Rightward: cherry waits at the far end
|
|
@@ -253,8 +258,6 @@ const LOOKS: Look[] = [
|
|
|
253
258
|
frames.push(row);
|
|
254
259
|
}
|
|
255
260
|
}
|
|
256
|
-
frames.push(`${" ".repeat(Math.max(0, FIXED_CELLS - 3))}${paint(SCORE, "100")}`);
|
|
257
|
-
frames.push(`${" ".repeat(Math.max(0, FIXED_CELLS - 3))}${paint(SCORE, "100")}`);
|
|
258
261
|
|
|
259
262
|
// Leftward: cherry waits at the start
|
|
260
263
|
for (let i = last; i >= 0; i--) {
|
|
@@ -270,26 +273,66 @@ const LOOKS: Look[] = [
|
|
|
270
273
|
frames.push(row);
|
|
271
274
|
}
|
|
272
275
|
}
|
|
273
|
-
frames.push(`${paint(SCORE, "300")}${" ".repeat(Math.max(0, FIXED_CELLS - 3))}`);
|
|
274
|
-
frames.push(`${paint(SCORE, "300")}${" ".repeat(Math.max(0, FIXED_CELLS - 3))}`);
|
|
275
276
|
return frames;
|
|
276
277
|
},
|
|
277
278
|
},
|
|
278
279
|
];
|
|
279
280
|
|
|
280
281
|
const LOOK_BY_ID = new Map(LOOKS.map((l) => [l.id, l]));
|
|
281
|
-
/**
|
|
282
|
-
const ROTATE_LOOKS = LOOKS.map((l) => l.id);
|
|
282
|
+
/** Rotate only cycles short looks; full-width classic/chase stay opt-in via /pacman <look>. */
|
|
283
|
+
const ROTATE_LOOKS = LOOKS.filter((l) => !l.fullWidth).map((l) => l.id);
|
|
284
|
+
|
|
285
|
+
/** Fun working-line blurbs; one is picked at random each agent turn (unless custom message). */
|
|
286
|
+
const MESSAGE_POOL = [
|
|
287
|
+
"waka waka...",
|
|
288
|
+
"chomp chomp...",
|
|
289
|
+
"nom nom nom...",
|
|
290
|
+
"pellet run...",
|
|
291
|
+
"extra life...",
|
|
292
|
+
"maze clear...",
|
|
293
|
+
"level up...",
|
|
294
|
+
"thinking in 8-bit...",
|
|
295
|
+
"chomping tokens...",
|
|
296
|
+
"munching context...",
|
|
297
|
+
"dot duty...",
|
|
298
|
+
"blinky inbound...",
|
|
299
|
+
"still hungry...",
|
|
300
|
+
"just one more pellet...",
|
|
301
|
+
"pac-ing thoughts...",
|
|
302
|
+
"busy in the maze...",
|
|
303
|
+
"sampling next token...",
|
|
304
|
+
"warming up the weights...",
|
|
305
|
+
"attention is all you need...",
|
|
306
|
+
"prompt cache go brrr...",
|
|
307
|
+
"context window snacking...",
|
|
308
|
+
"tool call pending...",
|
|
309
|
+
"reasoning tokens burning...",
|
|
310
|
+
"embedding the maze...",
|
|
311
|
+
"softmaxing vibes...",
|
|
312
|
+
"gradient descending...",
|
|
313
|
+
"hallucinating less... hopefully...",
|
|
314
|
+
"compacting the snack tray...",
|
|
315
|
+
"waiting on the model...",
|
|
316
|
+
"streaming tokens...",
|
|
317
|
+
"fine-tuning my appetite...",
|
|
318
|
+
"loading more context...",
|
|
319
|
+
"thinking tokens go chomp...",
|
|
320
|
+
"RAG-ing the pellets...",
|
|
321
|
+
"temperature set to spicy...",
|
|
322
|
+
"beam search for snacks...",
|
|
323
|
+
];
|
|
283
324
|
|
|
284
325
|
type Mode = string;
|
|
285
326
|
|
|
286
327
|
interface PersistedState {
|
|
287
328
|
mode?: string;
|
|
288
|
-
/** @deprecated old field
|
|
329
|
+
/** @deprecated old field, migrated to `rotate` */
|
|
289
330
|
mix?: string;
|
|
290
331
|
rotate?: boolean;
|
|
291
332
|
rotateIndex?: number;
|
|
292
333
|
customMessage?: string;
|
|
334
|
+
/** Fixed strip length for mini / arcade / fruit (cells). Default 10. */
|
|
335
|
+
cells?: number;
|
|
293
336
|
}
|
|
294
337
|
|
|
295
338
|
function statePath(): string {
|
|
@@ -312,20 +355,24 @@ function saveState(state: PersistedState): void {
|
|
|
312
355
|
mkdirSync(dirname(path), { recursive: true });
|
|
313
356
|
writeFileSync(path, `${JSON.stringify(state, null, "\t")}\n`, "utf-8");
|
|
314
357
|
} catch {
|
|
315
|
-
// Non-fatal
|
|
358
|
+
// Non-fatal: indicator still works without persistence
|
|
316
359
|
}
|
|
317
360
|
}
|
|
318
361
|
|
|
319
|
-
function resolveTrack(look: Look, message: string): number {
|
|
362
|
+
function resolveTrack(look: Look, message: string, cells: number): number {
|
|
320
363
|
if (look.fullWidth) return indicatorWidth(message);
|
|
321
|
-
return
|
|
364
|
+
return cells;
|
|
322
365
|
}
|
|
323
366
|
|
|
324
|
-
function getIndicator(
|
|
367
|
+
function getIndicator(
|
|
368
|
+
mode: Mode,
|
|
369
|
+
message: string,
|
|
370
|
+
cells: number,
|
|
371
|
+
): WorkingIndicatorOptions | undefined {
|
|
325
372
|
if (mode === "off") return { frames: [] };
|
|
326
373
|
const look = LOOK_BY_ID.get(mode);
|
|
327
374
|
if (!look) return undefined;
|
|
328
|
-
const track = resolveTrack(look, message);
|
|
375
|
+
const track = resolveTrack(look, message, cells);
|
|
329
376
|
const intervalMs = look.fullWidth ? FRAME_MS_FULL : FRAME_MS_FIXED;
|
|
330
377
|
return { frames: look.frames(track), intervalMs };
|
|
331
378
|
}
|
|
@@ -333,26 +380,31 @@ function getIndicator(mode: Mode, message: string): WorkingIndicatorOptions | un
|
|
|
333
380
|
function describeMode(mode: Mode): string {
|
|
334
381
|
if (mode === "off") return "hidden";
|
|
335
382
|
const look = LOOK_BY_ID.get(mode);
|
|
336
|
-
return look ? `${look.id}
|
|
383
|
+
return look ? `${look.id}: ${look.blurb}` : mode;
|
|
337
384
|
}
|
|
338
385
|
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
386
|
+
function pickRandomMessage(previous?: string): string {
|
|
387
|
+
const pool =
|
|
388
|
+
previous && MESSAGE_POOL.length > 1
|
|
389
|
+
? MESSAGE_POOL.filter((m) => m !== previous)
|
|
390
|
+
: MESSAGE_POOL;
|
|
391
|
+
return pool[Math.floor(Math.random() * pool.length)]!;
|
|
342
392
|
}
|
|
343
393
|
|
|
344
394
|
function listLooks(): string {
|
|
345
395
|
const lines = LOOKS.map((l) => {
|
|
346
396
|
const tag = l.fullWidth ? " (full width)" : "";
|
|
347
|
-
|
|
397
|
+
const rot = !l.fullWidth ? " · in rotate" : "";
|
|
398
|
+
return ` ${l.id.padEnd(8)} ${l.blurb}${tag}${rot}`;
|
|
348
399
|
});
|
|
349
400
|
return [
|
|
350
401
|
"Pac-Man looks:",
|
|
351
402
|
...lines,
|
|
352
403
|
"",
|
|
353
|
-
" rotate
|
|
404
|
+
" rotate Cycle short looks each message (mini/arcade/fruit)",
|
|
354
405
|
" off Hide indicator",
|
|
355
406
|
"",
|
|
407
|
+
"Working text is random each run (unless /pacman message …).",
|
|
356
408
|
"Pick a look name to lock it (stops rotate).",
|
|
357
409
|
].join("\n");
|
|
358
410
|
}
|
|
@@ -376,6 +428,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
376
428
|
typeof saved.customMessage === "string" && saved.customMessage.length > 0
|
|
377
429
|
? saved.customMessage
|
|
378
430
|
: undefined;
|
|
431
|
+
let cells =
|
|
432
|
+
typeof saved.cells === "number" ? clampCells(saved.cells) : DEFAULT_CELLS;
|
|
433
|
+
/** Last auto-picked blurb (not persisted); used to avoid immediate repeats. */
|
|
434
|
+
let autoMessage: string = pickRandomMessage();
|
|
379
435
|
let lastUi: ExtensionUIContext | undefined;
|
|
380
436
|
|
|
381
437
|
const persist = () => {
|
|
@@ -384,6 +440,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
384
440
|
rotate,
|
|
385
441
|
rotateIndex,
|
|
386
442
|
customMessage,
|
|
443
|
+
cells,
|
|
387
444
|
});
|
|
388
445
|
};
|
|
389
446
|
|
|
@@ -392,11 +449,22 @@ export default function (pi: ExtensionAPI) {
|
|
|
392
449
|
return `ᗧ ${mode}${rotate ? " ↻" : ""}`;
|
|
393
450
|
};
|
|
394
451
|
|
|
452
|
+
const workingMessage = (): string => {
|
|
453
|
+
if (mode === "off") return " ";
|
|
454
|
+
if (customMessage) return customMessage;
|
|
455
|
+
return autoMessage;
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const refreshAutoMessage = () => {
|
|
459
|
+
if (customMessage) return;
|
|
460
|
+
autoMessage = pickRandomMessage(autoMessage);
|
|
461
|
+
};
|
|
462
|
+
|
|
395
463
|
const apply = (ctx: ExtensionContext) => {
|
|
396
464
|
lastUi = ctx.ui;
|
|
397
|
-
const message =
|
|
398
|
-
ctx.ui.setWorkingIndicator(getIndicator(mode, message));
|
|
399
|
-
ctx.ui.setWorkingMessage(
|
|
465
|
+
const message = workingMessage();
|
|
466
|
+
ctx.ui.setWorkingIndicator(getIndicator(mode, message, cells));
|
|
467
|
+
ctx.ui.setWorkingMessage(message);
|
|
400
468
|
ctx.ui.setStatus("pacman-thinking", ctx.ui.theme.fg("dim", statusLabel()));
|
|
401
469
|
persist();
|
|
402
470
|
};
|
|
@@ -404,30 +472,40 @@ export default function (pi: ExtensionAPI) {
|
|
|
404
472
|
const isFullWidthMode = () => LOOK_BY_ID.get(mode)?.fullWidth === true;
|
|
405
473
|
|
|
406
474
|
const pickNextRotatedLook = () => {
|
|
475
|
+
if (ROTATE_LOOKS.length === 0) return;
|
|
407
476
|
mode = ROTATE_LOOKS[rotateIndex % ROTATE_LOOKS.length]!;
|
|
408
477
|
rotateIndex++;
|
|
409
478
|
};
|
|
410
479
|
|
|
411
480
|
const reapplyIfFullWidth = () => {
|
|
412
481
|
if (!lastUi || !isFullWidthMode()) return;
|
|
413
|
-
const message =
|
|
414
|
-
lastUi.setWorkingIndicator(getIndicator(mode, message));
|
|
482
|
+
const message = workingMessage();
|
|
483
|
+
lastUi.setWorkingIndicator(getIndicator(mode, message, cells));
|
|
415
484
|
};
|
|
416
485
|
|
|
417
486
|
process.stdout.on?.("resize", reapplyIfFullWidth);
|
|
418
487
|
|
|
419
488
|
pi.on("session_start", async (_event, ctx) => {
|
|
420
|
-
// Don't advance rotate here
|
|
489
|
+
// Don't advance rotate here; agent_start picks per message.
|
|
490
|
+
refreshAutoMessage();
|
|
421
491
|
apply(ctx);
|
|
422
492
|
});
|
|
423
493
|
|
|
424
494
|
pi.on("agent_start", async (_event, ctx) => {
|
|
495
|
+
// Fresh random blurb every run (unless user set /pacman message)
|
|
496
|
+
refreshAutoMessage();
|
|
425
497
|
if (rotate) {
|
|
426
498
|
pickNextRotatedLook();
|
|
427
499
|
apply(ctx);
|
|
428
500
|
return;
|
|
429
501
|
}
|
|
430
|
-
|
|
502
|
+
// Always re-apply so the new message (and full-width width) stick
|
|
503
|
+
apply(ctx);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
pi.on("session_shutdown", async () => {
|
|
507
|
+
// Drop the resize listener so reloads don't stack duplicates.
|
|
508
|
+
process.stdout.off?.("resize", reapplyIfFullWidth);
|
|
431
509
|
});
|
|
432
510
|
|
|
433
511
|
pi.registerCommand("pacman", {
|
|
@@ -435,15 +513,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
435
513
|
handler: async (args, ctx) => {
|
|
436
514
|
const raw = args.trim();
|
|
437
515
|
if (!raw) {
|
|
438
|
-
const msg =
|
|
516
|
+
const msg = workingMessage();
|
|
439
517
|
const extra = isFullWidthMode()
|
|
440
518
|
? ` · width ${indicatorWidth(msg)} cells`
|
|
441
519
|
: LOOK_BY_ID.has(mode)
|
|
442
|
-
? ` · width ${
|
|
520
|
+
? ` · width ${cells} cells`
|
|
443
521
|
: "";
|
|
444
|
-
const rotInfo = rotate ? " · rotate on" : "";
|
|
522
|
+
const rotInfo = rotate ? " · rotate on (short looks)" : "";
|
|
523
|
+
const msgMode = customMessage ? "custom" : "auto";
|
|
445
524
|
ctx.ui.notify(
|
|
446
|
-
`Pac-Man: ${describeMode(mode)}${rotInfo} · message: "${msg}"${extra}`,
|
|
525
|
+
`Pac-Man: ${describeMode(mode)}${rotInfo} · cells ${cells} · message (${msgMode}): "${msg}"${extra}`,
|
|
447
526
|
"info",
|
|
448
527
|
);
|
|
449
528
|
return;
|
|
@@ -470,24 +549,47 @@ export default function (pi: ExtensionAPI) {
|
|
|
470
549
|
if (head === "message") {
|
|
471
550
|
const text = rest.join(" ").trim();
|
|
472
551
|
customMessage = text.length > 0 ? text : undefined;
|
|
552
|
+
if (!customMessage) refreshAutoMessage();
|
|
473
553
|
apply(ctx);
|
|
474
554
|
ctx.ui.notify(
|
|
475
555
|
customMessage
|
|
476
|
-
? `Working message
|
|
477
|
-
:
|
|
556
|
+
? `Working message locked to: ${customMessage}`
|
|
557
|
+
: `Working message is auto again (now: "${autoMessage}")`,
|
|
478
558
|
"info",
|
|
479
559
|
);
|
|
480
560
|
return;
|
|
481
561
|
}
|
|
482
562
|
|
|
563
|
+
if (head === "cells" || head === "width") {
|
|
564
|
+
const rawN = rest[0]?.trim();
|
|
565
|
+
if (!rawN) {
|
|
566
|
+
ctx.ui.notify(
|
|
567
|
+
`Pac-Man cells: ${cells} (default ${DEFAULT_CELLS}, range ${CELLS_MIN}–${CELLS_MAX}). Set with /pacman cells <n> or in ~/.pi/agent/pacman-thinking.json`,
|
|
568
|
+
"info",
|
|
569
|
+
);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const n = Number(rawN);
|
|
573
|
+
if (!Number.isFinite(n)) {
|
|
574
|
+
ctx.ui.notify(`Invalid cells "${rawN}". Use a number ${CELLS_MIN}–${CELLS_MAX}.`, "error");
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
cells = clampCells(n);
|
|
578
|
+
apply(ctx);
|
|
579
|
+
ctx.ui.notify(`Pac-Man short-look width: ${cells} cells`, "info");
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
483
583
|
if (head === "rotate" || head === "cycle") {
|
|
484
584
|
rotate = true;
|
|
585
|
+
// Only short looks participate; jump into that cycle cleanly
|
|
485
586
|
const idx = ROTATE_LOOKS.indexOf(mode);
|
|
486
587
|
rotateIndex = idx >= 0 ? idx : 0;
|
|
487
588
|
pickNextRotatedLook();
|
|
589
|
+
refreshAutoMessage();
|
|
488
590
|
apply(ctx);
|
|
489
591
|
ctx.ui.notify(
|
|
490
|
-
`Pac-Man rotate on
|
|
592
|
+
`Pac-Man rotate on. Short looks only: ${ROTATE_LOOKS.join(", ")} (now: ${mode}).`,
|
|
491
593
|
"info",
|
|
492
594
|
);
|
|
493
595
|
return;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-extensions/pi-pacman",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Pac-Man working indicator for pi
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Pac-Man working indicator for pi: replaces the streaming spinner with pellet runs, ghost chases, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"pi-package",
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/smarzban/pi-
|
|
17
|
+
"url": "git+https://github.com/smarzban/pi-extensions.git",
|
|
18
18
|
"directory": "packages/pi-pacman"
|
|
19
19
|
},
|
|
20
20
|
"bugs": {
|
|
21
|
-
"url": "https://github.com/smarzban/pi-
|
|
21
|
+
"url": "https://github.com/smarzban/pi-extensions/issues"
|
|
22
22
|
},
|
|
23
|
-
"homepage": "https://github.com/smarzban/pi-
|
|
23
|
+
"homepage": "https://github.com/smarzban/pi-extensions/tree/main/packages/pi-pacman#readme",
|
|
24
24
|
"engines": {
|
|
25
25
|
"node": ">=18.0.0"
|
|
26
26
|
},
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
"./index.ts"
|
|
30
30
|
]
|
|
31
31
|
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
34
|
+
},
|
|
32
35
|
"files": [
|
|
33
36
|
"index.ts",
|
|
34
37
|
"README.md",
|