@a-t-h-i/bot-lobby 0.6.4 → 0.6.6
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 +150 -12
- package/package.json +12 -3
- package/prompts/backend.md +3 -1
- package/prompts/designer.md +24 -1
- package/prompts/global.md +18 -0
- package/prompts/master.md +122 -20
- package/prompts/quickfix.md +16 -3
- package/prompts/worker.md +7 -2
- package/src/agents/backend.ts +2 -2
- package/src/agents/designer.ts +2 -2
- package/src/ask/dialog.ts +11 -1
- package/src/classifier/triage.ts +24 -7
- package/src/index.ts +4 -1
- package/src/lobby/feed.ts +7 -2
- package/src/lobby/keys.ts +0 -1
- package/src/lobby/quickfix.ts +29 -3
- package/src/lobby/runtime.ts +29 -26
- package/src/lobby/tabs/home.ts +6 -42
- package/src/lobby/tabs/quickfix.ts +1 -1
- package/src/lobby/tabs/tasks.ts +3 -2
- package/src/lobby/view.ts +18 -23
- package/src/master/decisions.ts +10 -2
- package/src/pi/activity.ts +1 -33
- package/src/pi/commands.ts +22 -11
- package/src/pi/events.ts +3 -17
- package/src/pi/plan-checklist.ts +305 -0
- package/src/pi/route.ts +180 -0
- package/src/pi/run-summary.ts +12 -3
- package/src/pi/settings-ui.ts +2 -2
- package/src/pi/start-task.ts +51 -5
- package/src/pi/tools.ts +13 -7
- package/src/pi/ui.ts +18 -284
- package/src/roles/worker.ts +11 -3
- package/src/schemas/configuration.ts +19 -6
- package/src/schemas/task.ts +31 -0
- package/src/workflow/brief.ts +59 -0
- package/src/workflow/track.ts +436 -0
- package/src/workflow/workflow.ts +151 -10
- package/src/pi/expressions.ts +0 -169
- package/src/pi/kaomoji.ts +0 -227
- package/src/pi/mascot-art.ts +0 -359
- package/src/pi/zen-large.ts +0 -699
- package/src/pi/zen-metrics.ts +0 -130
- package/src/pi/zen.ts +0 -659
package/src/pi/kaomoji.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The agents' kaomoji: a pure catalogue of emotes and the rules that pick one.
|
|
3
|
-
*
|
|
4
|
-
* Every emote is a tiny animation: the open face, the same face blinking (same
|
|
5
|
-
* width, so nothing shifts), and an optional action frame (a table flip, a
|
|
6
|
-
* sparkle, a bow). Each agent has its own personality set for every emotion,
|
|
7
|
-
* and a shared pool of emotions is open to all of them:
|
|
8
|
-
*
|
|
9
|
-
* - DEV, the hacker: shades, flexing, coffee, and table flips on failure
|
|
10
|
-
* - DESIGN, the artist: sparkles, flowers, hearts and little flourishes
|
|
11
|
-
* - RESEARCH, the bookworm: note-taking, pointing, shrugging, deal-with-it glasses
|
|
12
|
-
* - QA, the inspector: side-eyes and squints, then a flex and a victory dance
|
|
13
|
-
*
|
|
14
|
-
* Nothing here reads a clock or a random source: the caller passes a `variant`
|
|
15
|
-
* drawn once per expression, so the same variant always picks the same face.
|
|
16
|
-
* Every face fits a slot cell and avoids combining marks, right-to-left
|
|
17
|
-
* scripts and emoji-presentation glyphs (enforced by tests).
|
|
18
|
-
*/
|
|
19
|
-
import type { SlotId, SlotState } from "./mascot-art.ts";
|
|
20
|
-
|
|
21
|
-
export const EMOTIONS = [
|
|
22
|
-
"happy",
|
|
23
|
-
"proud",
|
|
24
|
-
"love",
|
|
25
|
-
"excited",
|
|
26
|
-
"focused",
|
|
27
|
-
"curious",
|
|
28
|
-
"thinking",
|
|
29
|
-
"nervous",
|
|
30
|
-
"confused",
|
|
31
|
-
"sleepy",
|
|
32
|
-
"sad",
|
|
33
|
-
"angry",
|
|
34
|
-
"waiting",
|
|
35
|
-
"surprised",
|
|
36
|
-
"grateful",
|
|
37
|
-
] as const;
|
|
38
|
-
export type Emotion = (typeof EMOTIONS)[number];
|
|
39
|
-
|
|
40
|
-
/** One emote: the open face, the same face blinking, and an optional action frame. */
|
|
41
|
-
export interface Emote {
|
|
42
|
-
open: string;
|
|
43
|
-
blink: string;
|
|
44
|
-
action?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const e = (open: string, blink: string, action?: string): Emote => (action ? { open, blink, action } : { open, blink });
|
|
48
|
-
|
|
49
|
-
/** Emotions every agent can draw on. */
|
|
50
|
-
export const SHARED_EMOTES: Record<Emotion, readonly Emote[]> = {
|
|
51
|
-
happy: [e("(^▽^)", "(-▽-)"), e("(^ω^)", "(-ω-)"), e("ヽ(•‿•)ノ", "ヽ(-‿-)ノ"), e("(*^‿^*)", "(*-‿-*)"), e("(◕‿◕)", "(-‿-)")],
|
|
52
|
-
proud: [e("( ̄^ ̄)ゞ", "(-^-)ゞ"), e("(´▽`)b", "(-▽-)b"), e("(  ̄ー ̄)", "( -ー-)"), e("(•ᴗ•)ง", "(-ᴗ-)ง")],
|
|
53
|
-
love: [e("(♡‿♡)", "(-‿-)", "(♡‿♡)♡"), e("(っ˘з(˘⌣˘ )", "(っ-з(-⌣- )"), e("(´ε` )♡", "(-ε- )♡"), e("(◍•ᴗ•◍)♡", "(◍-ᴗ-◍)♡")],
|
|
54
|
-
excited: [e("\\(^o^)/", "\\(-o-)/"), e("ヽ(^▽^)ノ", "ヽ(-▽-)ノ"), e("(ノ◕ヮ◕)ノ", "(ノ-ヮ-)ノ", "(ノ◕ヮ◕)ノ*:・゚✧"), e("o(≧▽≦)o", "o(-▽-)o")],
|
|
55
|
-
focused: [e("(•_•)", "(-_-)"), e("(ง •_•)ง", "(ง -_-)ง"), e("(`・ω・´)", "(`-ω-´)"), e("( ._.)φ", "( -_-)φ")],
|
|
56
|
-
curious: [e("(・・ )?", "(-- )?"), e("(⊙_⊙)?", "(-_-)?"), e("(◔_◔)", "(-_-)"), e("(゚∀゚)?", "(-∀-)?")],
|
|
57
|
-
thinking: [e("( ̄~ ̄;)", "(-~-;)"), e("( ˘_˘ )", "( -_- )"), e("(・・;)ゞ", "(--;)ゞ"), e("(¬‿¬ )", "(-‿- )")],
|
|
58
|
-
nervous: [e("(;・∀・)", "(;-∀-)"), e("(⊙﹏⊙)", "(-﹏-)"), e("(●´⌓`●)", "(●-⌓-●)"), e("(°□°;)", "(-□-;)")],
|
|
59
|
-
confused: [e("(・_・ヾ", "(-_-ヾ"), e("(⊙_☉)", "(-_-)"), e("(゜-゜)", "(---)"), e("(・・?)", "(--?)")],
|
|
60
|
-
sleepy: [e("(-_-) zzZ", "(-.-) zzZ"), e("( ̄o ̄) zzZZ", "(-o-) zzZZ"), e("(∪。∪)。zz", "(-。-)。zz"), e("( ˘ω˘ )スヤァ", "( -ω- )スヤァ")],
|
|
61
|
-
sad: [e("(ಥ_ಥ)", "(-_-)"), e("(╥﹏╥)", "(-﹏-)"), e("(´;ω;`)", "(-;ω;-)"), e("(〒﹏〒)", "(-﹏-)"), e("(T_T)", "(-_-)")],
|
|
62
|
-
angry: [e("(╬ಠ益ಠ)", "(╬-益-)"), e("(ノಠ益ಠ)ノ", "(ノ-益-)ノ", "(ノಠ益ಠ)ノ彡┻━┻"), e("ヽ(`Д´)ノ", "ヽ(-Д-)ノ"), e("(¬_¬\")", "(-_-\")")],
|
|
63
|
-
waiting: [e("(´・_・`)", "(´-_-`)"), e("( ˘・_・˘)", "( ˘-_-˘)"), e("(っ´ω`c)", "(っ-ω-c)"), e("(・_・)…", "(-_-)…")],
|
|
64
|
-
surprised: [e("(°o°)", "(-o-)"), e("Σ(°△°|||)", "Σ(-△-|||)"), e("(゚Д゚)", "(-Д-)"), e("w(°o°)w", "w(-o-)w")],
|
|
65
|
-
grateful: [e("(人´∀`)", "(人-∀-)"), e("(◍•ᴗ•◍)", "(◍-ᴗ-◍)"), e("m(_ _)m", "m(- -)m"), e("(ᵔᴥᵔ)", "(-ᴥ-)"), e("(*´∀`*)", "(*-∀-*)")],
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/** DEV, the hacker: shades, flexes, coffee and code brackets; flips tables when things break. */
|
|
69
|
-
const DEV: Record<Emotion, readonly Emote[]> = {
|
|
70
|
-
happy: [e("(⌐■_■)", "(⌐-_-)"), e("(^_^)b", "(-_-)b"), e("</>(^▽^)", "</>(-▽-)"), e("( •_•)c[_]", "( -_-)c[_]")],
|
|
71
|
-
proud: [e("(⌐■_■)b", "(⌐-_-)b"), e("ᕦ(ò_óˇ)ᕤ", "ᕦ(-_-ˇ)ᕤ"), e("( •_•)>⌐■-■", "( -_-)>⌐■-■", "(⌐■_■)"), e("b( ̄▽ ̄)d", "b(-▽-)d")],
|
|
72
|
-
love: [e("(♡_♡)</>", "(-_-)</>"), e("(´∀`)♡{ }", "(-∀-)♡{ }"), e("(˘▽˘)c[_]", "(-▽-)c[_]"), e("(◍•_•◍)♡", "(◍-_-◍)♡")],
|
|
73
|
-
excited: [e("ᕦ(ò_ó)ᕤ", "ᕦ(-_-)ᕤ"), e("(ノ≧∀≦)ノ", "(ノ-∀-)ノ", "(ノ≧∀≦)ノ</>"), e("\\(•_•)/", "\\(-_-)/"), e("(⌐■▽■)", "(⌐-▽-)")],
|
|
74
|
-
focused: [e("(•_•)>_", "(-_-)>_"), e("[•_•]$_", "[-_-]$_"), e("(ง'-')ง", "(ง- -)ง"), e("(⌐■_■)>_", "(⌐-_-)>_")],
|
|
75
|
-
curious: [e("(•_•)?</>", "(-_-)?</>"), e("(・_・)ノ{ }", "(-_-)ノ{ }"), e("(°_°)>_", "(-_-)>_"), e("( ⊙_⊙)</>", "( -_-)</>")],
|
|
76
|
-
thinking: [e("( ̄_ ̄)c[_]", "(-_-)c[_]"), e("( •_•)…{ }", "( -_-)…{ }"), e("(¬_¬)>_", "(-_-)>_"), e("(°~°)</>", "(-~-)</>")],
|
|
77
|
-
nervous: [e("(°_°;)>_", "(-_-;)>_"), e("(・_・;)$_", "(-_-;)$_"), e("(⊙_⊙;)", "(-_-;)"), e("(゚_゚;)c[_]", "(-_-;)c[_]")],
|
|
78
|
-
confused: [e("(・_・)?{ }", "(-_-)?{ }"), e("(°_°)?>_", "(-_-)?>_"), e("(⊙_☉)</>", "(-_-)</>"), e("(゜_゜)$?", "(-_-)$?")],
|
|
79
|
-
sleepy: [e("(-_-)c[_] z", "(-.-)c[_] z"), e("( ̄o ̄)>_ zz", "(-o-)>_ zz"), e("(∪_∪)</> z", "(-_-)</> z"), e("( ˘_˘ )$_ z", "( -_- )$_ z")],
|
|
80
|
-
sad: [e("(╥_╥)>_", "(-_-)>_"), e("(T_T)</>", "(-_-)</>"), e("(ಥ_ಥ)c[_]", "(-_-)c[_]"), e("(´;_;`)$_", "(-;_;-)$_")],
|
|
81
|
-
angry: [e("(╯°□°)╯", "(╯-□-)╯", "(╯°□°)╯︵ ┻━┻"), e("(ノಠ益ಠ)ノ", "(ノ-益-)ノ", "(ノಠ益ಠ)ノ彡┻━┻"), e("(╬ò_ó)>_", "(╬-_-)>_"), e("┻━┻ ︵ (°□°)", "┻━┻ ︵ (-□-)")],
|
|
82
|
-
waiting: [e("(•_•)c[_]", "(-_-)c[_]"), e("(´・_・`)>_", "(´-_-`)>_"), e("( ˘_˘)</>…", "( -_-)</>…"), e("(・_・)$_…", "(-_-)$_…")],
|
|
83
|
-
surprised: [e("(°□°)>_", "(-□-)>_"), e("Σ(°_°)</>", "Σ(-_-)</>"), e("(゚Д゚)$_", "(-Д-)$_"), e("(⊙_⊙)!{ }", "(-_-)!{ }")],
|
|
84
|
-
grateful: [e("(⌐■_■)b", "(⌐-_-)b"), e("m(_ _)m</>", "m(- -)m</>"), e("(^_^)c[_]", "(-_-)c[_]"), e("(人•_•)", "(人-_-)")],
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
/** DESIGN, the artist: sparkles, flowers, hearts and a little flourish on most things. */
|
|
88
|
-
const DESIGN: Record<Emotion, readonly Emote[]> = {
|
|
89
|
-
happy: [e("✧(◕‿◕✿)", "✧(-‿-✿)"), e("(✿^‿^)", "(✿-‿-)"), e("(◕ᴗ◕✿)", "(-ᴗ-✿)"), e("❀(^▽^)❀", "❀(-▽-)❀")],
|
|
90
|
-
proud: [e("(✿◠‿◠)", "(✿-‿-)"), e("✧(˘▽˘)✧", "✧(-▽-)✧"), e("( ̄▽ ̄)✿", "(-▽-)✿"), e("(◕‿◕)b✧", "(-‿-)b✧")],
|
|
91
|
-
love: [e("(♡‿♡)✿", "(-‿-)✿"), e("(っ˘з˘)♡✧", "(っ-з-)♡✧"), e("(◍♡‿♡◍)", "(◍-‿-◍)"), e("✿(´ε`)♡", "✿(-ε-)♡")],
|
|
92
|
-
excited: [e("(ノ◕ヮ◕)ノ", "(ノ-ヮ-)ノ", "(ノ◕ヮ◕)ノ*:・゚✧"), e("✧*(^▽^)*✧", "✧*(-▽-)*✧"), e("☆(≧▽≦)☆", "☆(-▽-)☆"), e("♪(^∇^)♪", "♪(-∇-)♪")],
|
|
93
|
-
focused: [e("(•‿•)~✎", "(-‿-)~✎"), e("( ˘_˘)~✿", "( -_-)~✿"), e("(◕_◕)✎", "(-_-)✎"), e("(・ω・)~♪", "(-ω-)~♪")],
|
|
94
|
-
curious: [e("(◕_◕)?✧", "(-_-)?✧"), e("(・・ )✿?", "(-- )✿?"), e("(°‿°)?", "(-‿-)?"), e("✧(◔_◔)", "✧(-_-)")],
|
|
95
|
-
thinking: [e("( ˘‿˘)~✎", "( -‿-)~✎"), e("( ̄~ ̄)✿", "(-~-)✿"), e("(◕~◕)…", "(-~-)…"), e("(・・)~♪", "(--)~♪")],
|
|
96
|
-
nervous: [e("(◕﹏◕✿)", "(-﹏-✿)"), e("(°‿°;)✎", "(-‿-;)✎"), e("(・_・;)✿", "(-_-;)✿"), e("(⊙‿⊙;)", "(-‿-;)")],
|
|
97
|
-
confused: [e("(◕_◕)?~", "(-_-)?~"), e("(・・?)✿", "(--?)✿"), e("(゜‿゜)?", "(-‿-)?"), e("(°_°)✎?", "(-_-)✎?")],
|
|
98
|
-
sleepy: [e("(-‿-)✿ zz", "(-.-)✿ zz"), e("( ˘ω˘ )✿z", "( -ω- )✿z"), e("(∪‿∪)♪ z", "(-‿-)♪ z"), e("( ̄‿ ̄)zzZ", "(-‿-)zzZ")],
|
|
99
|
-
sad: [e("(╥﹏╥)✿", "(-﹏-)✿"), e("(ಥ‿ಥ)", "(-‿-)"), e("(´;ω;`)✎", "(-;ω;-)✎"), e("(◕︵◕)", "(-︵-)")],
|
|
100
|
-
angry: [e("(╬◣_◢)", "(╬-_-)"), e("(ノ`Д´)ノ✎", "(ノ-Д-)ノ✎"), e("(¬_¬)✿", "(-_-)✿"), e("(ಠ‿ಠ)✎", "(-‿-)✎")],
|
|
101
|
-
waiting: [e("(っ´ω`c)✿", "(っ-ω-c)✿"), e("( ˘‿˘)~♪", "( -‿-)~♪"), e("(・‿・)…✿", "(-‿-)…✿"), e("(´・‿・`)", "(´-‿-`)")],
|
|
102
|
-
surprised: [e("(°o°)✿", "(-o-)✿"), e("✧(⊙‿⊙)✧", "✧(-‿-)✧"), e("(゚∀゚)!✎", "(-∀-)!✎"), e("Σ(◕o◕)", "Σ(-o-)")],
|
|
103
|
-
grateful: [e("(人◕‿◕)", "(人-‿-)"), e("(◍•ᴗ•◍)✿", "(◍-ᴗ-◍)✿"), e("✿m(_ _)m", "✿m(- -)m"), e("(*´▽`*)✧", "(*-▽-*)✧")],
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/** RESEARCH, the bookworm: takes notes, points at things, shrugs at the unknown. */
|
|
107
|
-
const RESEARCH: Record<Emotion, readonly Emote[]> = {
|
|
108
|
-
happy: [e("φ(^▽^)", "φ(-▽-)"), e("(^_^)[ ]", "(-_-)[ ]"), e("(•‿•)φ", "(-‿-)φ"), e("( ˘▽˘)ノ", "( -▽-)ノ")],
|
|
109
|
-
proud: [e("( •_•)>⌐■-■", "( -_-)>⌐■-■", "(⌐■_■)"), e("( ̄^ ̄)φ", "(-^-)φ"), e("(•ᴗ•)b[ ]", "(-ᴗ-)b[ ]"), e("(  ̄ー ̄)φ", "( -ー-)φ")],
|
|
110
|
-
love: [e("(♡_♡)[ ]", "(-_-)[ ]"), e("φ(´∀`)♡", "φ(-∀-)♡"), e("(◍•_•◍)φ", "(◍-_-◍)φ"), e("(˘ε˘)♡[ ]", "(-ε-)♡[ ]")],
|
|
111
|
-
excited: [e("(σ・・)σ!", "(σ--)σ!"), e("φ(≧▽≦)", "φ(-▽-)"), e("\\(°o°)/", "\\(-o-)/"), e("(ノ°▽°)ノ[ ]", "(ノ-▽-)ノ[ ]")],
|
|
112
|
-
focused: [e("φ(..)", "φ(--)", "φ(..;)"), e("( ._.)φ__", "( -_-)φ__"), e("(・_・)[ ]", "(-_-)[ ]"), e("( •_•)φ", "( -_-)φ")],
|
|
113
|
-
curious: [e("(σ・・)σ", "(σ--)σ"), e("(・・ )?[ ]", "(-- )?[ ]"), e("(⊙_⊙)φ?", "(-_-)φ?"), e("(°_°)…?", "(-_-)…?")],
|
|
114
|
-
thinking: [e("φ( ̄~ ̄)", "φ(-~-)"), e("( ˘_˘ )φ", "( -_- )φ"), e("(・・;)[ ]", "(--;)[ ]"), e("(¬_¬)φ…", "(-_-)φ…")],
|
|
115
|
-
nervous: [e("φ(°_°;)", "φ(-_-;)"), e("(・_・;)[?]", "(-_-;)[?]"), e("(⊙﹏⊙)φ", "(-﹏-)φ"), e("(°□°;)[ ]", "(-□-;)[ ]")],
|
|
116
|
-
confused: [e("¯\\_(ツ)_/¯", "¯\\_(ー)_/¯"), e("(・_・ヾ[?]", "(-_-ヾ[?]"), e("(⊙_☉)φ", "(-_-)φ"), e("(゜-゜)?[ ]", "(---)?[ ]")],
|
|
117
|
-
sleepy: [e("(-_-)[ ] zz", "(-.-)[ ] zz"), e("φ( ̄o ̄) z", "φ(-o-) z"), e("(∪_∪)φ zz", "(-_-)φ zz"), e("( ˘ω˘ )[ ]z", "( -ω- )[ ]z")],
|
|
118
|
-
sad: [e("(ಥ_ಥ)φ", "(-_-)φ"), e("(╥_╥)[ ]", "(-_-)[ ]"), e("(´;ω;`)φ", "(-;ω;-)φ"), e("(T_T)[?]", "(-_-)[?]")],
|
|
119
|
-
angry: [e("(ಠ_ಠ)φ", "(-_-)φ"), e("(╬ಠ_ಠ)[ ]", "(╬-_-)[ ]"), e("ヽ(`Д´)ノφ", "ヽ(-Д-)ノφ"), e("(¬_¬\")[ ]", "(-_-\")[ ]")],
|
|
120
|
-
waiting: [e("(´・_・`)φ", "(´-_-`)φ"), e("( ˘・_・˘)[ ]", "( ˘-_-˘)[ ]"), e("φ(・_・)…", "φ(-_-)…"), e("(っ´ω`c)φ", "(っ-ω-c)φ")],
|
|
121
|
-
surprised: [e("Σ(°o°)φ", "Σ(-o-)φ"), e("(゚Д゚)[!]", "(-Д-)[!]"), e("(⊙_⊙)!φ", "(-_-)!φ"), e("w(°o°)w[ ]", "w(-o-)w[ ]")],
|
|
122
|
-
grateful: [e("(人´∀`)φ", "(人-∀-)φ"), e("m(_ _)m[ ]", "m(- -)m[ ]"), e("(ᵔᴥᵔ)φ", "(-ᴥ-)φ"), e("(◍•ᴗ•◍)[ ]", "(◍-ᴗ-◍)[ ]")],
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
/** QA, the inspector: side-eyes and squints at everything, then flexes and dances on a pass. */
|
|
126
|
-
const QA: Record<Emotion, readonly Emote[]> = {
|
|
127
|
-
happy: [e("(•‿•)✓", "(-‿-)✓"), e("( ^_^)[✓]", "( -_-)[✓]"), e("(ᵔ.ᵔ)✓", "(-.-)✓"), e("(^_^)✓", "(-_-)✓")],
|
|
128
|
-
proud: [e("ᕙ( • ‿ • )ᕗ", "ᕙ( - ‿ - )ᕗ"), e("ᕦ(•_•)ᕤ✓", "ᕦ(-_-)ᕤ✓"), e("( ̄^ ̄)✓", "(-^-)✓"), e("( •_•)b✓", "( -_-)b✓")],
|
|
129
|
-
love: [e("(♡_♡)✓", "(-_-)✓"), e("(´∀`)♡[✓]", "(-∀-)♡[✓]"), e("(◍•_•◍)✓", "(◍-_-◍)✓"), e("(˘ε˘)♡✓", "(-ε-)♡✓")],
|
|
130
|
-
excited: [e("ᕕ( ᐛ )ᕗ", "ᕕ( - )ᕗ"), e("\\(•_•)/✓", "\\(-_-)/✓"), e("(ノ≧▽≦)ノ✓", "(ノ-▽-)ノ✓"), e("ᕙ(⇀‸↼)ᕗ", "ᕙ(-‸-)ᕗ")],
|
|
131
|
-
focused: [e("(ಠ_ಠ)", "(−_−)"), e("(¬_¬)", "(-_-)"), e("(눈_눈)", "(-_-)"), e("(•_•)⊙", "(-_-)⊙")],
|
|
132
|
-
curious: [e("(ಠ_ಠ)?", "(−_−)?"), e("(¬_¬)?[ ]", "(-_-)?[ ]"), e("(⊙_⊙)⊙", "(-_-)⊙"), e("(・_・)?✓", "(-_-)?✓")],
|
|
133
|
-
thinking: [e("(¬‿¬)[ ]", "(-‿-)[ ]"), e("(ಠ~ಠ)", "(−~−)"), e("( ̄_ ̄)[?]", "(-_-)[?]"), e("( ˘_˘ )✓?", "( -_- )✓?")],
|
|
134
|
-
nervous: [e("(ಠ_ಠ;)", "(−_−;)"), e("(°_°;)[ ]", "(-_-;)[ ]"), e("(⊙﹏⊙)✓?", "(-﹏-)✓?"), e("(・_・;)✗?", "(-_-;)✗?")],
|
|
135
|
-
confused: [e("(ಠ_ಠ)?!", "(−_−)?!"), e("(・_・ヾ[?]", "(-_-ヾ[?]"), e("(⊙_☉)✗", "(-_-)✗"), e("(゜-゜)[✗]", "(---)[✗]")],
|
|
136
|
-
sleepy: [e("(-_-)[ ] zz", "(-.-)[ ] zz"), e("( ̄o ̄)✓ zz", "(-o-)✓ zz"), e("(∪_∪)⊙ z", "(-_-)⊙ z"), e("( ˘ω˘ )✓z", "( -ω- )✓z")],
|
|
137
|
-
sad: [e("(ಥ_ಥ)✗", "(-_-)✗"), e("(╥﹏╥)[✗]", "(-﹏-)[✗]"), e("(´;ω;`)✗", "(-;ω;-)✗"), e("(T_T)✗", "(-_-)✗")],
|
|
138
|
-
angry: [e("(ಠ益ಠ)", "(−益−)"), e("(╬ಠ_ಠ)✗", "(╬−_−)✗"), e("(ノಠ益ಠ)ノ", "(ノ−益−)ノ", "(ノಠ益ಠ)ノ[✗]"), e("(¬_¬\")✗", "(-_-\")✗")],
|
|
139
|
-
waiting: [e("(¬_¬)…", "(-_-)…"), e("(´・_・`)[ ]", "(´-_-`)[ ]"), e("(ಠ_ಠ)…", "(−_−)…"), e("( ˘_˘)✓…", "( -_-)✓…")],
|
|
140
|
-
surprised: [e("(ಠ_ಠ)!", "(−_−)!"), e("Σ(°_°)✗", "Σ(-_-)✗"), e("(⊙_⊙)[!]", "(-_-)[!]"), e("(゚Д゚)✓!", "(-Д-)✓!")],
|
|
141
|
-
grateful: [e("(人•_•)✓", "(人-_-)✓"), e("m(_ _)m✓", "m(- -)m✓"), e("(ᵔ.ᵔ)b", "(-.-)b"), e("(◍•ᴗ•◍)✓", "(◍-ᴗ-◍)✓")],
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
/** Personality sets per agent column. */
|
|
145
|
-
export const PERSONALITY_EMOTES: Record<SlotId, Record<Emotion, readonly Emote[]>> = {
|
|
146
|
-
dev: DEV,
|
|
147
|
-
design: DESIGN,
|
|
148
|
-
research: RESEARCH,
|
|
149
|
-
qa: QA,
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
/** Frames per emote: open, blink, then the action (or the open face again), held one step each. */
|
|
153
|
-
export const EMOTE_STEPS = 4;
|
|
154
|
-
|
|
155
|
-
/** The four frames an emote steps through: open, blink, action, action. */
|
|
156
|
-
export function emoteFrames(emote: Emote): readonly string[] {
|
|
157
|
-
const finish = emote.action ?? emote.open;
|
|
158
|
-
return [emote.open, emote.blink, finish, finish];
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** What a slot is going through, as far as its face is concerned. */
|
|
162
|
-
export interface Situation {
|
|
163
|
-
status: SlotState;
|
|
164
|
-
/** Live one-word activity while working (reading, editing, running, thinking, ...). */
|
|
165
|
-
activity?: string;
|
|
166
|
-
flag?: "waiting" | "quiet" | "retry";
|
|
167
|
-
/** The run was asked to wrap up early. */
|
|
168
|
-
wrappedUp?: boolean;
|
|
169
|
-
/** A file was just handed to this agent. */
|
|
170
|
-
handover?: boolean;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const ACTIVITY_EMOTIONS: Record<string, readonly Emotion[]> = {
|
|
174
|
-
reading: ["curious", "focused"],
|
|
175
|
-
searching: ["curious", "thinking"],
|
|
176
|
-
editing: ["focused", "excited", "proud"],
|
|
177
|
-
running: ["nervous", "focused"],
|
|
178
|
-
thinking: ["thinking", "curious"],
|
|
179
|
-
writing: ["focused", "proud"],
|
|
180
|
-
researching: ["curious", "thinking"],
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
/** The emotions that fit a situation, most fitting first. */
|
|
184
|
-
export function emotionsFor(situation: Situation): readonly Emotion[] {
|
|
185
|
-
switch (situation.status) {
|
|
186
|
-
case "idle":
|
|
187
|
-
return ["sleepy", "waiting", "happy"];
|
|
188
|
-
case "done":
|
|
189
|
-
return situation.wrappedUp ? ["nervous", "proud"] : ["happy", "proud", "love", "excited"];
|
|
190
|
-
case "failed":
|
|
191
|
-
return ["sad", "angry", "confused", "surprised"];
|
|
192
|
-
default:
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
if (situation.flag === "waiting") return ["waiting"];
|
|
196
|
-
if (situation.flag === "quiet") return ["confused", "sleepy"];
|
|
197
|
-
if (situation.flag === "retry") return ["nervous", "sad"];
|
|
198
|
-
if (situation.handover) return ["grateful", "happy"];
|
|
199
|
-
if (situation.wrappedUp) return ["nervous", "surprised"];
|
|
200
|
-
return ACTIVITY_EMOTIONS[situation.activity ?? ""] ?? ["focused", "excited", "happy"];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/** Share of picks drawn from the agent's own personality set (out of 10). */
|
|
204
|
-
const PERSONALITY_SHARE = 6;
|
|
205
|
-
|
|
206
|
-
function positive(variant: number): number {
|
|
207
|
-
return Number.isFinite(variant) ? Math.abs(Math.floor(variant)) : 0;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* The emote for one expression: the emotion from the situation, then a face
|
|
212
|
-
* from the agent's personality (about 60%) or the shared pool, all chosen by
|
|
213
|
-
* the expression's `variant`.
|
|
214
|
-
*/
|
|
215
|
-
export function pickEmote(slot: SlotId, situation: Situation, variant: number): Emote {
|
|
216
|
-
const seed = positive(variant);
|
|
217
|
-
const emotions = emotionsFor(situation);
|
|
218
|
-
const emotion = emotions[seed % emotions.length]!;
|
|
219
|
-
const own = (Math.floor(seed / 7) % 10) < PERSONALITY_SHARE;
|
|
220
|
-
const pool = own ? PERSONALITY_EMOTES[slot][emotion] : SHARED_EMOTES[emotion];
|
|
221
|
-
return pool[Math.floor(seed / 71) % pool.length]!;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/** The frames of the emote a slot plays for this expression. */
|
|
225
|
-
export function slotEmote(slot: SlotId, situation: Situation, variant: number): readonly string[] {
|
|
226
|
-
return emoteFrames(pickEmote(slot, situation, variant));
|
|
227
|
-
}
|
package/src/pi/mascot-art.ts
DELETED
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
import type { TaskState } from "../schemas/task.ts";
|
|
2
|
-
import { slotEmote } from "./kaomoji.ts";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Pure ASCII art data for the scene-based zen panel. No logic lives here: the
|
|
6
|
-
* captions, status vocabulary, frame sets and tower templates are constants
|
|
7
|
-
* that `zen.ts` and `zen-large.ts` compose. Every glyph is exactly one visible
|
|
8
|
-
* column so the art stays narrow/ambiguous-width safe: the non-ASCII glyphs are
|
|
9
|
-
* the lobby `⌂` in `BANNER_NARROW`, the panel's ◐/✓/✗ status family, and the
|
|
10
|
-
* box-drawing, bar, eye and aura glyphs of the large scene (─ │ ┌ ┐ └ ┘ ╭ ╮ ╰ ╯ ╲ ╱ ╶ ├ ┤ ┬ ┴ █ ▓ ▒ ░ ▁-▇ ◉ ◍ ◎ ◌ ○ ◓ ◒ ✦ ✧).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* One-column status glyphs, matching the glyph family the panel already uses
|
|
15
|
-
* for runs and steps (◐ running, ✓ success, ✗ failure); the slot roster below
|
|
16
|
-
* reuses them so a status never rests on colour alone.
|
|
17
|
-
*/
|
|
18
|
-
const STATUS_GLYPHS = { idle: "·", running: "◐", done: "✓", failed: "✗" } as const;
|
|
19
|
-
|
|
20
|
-
/** Braille spinner frames shared by the large scene and the compact working line. */
|
|
21
|
-
export const SPIN_FRAMES: readonly string[] = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
22
|
-
|
|
23
|
-
export const BANNER_TITLE = "THE BOT LOBBY";
|
|
24
|
-
export const BANNER_NARROW = "⌂ THE BOT LOBBY";
|
|
25
|
-
|
|
26
|
-
/** One-line (<= 30 chars) caption per task state, labelling the compact strip. */
|
|
27
|
-
export const SCENE_PROPS: Record<TaskState, string> = {
|
|
28
|
-
created: "no work started yet",
|
|
29
|
-
clarifying: "clarifying the request",
|
|
30
|
-
scouting: "scouting the codebase",
|
|
31
|
-
synthesizing: "synthesizing findings",
|
|
32
|
-
awaiting_approval: "awaiting your approval",
|
|
33
|
-
planning: "writing the plan",
|
|
34
|
-
implementing: "agents at work",
|
|
35
|
-
reviewing: "reviewing the diff",
|
|
36
|
-
blocked: "blocked, needs a hand",
|
|
37
|
-
completed: "work complete",
|
|
38
|
-
abandoned: "task set aside",
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
/* -------------------------------------------------------------------------
|
|
43
|
-
* Slot roster and animated art for the zen scene (composed by zen-large.ts at
|
|
44
|
-
* >= LARGE_MIN_WIDTH and by zen.ts's compact strip below it).
|
|
45
|
-
* ---------------------------------------------------------------------- */
|
|
46
|
-
|
|
47
|
-
/** The four agent columns of the large scene, in the order the scene draws them. */
|
|
48
|
-
export type SlotId = "dev" | "design" | "research" | "qa";
|
|
49
|
-
|
|
50
|
-
export const SLOT_IDS: readonly SlotId[] = ["dev", "design", "research", "qa"];
|
|
51
|
-
|
|
52
|
-
/** Uppercase column captions; RESEARCH is the longest at 8 columns. */
|
|
53
|
-
export const SLOT_LABELS: Record<SlotId, string> = {
|
|
54
|
-
dev: "DEV",
|
|
55
|
-
design: "DESIGN",
|
|
56
|
-
research: "RESEARCH",
|
|
57
|
-
qa: "QA",
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/** Status the roster reports; drives the face, the colour and the state word. */
|
|
61
|
-
export type SlotState = "working" | "idle" | "done" | "failed";
|
|
62
|
-
|
|
63
|
-
export const SLOT_STATES: readonly SlotState[] = ["working", "idle", "done", "failed"];
|
|
64
|
-
|
|
65
|
-
export const SLOT_STATE_WORDS: Record<SlotState, string> = {
|
|
66
|
-
working: "working",
|
|
67
|
-
idle: "idle",
|
|
68
|
-
done: "done",
|
|
69
|
-
failed: "failed",
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
/** One-column status glyph sitting beside the state word, so status never rests on colour alone. */
|
|
73
|
-
export const SLOT_STATE_GLYPHS: Record<SlotState, string> = {
|
|
74
|
-
working: STATUS_GLYPHS.running,
|
|
75
|
-
idle: STATUS_GLYPHS.idle,
|
|
76
|
-
done: STATUS_GLYPHS.done,
|
|
77
|
-
failed: STATUS_GLYPHS.failed,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
/** The slice of a panel theme's colour vocabulary this art maps onto. */
|
|
81
|
-
export type PanelColor = "accent" | "muted" | "dim" | "success" | "error" | "warning";
|
|
82
|
-
|
|
83
|
-
/** Status -> colour for the face, state word and bar fill. Working is bold as well. */
|
|
84
|
-
export const SLOT_STATE_COLORS: Record<SlotState, { color: PanelColor; bold: boolean }> = {
|
|
85
|
-
working: { color: "accent", bold: true },
|
|
86
|
-
idle: { color: "dim", bold: false },
|
|
87
|
-
done: { color: "success", bold: false },
|
|
88
|
-
failed: { color: "error", bold: false },
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/** Ten-cell progress bar; the caller fills `cells` of it from a percentage. */
|
|
92
|
-
export const BAR = { filled: "█", empty: "░", cells: 10 } as const;
|
|
93
|
-
|
|
94
|
-
/** The master's pose: orchestrating while the task is live, dormant when paused or finished. */
|
|
95
|
-
export type OraclePose = "orchestrating" | "dormant";
|
|
96
|
-
|
|
97
|
-
export const ORACLE_POSES: readonly OraclePose[] = ["orchestrating", "dormant"];
|
|
98
|
-
|
|
99
|
-
export const ORACLE_WORDS: Record<OraclePose, string> = {
|
|
100
|
-
orchestrating: "orchestrating",
|
|
101
|
-
dormant: "dormant",
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export const ORACLE_COLORS: Record<OraclePose, { color: PanelColor; bold: boolean }> = {
|
|
105
|
-
orchestrating: { color: "accent", bold: true },
|
|
106
|
-
dormant: { color: "dim", bold: false },
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
/** One oracle frame: the crown orb, both pupils and the seven-column mouth. */
|
|
110
|
-
export interface OracleFrame {
|
|
111
|
-
orb: string;
|
|
112
|
-
/**
|
|
113
|
-
* One-column pupil drawn inside its three-column eye window: `OPEN_EYE` takes
|
|
114
|
-
* the gaze's pupil, `CLOSED_EYE` fills the window as a lid, anything else is drawn as is.
|
|
115
|
-
*/
|
|
116
|
-
winL: string;
|
|
117
|
-
winR: string;
|
|
118
|
-
/** Exactly seven one-column glyphs; never moves the tower geometry. */
|
|
119
|
-
mouth: string;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Oracle expression frame indexes, matching the shared schedule (see expressions.ts). */
|
|
123
|
-
export const ORACLE_REST = 0;
|
|
124
|
-
export const ORACLE_BLINK = 1;
|
|
125
|
-
/** Both emote frames are the look-around; the second only dims the orb. */
|
|
126
|
-
export const ORACLE_LOOK = 2;
|
|
127
|
-
|
|
128
|
-
/** An open eye: its pupil comes from where the oracle is looking. */
|
|
129
|
-
export const OPEN_EYE = "◉";
|
|
130
|
-
/** A closed eye: the lid fills the whole window. */
|
|
131
|
-
export const CLOSED_EYE = "─";
|
|
132
|
-
/** A half-closed lid, mid-blink or half-asleep. */
|
|
133
|
-
export const HALF_LID = "◒";
|
|
134
|
-
/** Inner width of each eye window; the pupil moves across it. */
|
|
135
|
-
export const EYE_WIDTH = 3;
|
|
136
|
-
|
|
137
|
-
/** The oracle's straight, serious mouth: it never idles on a smile. */
|
|
138
|
-
export const ORACLE_MOUTH = " ─── ";
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Indexed oracle expressions per pose: rest, blink, then the two emote steps.
|
|
142
|
-
* Orchestrating, the oracle keeps a straight face and its emotes are a look
|
|
143
|
-
* around the room; dormant, it sleeps, peeks one eye open and snores (o, then O).
|
|
144
|
-
* While it talks the mouth lip-syncs instead.
|
|
145
|
-
*/
|
|
146
|
-
export const ORACLE_FRAMES: Record<OraclePose, readonly OracleFrame[]> = {
|
|
147
|
-
orchestrating: [
|
|
148
|
-
{ orb: "◉", winL: OPEN_EYE, winR: OPEN_EYE, mouth: ORACLE_MOUTH },
|
|
149
|
-
{ orb: "◉", winL: CLOSED_EYE, winR: CLOSED_EYE, mouth: ORACLE_MOUTH },
|
|
150
|
-
{ orb: "◎", winL: OPEN_EYE, winR: OPEN_EYE, mouth: ORACLE_MOUTH },
|
|
151
|
-
{ orb: "◍", winL: OPEN_EYE, winR: OPEN_EYE, mouth: ORACLE_MOUTH },
|
|
152
|
-
],
|
|
153
|
-
dormant: [
|
|
154
|
-
{ orb: "◌", winL: CLOSED_EYE, winR: CLOSED_EYE, mouth: ORACLE_MOUTH },
|
|
155
|
-
{ orb: "◌", winL: HALF_LID, winR: CLOSED_EYE, mouth: ORACLE_MOUTH },
|
|
156
|
-
{ orb: "○", winL: CLOSED_EYE, winR: CLOSED_EYE, mouth: " ─o─ " },
|
|
157
|
-
{ orb: "○", winL: CLOSED_EYE, winR: CLOSED_EYE, mouth: " ─O─ " },
|
|
158
|
-
],
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Lid positions stepped through by a blink (one per `PHASE_MS`, the last held):
|
|
163
|
-
* half-lidded, shut, shut, half-lidded. Dormant, the "blink" is a sleepy peek:
|
|
164
|
-
* the left eye cracks open, looks, and drifts shut again.
|
|
165
|
-
*/
|
|
166
|
-
export const ORACLE_BLINKS: Record<OraclePose, readonly (readonly [string, string])[]> = {
|
|
167
|
-
orchestrating: [[HALF_LID, HALF_LID], [CLOSED_EYE, CLOSED_EYE], [CLOSED_EYE, CLOSED_EYE], [HALF_LID, HALF_LID]],
|
|
168
|
-
dormant: [[HALF_LID, CLOSED_EYE], [OPEN_EYE, CLOSED_EYE], [OPEN_EYE, CLOSED_EYE], [OPEN_EYE, CLOSED_EYE], [HALF_LID, CLOSED_EYE], [CLOSED_EYE, CLOSED_EYE]],
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Where the pupils look. `x` moves the pupil across its three-column window
|
|
173
|
-
* (left, centre, right); `y` swaps its glyph, since the window is one row:
|
|
174
|
-
* `◓` looks up, `◉` straight ahead, `◒` down.
|
|
175
|
-
*/
|
|
176
|
-
export interface Gaze {
|
|
177
|
-
x: -1 | 0 | 1;
|
|
178
|
-
y: -1 | 0 | 1;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export const GAZE_AHEAD: Gaze = { x: 0, y: 0 };
|
|
182
|
-
|
|
183
|
-
/** The pupil glyph for each vertical gaze. */
|
|
184
|
-
export const ORACLE_PUPILS: Record<Gaze["y"], string> = { [-1]: "◓", 0: "◉", 1: "◒" };
|
|
185
|
-
|
|
186
|
-
const look = (x: Gaze["x"], y: Gaze["y"]): Gaze => ({ x, y });
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* The look-around emote, one step per `PHASE_MS` across both emote frames
|
|
190
|
-
* (~2 s): a slow scan left, up across the ceiling, right, down at the agents
|
|
191
|
-
* and back to you.
|
|
192
|
-
*/
|
|
193
|
-
export const ORACLE_LOOK_PATH: readonly Gaze[] = [
|
|
194
|
-
look(0, 0), look(-1, 0), look(-1, 0), look(-1, 0), look(-1, -1), look(-1, -1),
|
|
195
|
-
look(0, -1), look(1, -1), look(1, -1), look(1, 0), look(1, 0), look(1, 0),
|
|
196
|
-
look(1, 1), look(0, 1), look(0, 1), look(-1, 1), look(0, 0), look(0, 0),
|
|
197
|
-
];
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Idle wandering when nothing holds the oracle's attention: it keeps returning
|
|
201
|
-
* to you between glances around the room. Starts looking ahead.
|
|
202
|
-
*/
|
|
203
|
-
export const ORACLE_WANDER: readonly Gaze[] = [
|
|
204
|
-
look(0, 0), look(-1, 0), look(0, 0), look(1, -1), look(1, 0), look(0, 0),
|
|
205
|
-
look(-1, 1), look(0, -1), look(0, 0), look(1, 1), look(-1, -1), look(0, 0),
|
|
206
|
-
];
|
|
207
|
-
|
|
208
|
-
/** Ticks the eyes hold one wandering spot (~2.5 s while work is live). */
|
|
209
|
-
export const ORACLE_WANDER_TICKS = 10;
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Lip-sync while the oracle talks: a voice waveform that swells and falls, with
|
|
213
|
-
* a closed-mouth breath so it reads as phrases rather than a buzz.
|
|
214
|
-
*/
|
|
215
|
-
export const ORACLE_TALK: readonly string[] = [
|
|
216
|
-
" ▁▃▅▃▁ ",
|
|
217
|
-
" ▂▆▂ ",
|
|
218
|
-
" ▂▄▇▄▂ ",
|
|
219
|
-
" ▁▂▁ ",
|
|
220
|
-
" ▃▅▂▅▃ ",
|
|
221
|
-
" ▁▂▄▂▁ ",
|
|
222
|
-
" ▁ ",
|
|
223
|
-
ORACLE_MOUTH,
|
|
224
|
-
];
|
|
225
|
-
|
|
226
|
-
/** The oracle's resting mood: steady by default, worried when the task is blocked. */
|
|
227
|
-
export type OracleMood = "steady" | "worried";
|
|
228
|
-
|
|
229
|
-
/** The resting mouth per mood: straight, or a tight frown when blocked. */
|
|
230
|
-
export const ORACLE_MOOD_MOUTHS: Record<OracleMood, string> = {
|
|
231
|
-
steady: ORACLE_MOUTH,
|
|
232
|
-
worried: " ╭─╮ ",
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
/** Ticks the eyes hold on one working agent before glancing to the next. */
|
|
236
|
-
export const ORACLE_GAZE_TICKS = 8;
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* The seven-column aura above the crown, stepped by the spinner tick: sparkles
|
|
240
|
-
* twinkle while the oracle orchestrates, and z's drift up while it sleeps.
|
|
241
|
-
*/
|
|
242
|
-
export const ORACLE_AURA: Record<OraclePose, readonly string[]> = {
|
|
243
|
-
orchestrating: ["· ✦ ·", " ✧ · ✧ ", "✦ · ✦", " · ✧ · "],
|
|
244
|
-
dormant: [" z ", " z ", " z Z ", " Z "],
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
/** Ticks each aura frame holds: a calm twinkle rather than the spinner's pace. */
|
|
248
|
-
export const ORACLE_AURA_TICKS = 2;
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* One frame per status and expression: index 0 is the calm rest face, 1 the
|
|
252
|
-
* blink and 2-3 the emotes. Every status keeps the same frame count and one row
|
|
253
|
-
* each, so a caller-chosen index stays comparable across statuses.
|
|
254
|
-
*/
|
|
255
|
-
const FACE_FRAMES: Record<SlotState, readonly string[]> = {
|
|
256
|
-
working: ["(^_^)", "(-_-)", "(^o^)", "(^-^)"],
|
|
257
|
-
idle: ["(-_-)", "(u_u)", "(-.-)", "(u.u)"],
|
|
258
|
-
done: ["(o_o)", "(-_-)", "(^-^)", "(^_^)"],
|
|
259
|
-
failed: ["(>_<)", "(x_x)", "(T_T)", "(;_;)"],
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
/** Every compact face is five columns, so a centered column never shifts between frames. */
|
|
263
|
-
export const FACE_WIDTH = 5;
|
|
264
|
-
|
|
265
|
-
function buildSlotFrames(id: SlotId): Record<SlotState, readonly string[][]> {
|
|
266
|
-
const frames = {} as Record<SlotState, readonly string[][]>;
|
|
267
|
-
for (const state of SLOT_STATES) {
|
|
268
|
-
frames[state] = [...FACE_FRAMES[state].slice(0, 2), ...slotEmote(id, { status: state }, 0)].map((face) => [face]);
|
|
269
|
-
}
|
|
270
|
-
return frames;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Animated faces per slot: one row each. Rest (0) and blink (1) stay the five-column
|
|
275
|
-
* ASCII eyes; the emote frames (2+) are the slot's default kaomoji for the status
|
|
276
|
-
* (open, blink, action). The live scene swaps in a situation-aware emote per
|
|
277
|
-
* expression (see kaomoji.ts); either way the scene centres each frame in its cell.
|
|
278
|
-
*/
|
|
279
|
-
export const SLOT_FRAMES: Record<SlotId, Record<SlotState, readonly string[][]>> = {
|
|
280
|
-
dev: buildSlotFrames("dev"),
|
|
281
|
-
design: buildSlotFrames("design"),
|
|
282
|
-
research: buildSlotFrames("research"),
|
|
283
|
-
qa: buildSlotFrames("qa"),
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
/** Three-column accessory that marks each slot in the one-row compact strip. */
|
|
287
|
-
const SLOT_MARKS: Record<SlotId, string> = {
|
|
288
|
-
dev: "[=]",
|
|
289
|
-
design: "~,~",
|
|
290
|
-
research: "<?>",
|
|
291
|
-
qa: "<o>",
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
/** Eight columns: the slot's accessory followed by one face frame. */
|
|
295
|
-
export const COMPACT_WIDTH = 8;
|
|
296
|
-
|
|
297
|
-
function buildCompactFrames(id: SlotId): Record<SlotState, readonly string[]> {
|
|
298
|
-
const mark = SLOT_MARKS[id];
|
|
299
|
-
const frames = {} as Record<SlotState, readonly string[]>;
|
|
300
|
-
for (const state of SLOT_STATES) frames[state] = FACE_FRAMES[state].map((face) => `${mark}${face}`);
|
|
301
|
-
return frames;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/** Animated one-row sprites per slot and status for the compact strip. */
|
|
305
|
-
export const COMPACT_FRAMES: Record<SlotId, Record<SlotState, readonly string[]>> = {
|
|
306
|
-
dev: buildCompactFrames("dev"),
|
|
307
|
-
design: buildCompactFrames("design"),
|
|
308
|
-
research: buildCompactFrames("research"),
|
|
309
|
-
qa: buildCompactFrames("qa"),
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* The oracle tower: 13 columns wide, `rows` while height allows and `smallRows`
|
|
314
|
-
* when it must shrink. `{aura}` twinkles over the radiant `{orb}` crown,
|
|
315
|
-
* `{winL}`/`{winR}` are the three-column eye windows the pupils move in,
|
|
316
|
-
* `{mouth}` speaks and `{door}` is the name over the door. The speech bubble
|
|
317
|
-
* hangs beside the crown.
|
|
318
|
-
*/
|
|
319
|
-
export interface TowerSpec {
|
|
320
|
-
rows: readonly string[];
|
|
321
|
-
smallRows: readonly string[];
|
|
322
|
-
tokens: { aura: string; orb: string; winL: string; winR: string; door: string; mouth: string };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export const TOWER_WIDTH = 13;
|
|
326
|
-
|
|
327
|
-
export const TOWER_DOOR = "ORC";
|
|
328
|
-
|
|
329
|
-
export const TOWER: TowerSpec = {
|
|
330
|
-
rows: [
|
|
331
|
-
" {aura} ",
|
|
332
|
-
" ╲ │ ╱ ",
|
|
333
|
-
" ──({orb})── ",
|
|
334
|
-
" ╱ │ ╲ ",
|
|
335
|
-
" │ ",
|
|
336
|
-
"╭─────┴─────╮",
|
|
337
|
-
"│╭───╮ ╭───╮│",
|
|
338
|
-
"││{winL}│ │{winR}││",
|
|
339
|
-
"│╰───╯ ╰───╯│",
|
|
340
|
-
"│ {mouth} │",
|
|
341
|
-
"├───┬───┬───┤",
|
|
342
|
-
"│░▒▓│{door}│▓▒░│",
|
|
343
|
-
"└───┴───┴───┘",
|
|
344
|
-
],
|
|
345
|
-
smallRows: [
|
|
346
|
-
" ──({orb})── ",
|
|
347
|
-
"╭─────┴─────╮",
|
|
348
|
-
"││{winL}│ │{winR}││",
|
|
349
|
-
"│░▒▓│{door}│▓▒░│",
|
|
350
|
-
"└───┴───┴───┘",
|
|
351
|
-
],
|
|
352
|
-
tokens: { aura: "{aura}", orb: "{orb}", winL: "{winL}", winR: "{winR}", door: "{door}", mouth: "{mouth}" },
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
/** The oracle's speech bubble: outer width including borders, and its text rows. */
|
|
356
|
-
export const BUBBLE = { width: 24, lines: 2 } as const;
|
|
357
|
-
|
|
358
|
-
/** What the oracle says while the master waits on the user between turns. */
|
|
359
|
-
export const ORACLE_IDLE_WORD = "your turn";
|