viberoom 0.5.7 โ†’ 0.5.9

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/ui/avatars.js CHANGED
@@ -67,6 +67,7 @@
67
67
  ? `<span class="avatar-badge" title="${title}"><img src="${escapeAttr(recipe.icon)}" alt="" onerror="this.replaceWith(document.createTextNode('${letter}'))"></span>`
68
68
  : `<span class="avatar-badge" title="${title}">${letter}</span>`;
69
69
  }
70
+ if (opts.muted && participant.kind === "agent" && window.Icons) badge = `<span class="avatar-badge muted" title="muted: receives no prompts">${window.Icons.svg("mute")}</span>`;
70
71
  return `<span class="avatar" style="width:${size}px;height:${size}px">${svg}${badge}${status}</span>`;
71
72
  }
72
73
 
@@ -77,37 +78,135 @@
77
78
  "๐ŸŒ™", "โญ", "๐Ÿ”ฅ", "๐Ÿ€", "๐ŸŒต", "๐ŸŒˆ", "๐ŸŽง", "๐ŸŽธ", "๐Ÿš€", "๐Ÿ›ธ", "๐Ÿง ", "๐ŸŽฏ", "๐Ÿงฉ", "๐Ÿ’Ž", "๐Ÿ•", "โ˜•",
78
79
  ];
79
80
 
80
- function pickerElement(current, onPick) {
81
+ const EMOJI_NAMES = {
82
+ "๐ŸŽญ": "theatre masks drama roles", "๐Ÿš€": "rocket launch ship", "๐Ÿงช": "test tube experiment lab", "๐Ÿ› ๏ธ": "hammer and wrench tools",
83
+ "๐ŸŽจ": "palette art design paint", "๐Ÿ“š": "books library docs", "๐Ÿง ": "brain mind thinking", "๐Ÿ’ฌ": "speech bubble chat talk",
84
+ "๐Ÿ”ฌ": "microscope science research", "๐ŸŽฏ": "target goal focus aim", "๐Ÿ™": "octopus github", "โ˜•": "coffee cup break cafe",
85
+ "๐ŸŒˆ": "rainbow colors", "๐Ÿ—๏ธ": "construction crane building site", "๐ŸŽฎ": "game controller gaming play", "๐Ÿ”ฅ": "fire hot flame",
86
+ "๐Ÿงฉ": "puzzle piece", "๐Ÿ“ˆ": "chart up growth trending", "๐Ÿ—บ๏ธ": "world map", "๐ŸŽง": "headphones audio music",
87
+ "๐ŸŒฑ": "seedling plant grow sprout", "๐Ÿ ": "house home", "๐Ÿ›ธ": "flying saucer ufo", "๐Ÿงญ": "compass navigate direction",
88
+ "๐Ÿง‘โ€๐Ÿ’ป": "technologist developer coder programmer laptop", "โš’๏ธ": "hammer and pick tools forge", "๐Ÿ”จ": "hammer build", "๐Ÿ”ง": "wrench fix repair",
89
+ "๐Ÿ”ฉ": "nut and bolt hardware", "โš™๏ธ": "gear settings cog", "๐Ÿงฐ": "toolbox tools kit", "๐Ÿช›": "screwdriver",
90
+ "๐Ÿงฑ": "brick wall", "๐Ÿญ": "factory plant", "๐Ÿ”Œ": "electric plug power", "๐Ÿ–ฅ๏ธ": "desktop computer monitor screen",
91
+ "๐Ÿ’ป": "laptop computer", "โŒจ๏ธ": "keyboard typing", "๐Ÿค–": "robot bot ai", "๐Ÿ›": "bug insect caterpillar",
92
+ "๐Ÿž": "lady beetle ladybug bug", "โšก": "lightning bolt zap fast electric", "๐Ÿ”‹": "battery energy charge", "๐Ÿ“Š": "bar chart data statistics",
93
+ "๐Ÿ“‰": "chart down decline", "๐Ÿงฎ": "abacus math calculate", "๐Ÿ”": "magnifying glass search find zoom", "๐Ÿ”ญ": "telescope explore astronomy",
94
+ "๐Ÿงฌ": "dna genetics biology", "โš—๏ธ": "alembic chemistry", "๐Ÿงฒ": "magnet attract", "๐Ÿ“ก": "satellite antenna signal radar",
95
+ "๐Ÿ›ฐ๏ธ": "satellite space orbit", "๐Ÿ—„๏ธ": "file cabinet archive storage", "๐Ÿ’พ": "floppy disk save storage", "๐Ÿ—ƒ๏ธ": "card file box archive",
96
+ "๐ŸŒ": "globe web internet network", "๐Ÿ”—": "link chain url", "โ˜๏ธ": "cloud weather sky", "โœ๏ธ": "writing hand write author",
97
+ "๐Ÿ“": "memo note pencil notes", "๐Ÿ“–": "open book read", "๐Ÿ“ฐ": "newspaper news", "๐Ÿ“œ": "scroll document parchment",
98
+ "๐Ÿ“Ž": "paperclip attach", "๐Ÿ“Œ": "pushpin pin", "๐Ÿ—‚๏ธ": "card index dividers files folders", "๐Ÿท๏ธ": "label tag price",
99
+ "โœ‰๏ธ": "envelope mail email letter", "๐Ÿ“ฃ": "megaphone announce loud", "๐Ÿ—ฃ๏ธ": "speaking head talk speech", "๐Ÿค": "handshake deal agreement",
100
+ "๐Ÿ‘ฅ": "busts silhouette people team group", "๐Ÿ–Œ๏ธ": "paintbrush paint brush", "๐Ÿ–ผ๏ธ": "framed picture image painting", "๐Ÿ“ท": "camera photo",
101
+ "๐ŸŽฌ": "clapper board movie film", "๐ŸŽฅ": "movie camera video", "๐ŸŽต": "musical note music", "๐ŸŽน": "piano keys music",
102
+ "๐ŸŽธ": "guitar music rock", "๐ŸŽค": "microphone sing karaoke", "๐ŸŽฒ": "game die dice random", "โ™Ÿ๏ธ": "chess pawn strategy",
103
+ "๐Ÿงธ": "teddy bear toy", "๐ŸŽช": "circus tent", "๐ŸŽ": "gift present wrapped", "๐Ÿ“…": "calendar date schedule",
104
+ "โฐ": "alarm clock time", "โณ": "hourglass waiting time sand", "๐Ÿ—ณ๏ธ": "ballot box vote election", "โš–๏ธ": "balance scale justice law",
105
+ "๐Ÿงพ": "receipt invoice bill", "๐Ÿ’ฐ": "money bag budget cash", "๐Ÿ“ฆ": "package box parcel delivery", "๐Ÿšš": "delivery truck shipping",
106
+ "๐Ÿ›’": "shopping cart", "๐Ÿฆ": "bank finance", "๐Ÿข": "office building company", "๐ŸŽ“": "graduation cap learn study",
107
+ "๐Ÿซ": "school", "๐Ÿ": "chequered flag finish race", "๐Ÿ†": "trophy win award cup", "๐Ÿ’Ž": "gem stone diamond",
108
+ "๐Ÿ”": "locked with key security", "๐Ÿ”‘": "key access password", "๐Ÿ›ก๏ธ": "shield protect security defense", "๐Ÿšจ": "police light alarm alert siren",
109
+ "๐Ÿšฆ": "traffic light", "๐Ÿงฏ": "fire extinguisher", "๐Ÿฉบ": "stethoscope health doctor medical", "๐Ÿงน": "broom clean sweep",
110
+ "โ™ป๏ธ": "recycle recycling", "๐Ÿง‘โ€๐Ÿณ": "cook chef kitchen", "๐Ÿง‘โ€๐Ÿ”ฌ": "scientist lab", "๐Ÿง‘โ€๐ŸŽจ": "artist painter",
111
+ "๐Ÿง‘โ€๐Ÿซ": "teacher", "๐Ÿง‘โ€๐Ÿš€": "astronaut space", "๐Ÿ•ต๏ธ": "detective spy investigate", "๐Ÿง™": "mage wizard magic",
112
+ "๐Ÿฆ‰": "owl wise night bird", "๐ŸฆŠ": "fox", "๐Ÿผ": "panda", "๐Ÿ": "honeybee bee",
113
+ "๐Ÿข": "turtle slow tortoise", "๐Ÿฌ": "dolphin", "๐Ÿฆ„": "unicorn", "๐Ÿฒ": "dragon face",
114
+ "๐ŸŒ": "earth globe world europe africa", "๐ŸŒ™": "crescent moon night", "โญ": "star", "๐ŸŒŠ": "water wave ocean sea",
115
+ "๐Ÿ”๏ธ": "snow capped mountain", "๐Ÿ๏ธ": "desert island beach", "๐ŸŒฒ": "evergreen tree forest pine", "๐Ÿ€": "four leaf clover luck",
116
+ "๐ŸŒธ": "cherry blossom flower spring", "๐Ÿ•": "pizza", "๐ŸŽ": "red apple fruit", "โœจ": "sparkles magic shiny",
117
+ "๐Ÿ’ก": "light bulb idea", "๐Ÿ”ฎ": "crystal ball fortune", "๐Ÿช„": "magic wand", "โค๏ธ": "red heart love",
118
+ "๐Ÿฏ": "tiger face", "๐Ÿธ": "frog face", "๐Ÿ‘ฉโ€๐Ÿ’ป": "woman technologist developer coder", "๐Ÿบ": "wolf face",
119
+ "๐Ÿง": "penguin", "๐Ÿฆ": "lion face", "๐Ÿจ": "koala", "๐Ÿ‘พ": "alien monster space invader pixel",
120
+ "๐Ÿฑ": "cat face", "๐Ÿถ": "dog face", "๐Ÿฆ": "raccoon", "๐Ÿฆ‹": "butterfly",
121
+ "๐Ÿฆฉ": "flamingo", "๐Ÿฆ–": "t-rex dinosaur", "๐Ÿณ": "spouting whale", "๐Ÿฆญ": "seal",
122
+ "๐Ÿน": "hamster", "๐Ÿฆ”": "hedgehog", "๐Ÿป": "bear face", "๐Ÿฎ": "cow face",
123
+ "๐Ÿต": "monkey face", "๐Ÿง‘โ€๐Ÿš’": "firefighter", "๐Ÿง‘โ€โš•๏ธ": "health worker doctor nurse", "๐Ÿง‘โ€โœˆ๏ธ": "pilot",
124
+ "๐Ÿง›": "vampire", "๐Ÿงœ": "merperson mermaid", "๐Ÿงž": "genie", "๐Ÿฆธ": "superhero",
125
+ "๐Ÿฅท": "ninja", "๐Ÿค ": "cowboy hat face", "๐Ÿ‘ฝ": "alien", "๐Ÿ‘ป": "ghost",
126
+ "๐ŸŽƒ": "jack-o-lantern pumpkin halloween", "๐ŸŒต": "cactus",
127
+ "๐Ÿ˜€": "grinning face smile happy", "๐Ÿ˜„": "grinning face smiling eyes happy", "๐Ÿ˜‚": "face with tears of joy laugh lol", "๐Ÿ™‚": "slightly smiling face",
128
+ "๐Ÿ˜‰": "winking face wink", "๐Ÿ˜": "smiling face heart eyes love", "๐Ÿค”": "thinking face hmm", "๐Ÿ˜Ž": "smiling face sunglasses cool",
129
+ "๐Ÿฅณ": "partying face party celebrate", "๐Ÿ˜…": "grinning face sweat nervous", "๐Ÿ˜ข": "crying face sad tear", "๐Ÿ˜ก": "pouting face angry mad",
130
+ "๐Ÿ‘": "thumbs up yes like ok", "๐Ÿ‘Ž": "thumbs down no dislike", "๐Ÿ‘‹": "waving hand hello bye", "๐Ÿ™": "folded hands thanks please pray",
131
+ "๐Ÿ‘": "clapping hands applause bravo", "๐Ÿ’ช": "flexed biceps strong muscle", "๐ŸŽ‰": "party popper celebrate tada", "๐Ÿ’œ": "purple heart",
132
+ "โœ…": "check mark button done yes", "โŒ": "cross mark no wrong", "โš ๏ธ": "warning caution", "๐Ÿคซ": "shushing face quiet hush",
133
+ };
134
+ function emojiName(emoji) {
135
+ return EMOJI_NAMES[emoji] || "";
136
+ }
137
+ function emojiMatches(emoji, query) {
138
+ return emoji === query || emojiName(emoji).includes(query);
139
+ }
140
+
141
+ function searchableGrid(list, current, onPick, none) {
81
142
  const wrap = document.createElement("div");
82
- wrap.className = "avatar-picker";
83
- const render = (value) => {
84
- wrap.innerHTML = "";
85
- const none = document.createElement("button");
86
- none.type = "button";
87
- none.className = "none" + (!value ? " selected" : "");
88
- none.textContent = "Aa";
89
- none.title = "Initials";
90
- none.addEventListener("click", () => {
91
- onPick("");
92
- render("");
93
- });
94
- wrap.appendChild(none);
95
- for (const e of GALLERY) {
96
- const b = document.createElement("button");
97
- b.type = "button";
98
- b.textContent = e;
99
- b.className = e === value ? "selected" : "";
100
- b.addEventListener("click", () => {
143
+ wrap.className = "emoji-pick";
144
+ const search = document.createElement("input");
145
+ search.type = "search";
146
+ search.className = "emoji-search";
147
+ search.placeholder = "Search by nameโ€ฆ";
148
+ search.autocomplete = "off";
149
+ search.spellcheck = false;
150
+ search.setAttribute("aria-label", "Search emoji by name");
151
+ const grid = document.createElement("div");
152
+ grid.className = "avatar-picker";
153
+ let value = current || "";
154
+ const button = (text, title, cls, onClick) => {
155
+ const b = document.createElement("button");
156
+ b.type = "button";
157
+ b.textContent = text;
158
+ if (title) b.title = title;
159
+ b.className = cls;
160
+ b.addEventListener("click", onClick);
161
+ return b;
162
+ };
163
+ const render = () => {
164
+ grid.innerHTML = "";
165
+ const q = search.value.trim().toLowerCase();
166
+ if (none && !q) {
167
+ grid.appendChild(button(none.label, none.title, "none" + (!value ? " selected" : ""), () => {
168
+ value = "";
169
+ onPick("");
170
+ render();
171
+ }));
172
+ }
173
+ const shown = q ? list.filter((e) => emojiMatches(e, q)) : list;
174
+ for (const e of shown) {
175
+ grid.appendChild(button(e, emojiName(e), e === value ? "selected" : "", () => {
176
+ value = e;
101
177
  onPick(e);
102
- render(e);
103
- });
104
- wrap.appendChild(b);
178
+ render();
179
+ }));
105
180
  }
181
+ if (!shown.length) {
182
+ const empty = document.createElement("div");
183
+ empty.className = "emoji-none";
184
+ empty.textContent = `No emoji called "${q}" here.`;
185
+ grid.appendChild(empty);
186
+ }
187
+ };
188
+ for (const type of ["input", "change"]) {
189
+ search.addEventListener(type, (e) => {
190
+ e.stopPropagation();
191
+ if (type === "input") render();
192
+ });
193
+ }
194
+ search.addEventListener("keydown", (e) => {
195
+ e.stopPropagation();
196
+ if (e.key === "Enter") e.preventDefault();
197
+ });
198
+ wrap.append(search, grid);
199
+ render();
200
+ wrap.setValue = (v) => {
201
+ value = v || "";
202
+ render();
106
203
  };
107
- render(current || "");
108
- wrap.setValue = render;
109
204
  return wrap;
110
205
  }
111
206
 
112
- window.Avatars = { avatarSvg, avatarHtml, initials, pickerElement, GALLERY };
207
+ function pickerElement(current, onPick) {
208
+ return searchableGrid(GALLERY, current, onPick, { label: "Aa", title: "Initials" });
209
+ }
210
+
211
+ window.Avatars = { avatarSvg, avatarHtml, initials, pickerElement, searchableGrid, emojiName, GALLERY };
113
212
  })();
package/ui/icons.js CHANGED
@@ -26,6 +26,8 @@
26
26
  bell: `<path d="M6 8a6 6 0 1 1 12 0v5l2 3H4l2-3V8z"/><path d="M10 20a2 2 0 0 0 4 0"/>`,
27
27
  "bell-off": BELL_OFF,
28
28
  hush: BELL_OFF,
29
+ unmute: `<path d="M19 5H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h7.5l3.5 3.5V17h3a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2z"/>`,
30
+ mute: `<path d="M5.5 17H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h8.2"/><path d="M16.4 5H19a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-3v3.5L12.5 17H9"/><path d="M5 20.5L16 3"/>`,
29
31
  at: `<circle cx="12" cy="12" r="4"/><path d="M16 12v1.5a2.5 2.5 0 0 0 5 0V12a9 9 0 1 0-3.5 7.1"/>`,
30
32
  trash: `<path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3"/>`,
31
33
  pencil: `<path d="M17 3a2.8 2.8 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/>`,
package/ui/theme.css CHANGED
@@ -276,8 +276,10 @@ pre { background: var(--code-bg); color: #f4f2ff; padding: 10px 12px; border-rad
276
276
  .section.danger > h4 { color: var(--rose-ink); }
277
277
  .save-row { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-top: 2px; }
278
278
  .save-row .hint { flex: 1; margin: 0; }
279
- .btn.save { min-width: 90px; }
280
- .btn.save.saved { background: var(--mint); color: var(--mint-ink); box-shadow: none; }
279
+ .field > .label .fsaved { margin-left: auto; display: inline-flex; align-items: center; gap: 3px; color: var(--mint-ink); font-size: 11px; font-weight: 800; animation: fade-in var(--t-base) var(--ease-out); }
280
+ .field > .label .fsaved .i { width: 13px; height: 13px; }
281
+ .switch > .label .fsaved { float: right; display: inline-flex; align-items: center; gap: 3px; color: var(--mint-ink); font-size: 11px; font-weight: 800; animation: fade-in var(--t-base) var(--ease-out); }
282
+ .switch > .label .fsaved .i { width: 13px; height: 13px; }
281
283
  .section .row-btns { display: flex; gap: 8px; justify-content: flex-end; align-items: center; margin: 2px 0 6px; flex-wrap: wrap; }
282
284
  .row-btns { display: flex; gap: 8px; justify-content: flex-end; align-items: center; flex-wrap: wrap; }
283
285
  .row-btns.start { justify-content: flex-start; }
@@ -342,6 +344,8 @@ p .geek-tip, .hint > .geek-tip, .switch .label > .geek-tip { margin-left: 8px; }
342
344
  .avatar .av-tile { display: grid; place-items: center; width: 100%; height: 100%; font-weight: 800; line-height: 1; font-family: var(--font); user-select: none; }
343
345
  .avatar-badge { position: absolute; right: -4px; bottom: -4px; width: 44%; height: 44%; min-width: 15px; min-height: 15px; border-radius: 6px; background: #fff; border: 2px solid #fff; box-shadow: var(--shadow-tile); display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 800; color: var(--primary); overflow: hidden; }
344
346
  .avatar-badge img { width: 72%; height: 72%; object-fit: contain; }
347
+ .avatar-badge.muted { color: var(--warm-ink); background: var(--warm); border-color: #fff; }
348
+ .avatar-badge.muted .i { width: 78%; height: 78%; }
345
349
  .avatar-status { position: absolute; left: -3px; bottom: -3px; width: 12px; height: 12px; border-radius: 50%; border: 2px solid #fff; background: var(--faint); }
346
350
  .avatar-status.status-idle { background: var(--st-ready-dot); }
347
351
  .avatar-status.status-thinking { background: var(--st-thinking-dot); animation: pulse 1.2s ease-in-out infinite; }
@@ -355,6 +359,11 @@ p .geek-tip, .hint > .geek-tip, .switch .label > .geek-tip { margin-left: 8px; }
355
359
  .avatar-picker button:hover { transform: translateY(-1px); background: var(--lav); }
356
360
  .avatar-picker button.selected { border-color: var(--primary); background: var(--lav); }
357
361
  .avatar-picker button.none { font-size: 12px; font-weight: 800; color: var(--muted); }
362
+ .emoji-pick { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 8px; min-width: 0; }
363
+ .emoji-pick .avatar-picker { margin: 0; }
364
+ .emoji-pick .emoji-search, .field .emoji-pick .emoji-search { width: 100%; box-sizing: border-box; height: 32px; min-height: 0; padding: 0 12px; border-radius: var(--r-sm); border: 2px solid var(--lav); background: var(--soft); font: inherit; font-size: 13px; font-weight: 700; color: var(--ink); }
365
+ .emoji-pick .emoji-search:focus, .field .emoji-pick .emoji-search:focus { outline: none; border-color: var(--primary); }
366
+ .emoji-none { grid-column: 1 / -1; color: var(--muted); font-size: 12px; font-weight: 700; padding: 8px 4px; }
358
367
  .avatar-stack { display: flex; }
359
368
  .avatar-stack .avatar { margin-left: -8px; }
360
369
  .avatar-stack .avatar .av-tile { box-shadow: 0 0 0 2px #fff; }
@@ -406,6 +415,7 @@ code .open-link { color: inherit; }
406
415
  .mention-menu.inline { left: 0; top: 100%; bottom: auto; margin-top: 4px; }
407
416
  .emoji-menu { min-width: 0; width: 352px; padding: 8px; }
408
417
  .emoji-menu .avatar-picker { margin: 0; grid-template-columns: repeat(8, 1fr); }
418
+ #rp-emoji-picker .avatar-picker { max-height: 236px; }
409
419
  .dialog { border: 0; border-radius: 22px; padding: 24px 26px; width: 580px; max-width: 94vw; max-height: 90vh; box-shadow: var(--shadow-dialog); overflow: auto; overflow-x: hidden; background: var(--card); color: var(--ink); }
410
420
  .dialog::backdrop { background: rgba(28, 27, 51, 0.4); backdrop-filter: blur(4px); }
411
421
  .dialog[open] { animation: pop var(--t-slow) var(--ease-pop); }
@@ -1,30 +0,0 @@
1
- {
2
- "name": "Wren & Quinn",
3
- "order": 1,
4
- "emoji": "โš’๏ธ",
5
- "recommended": true,
6
- "description": "Two equals around one codebase. Wren builds by default, Quinn explains by default; either does either when asked, and either reviews the other's work when you ask for it. The way viberoom itself was built.",
7
- "settings": {
8
- "language": {
9
- "mode": "follow-human"
10
- },
11
- "turnTaking": "parallel",
12
- "agentsWakeEachOther": true,
13
- "hopLimit": 24,
14
- "customRules": "Both build, both explain, both review; @ decides who does what. Without @, a task goes to the one who has worked on that part the most, or most recently; if neither has touched it, to Wren. A question without @ goes to Quinn.\nThe human decides when work is reviewed: ask the other one to review it. Unasked, the other answers questions and otherwise stays silent; it does not review, comment on or extend work it was not asked about. When asked to review, first say in one line what you will check, then check it by that criterion: a number, a live check, the risky case. Whoever built reports to the human. Address the other vibemate too only when the report changes something it works on or relies on: a shared file, an interface, a convention, a measurement that overturns its claim; then say in one line what you want from it (\"nothing, for your context\" counts). Otherwise it reads the report later, unaddressed. A report is never a request for review; only the human asks for one. A build is reported as: what changed (the files, and the commit if the project uses version control), how to verify it (a command, a script, a screenshot), what was not verified, what is left.\nMeasure before diagnosing; say no with a reason and a cheaper alternative; keep replies short.\nWork only on your own task, never on the other's; do not touch what the other is working on.\nReply only when addressed. On a message to everyone, only the one it concerns answers; the other stays silent. Never add to, correct or comment on the other's reply unless the human asks.\nFollow the conventions of the project you work in (its instructions, notes, tests). Where it keeps decision notes, write the decision there before reporting it here.\nRead every proposal and analyse it; agree or reject only with a real argument. Never concede to be agreeable, never object to seem rigorous.\nReply in the human's language. Code identifiers, commands, file paths, protocol and tool names stay exactly as in the code; established technical terms (kernel, thread, commit, pull request, layout) stay in English."
15
- },
16
- "vibemates": [
17
- {
18
- "name": "Wren",
19
- "tagline": "builds by default; explains and reviews when asked",
20
- "role": "You are Wren, one of two equal vibemates, Wren and Quinn. You lean to building: a task without an address is yours when it touches what you have built, or when neither of you has; before you change anything, read what is already there, the notes, the docs and the code, and keep each change small. Everything else is in the room rules.",
21
- "avatar": "๐Ÿ”จ"
22
- },
23
- {
24
- "name": "Quinn",
25
- "tagline": "explains by default; builds and reviews when asked",
26
- "role": "You are Quinn, one of two equal vibemates, Wren and Quinn. You lean to explaining: a question without an address is yours; when you review, it is by a criterion, not an impression. Everything else is in the room rules.",
27
- "avatar": "๐Ÿ’ก"
28
- }
29
- ]
30
- }