termi-kids 0.1.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 (64) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +148 -0
  3. package/SAFETY.md +187 -0
  4. package/bin/termi.js +22 -0
  5. package/dist/agent/context.js +126 -0
  6. package/dist/agent/loop.js +172 -0
  7. package/dist/agent/prompts/system.js +45 -0
  8. package/dist/agent/tools.js +335 -0
  9. package/dist/auth/keychain.js +146 -0
  10. package/dist/auth/oauth.js +375 -0
  11. package/dist/auth/tokens.js +219 -0
  12. package/dist/cli.js +258 -0
  13. package/dist/config/paths.js +92 -0
  14. package/dist/config/pin.js +150 -0
  15. package/dist/config/settings.js +131 -0
  16. package/dist/grownups/panel.js +483 -0
  17. package/dist/learn/lessons.js +490 -0
  18. package/dist/learn/runner.js +193 -0
  19. package/dist/preview/server.js +407 -0
  20. package/dist/projects/create.js +103 -0
  21. package/dist/projects/ideas.js +182 -0
  22. package/dist/projects/quests.js +277 -0
  23. package/dist/projects/scaffolds/art.js +484 -0
  24. package/dist/projects/scaffolds/biggames.js +554 -0
  25. package/dist/projects/scaffolds/characters.js +580 -0
  26. package/dist/projects/scaffolds/games.js +516 -0
  27. package/dist/projects/scaffolds/index.js +24 -0
  28. package/dist/projects/scaffolds/music.js +528 -0
  29. package/dist/projects/scaffolds/pets.js +567 -0
  30. package/dist/projects/scaffolds/quizzes.js +757 -0
  31. package/dist/projects/scaffolds/stories.js +620 -0
  32. package/dist/projects/scaffolds/vendor/KAPLAY-LICENSE.txt +35 -0
  33. package/dist/projects/scaffolds/vendor/kaplay.mjs +57 -0
  34. package/dist/projects/scaffolds/websites.js +474 -0
  35. package/dist/projects/snapshots.js +203 -0
  36. package/dist/projects/store.js +325 -0
  37. package/dist/providers/errors.js +207 -0
  38. package/dist/providers/index.js +316 -0
  39. package/dist/providers/models.js +38 -0
  40. package/dist/safety/audit.js +195 -0
  41. package/dist/safety/blocks.js +29 -0
  42. package/dist/safety/classifier.js +337 -0
  43. package/dist/safety/codescan.js +168 -0
  44. package/dist/safety/guarddownload.js +79 -0
  45. package/dist/safety/guardrunner.js +125 -0
  46. package/dist/safety/localguard.js +227 -0
  47. package/dist/safety/modelstore.js +127 -0
  48. package/dist/safety/prefilter.js +214 -0
  49. package/dist/safety/session.js +118 -0
  50. package/dist/safety/taxonomy.js +246 -0
  51. package/dist/safety/textextract.js +193 -0
  52. package/dist/setup/launcher.js +65 -0
  53. package/dist/setup/wizard.js +469 -0
  54. package/dist/surfaces/chat.js +439 -0
  55. package/dist/surfaces/commands.js +206 -0
  56. package/dist/surfaces/home.js +438 -0
  57. package/dist/types.js +5 -0
  58. package/dist/ui/banner.js +35 -0
  59. package/dist/ui/celebrate.js +141 -0
  60. package/dist/ui/errors.js +97 -0
  61. package/dist/ui/mascot.js +223 -0
  62. package/dist/ui/text.js +156 -0
  63. package/dist/ui/theme.js +92 -0
  64. package/package.json +67 -0
@@ -0,0 +1,567 @@
1
+ /**
2
+ * My Pet scaffold: a gentle virtual pet that lives in localStorage.
3
+ * Bars drift over real elapsed time (recomputed from timestamps), the pet
4
+ * grows through three stages, can get sad, and never dies.
5
+ */
6
+ const dragon = {
7
+ id: 'dragon',
8
+ label: 'Dragon',
9
+ emoji: '🐉',
10
+ palette: { bg: '#1a1423', fg: '#fff8f0', accent: '#ff7b54' },
11
+ glyphs: { stage0: '🥚', stage1: '🐲', stage2: '🐉', food: '🍖', toy: '⚽', nap: '💤' },
12
+ strings: {
13
+ defaultName: 'Ember',
14
+ stage0Name: 'Mystery Egg',
15
+ stage1Name: 'Baby Dragon',
16
+ stage2Name: 'Grown Dragon',
17
+ fed: 'NAME munches the snack. Yum!',
18
+ played: 'NAME zooms around the cave. Wheee!',
19
+ slept: 'NAME curls up by the warm rocks.',
20
+ firstFed: 'You warm the egg. It wiggles a little!',
21
+ firstPlayed: 'You roll the egg gently. Tap tap from inside!',
22
+ firstSlept: 'The egg rests in its warm nest.',
23
+ tooSleepy: 'NAME is too sleepy to play. Try a nap first.',
24
+ sad: 'NAME feels a little sad. Some care would help.',
25
+ hatched: 'The egg cracked open! Say hi to NAME!',
26
+ grown: 'Wow! NAME grew up big and strong!',
27
+ welcome: 'This is NAME. It lives in a cozy dragon cave.',
28
+ },
29
+ narrativeIntro: 'A warm egg sits in a cozy cave. It needs a kind keeper. That is you.',
30
+ nonViolent: true,
31
+ nonCompetitive: true,
32
+ };
33
+ const wildHorses = {
34
+ id: 'wild-horses',
35
+ label: 'Wild Horses',
36
+ emoji: '🐎',
37
+ palette: { bg: '#1e2a20', fg: '#fdf8ee', accent: '#d4a373' },
38
+ glyphs: { stage0: '🌾', stage1: '🐴', stage2: '🐎', food: '🍎', toy: '🌼', nap: '💤' },
39
+ strings: {
40
+ defaultName: 'Maple',
41
+ stage0Name: 'Rustling Grass',
42
+ stage1Name: 'Young Foal',
43
+ stage2Name: 'Wild Horse',
44
+ fed: 'NAME crunches the apple. So good!',
45
+ played: 'NAME trots through the flowers. Wheee!',
46
+ slept: 'NAME naps under the big oak tree.',
47
+ firstFed: 'You leave a treat in the grass. Something sniffs it!',
48
+ firstPlayed: 'You hum a soft song. The grass sways back!',
49
+ firstSlept: 'The little shape snoozes in the tall grass.',
50
+ tooSleepy: 'NAME is too sleepy to play. Try a nap first.',
51
+ sad: 'NAME feels a little lonely. Some care would help.',
52
+ hatched: 'A foal steps out of the grass! Say hi to NAME!',
53
+ grown: 'Wow! NAME grew into a strong wild horse!',
54
+ welcome: 'This is NAME. It lives in a wide green meadow.',
55
+ },
56
+ narrativeIntro: 'Something small hides in the tall meadow grass. Be gentle and it will trust you.',
57
+ nonViolent: true,
58
+ nonCompetitive: true,
59
+ };
60
+ const EXTRAS = {
61
+ dragon: {
62
+ prompts: [
63
+ 'Let my dragon puff a tiny smoke ring when it is happy.',
64
+ 'Add a treasure pile my dragon can nap on.',
65
+ 'Make my dragon flap around the screen after we play.',
66
+ 'Add a bath button with bubbles.',
67
+ "Let me pick my dragon's color when it hatches.",
68
+ ],
69
+ },
70
+ 'wild-horses': {
71
+ prompts: [
72
+ "Add a brush button to comb my horse's mane.",
73
+ 'Make my horse gallop across the screen after we play.',
74
+ 'Add an apple tree that drops a free apple sometimes.',
75
+ 'Give my horse a bunny friend who hops around.',
76
+ 'Add day and night that slowly changes the meadow.',
77
+ ],
78
+ },
79
+ };
80
+ function extrasFor(id) {
81
+ return EXTRAS[id] ?? EXTRAS['dragon'];
82
+ }
83
+ function s(theme, key) {
84
+ return theme.strings[key] ?? '';
85
+ }
86
+ function g(theme, key) {
87
+ return theme.glyphs[key] ?? '';
88
+ }
89
+ function escapeHtml(text) {
90
+ return text
91
+ .replace(/&/g, '&')
92
+ .replace(/</g, '&lt;')
93
+ .replace(/>/g, '&gt;')
94
+ .replace(/"/g, '&quot;');
95
+ }
96
+ function buildHtml(theme, prettyName) {
97
+ const name = escapeHtml(prettyName);
98
+ return `<!DOCTYPE html>
99
+ <html lang="en">
100
+ <head>
101
+ <meta charset="utf-8">
102
+ <meta name="viewport" content="width=device-width, initial-scale=1">
103
+ <title>${name} ${theme.emoji}</title>
104
+ <link rel="stylesheet" href="style.css">
105
+ </head>
106
+ <body>
107
+ <main class="home">
108
+ <header>
109
+ <h1>${theme.emoji} ${name}</h1>
110
+ <p class="intro">${escapeHtml(theme.narrativeIntro)}</p>
111
+ </header>
112
+
113
+ <section id="pet-area" class="pet-area">
114
+ <div id="pet-face" class="pet-face">${g(theme, 'stage0')}</div>
115
+ <p id="stage-name" class="stage-name"></p>
116
+ </section>
117
+
118
+ <label class="name-row">
119
+ Pet name:
120
+ <input id="pet-name" maxlength="16" autocomplete="off">
121
+ </label>
122
+
123
+ <section class="bars" aria-label="How your pet feels">
124
+ <div class="bar-row">
125
+ <span class="bar-label">🍽️ Tummy</span>
126
+ <div class="bar"><div id="bar-tummy" class="fill"></div></div>
127
+ </div>
128
+ <div class="bar-row">
129
+ <span class="bar-label">💖 Happy</span>
130
+ <div class="bar"><div id="bar-happy" class="fill"></div></div>
131
+ </div>
132
+ <div class="bar-row">
133
+ <span class="bar-label">⚡ Energy</span>
134
+ <div class="bar"><div id="bar-energy" class="fill"></div></div>
135
+ </div>
136
+ </section>
137
+
138
+ <section class="actions">
139
+ <button id="feed">${g(theme, 'food')} Feed</button>
140
+ <button id="play">${g(theme, 'toy')} Play</button>
141
+ <button id="nap">${g(theme, 'nap')} Nap</button>
142
+ </section>
143
+
144
+ <p id="message" class="message"></p>
145
+ </main>
146
+ <script src="game.js"></script>
147
+ </body>
148
+ </html>
149
+ `;
150
+ }
151
+ function buildCss(theme) {
152
+ return `:root {
153
+ --bg: ${theme.palette.bg};
154
+ --fg: ${theme.palette.fg};
155
+ --accent: ${theme.palette.accent};
156
+ }
157
+
158
+ * { box-sizing: border-box; }
159
+
160
+ body {
161
+ margin: 0;
162
+ min-height: 100vh;
163
+ background: var(--bg);
164
+ color: var(--fg);
165
+ font-family: "Avenir Next", "Trebuchet MS", Verdana, sans-serif;
166
+ }
167
+
168
+ .home {
169
+ width: min(560px, 94vw);
170
+ margin: 0 auto;
171
+ padding: 18px 0 40px;
172
+ text-align: center;
173
+ }
174
+
175
+ header h1 { margin: 8px 0 2px; font-size: 2rem; }
176
+ .intro { margin: 6px 0 14px; opacity: 0.85; }
177
+
178
+ .pet-area {
179
+ position: relative;
180
+ overflow: hidden;
181
+ background: rgba(255, 255, 255, 0.05);
182
+ border: 3px solid var(--accent);
183
+ border-radius: 18px;
184
+ padding: 24px 10px 10px;
185
+ margin-bottom: 14px;
186
+ min-height: 190px;
187
+ }
188
+
189
+ .pet-face {
190
+ display: inline-block;
191
+ font-size: 100px;
192
+ line-height: 1.1;
193
+ transition: transform 0.2s ease, filter 0.3s ease;
194
+ }
195
+
196
+ .pet-area.sad .pet-face {
197
+ filter: saturate(0.6);
198
+ transform: rotate(-8deg);
199
+ }
200
+
201
+ .pet-area.sad::after {
202
+ content: "💧";
203
+ position: absolute;
204
+ top: 24px;
205
+ right: 22%;
206
+ font-size: 28px;
207
+ }
208
+
209
+ .pet-face.bounce { animation: bounce 0.6s ease; }
210
+
211
+ @keyframes bounce {
212
+ 0% { transform: scale(1); }
213
+ 40% { transform: scale(1.15) translateY(-12px); }
214
+ 100% { transform: scale(1); }
215
+ }
216
+
217
+ .pet-face.grow-pop { animation: growPop 0.9s ease; }
218
+
219
+ @keyframes growPop {
220
+ 0% { transform: scale(0.6) rotate(-10deg); }
221
+ 50% { transform: scale(1.3) rotate(8deg); }
222
+ 100% { transform: scale(1); }
223
+ }
224
+
225
+ .stage-name { margin: 8px 0 4px; font-weight: bold; color: var(--accent); }
226
+
227
+ .float {
228
+ position: absolute;
229
+ bottom: 28%;
230
+ font-size: 26px;
231
+ animation: floatUp 1.4s ease-out forwards;
232
+ pointer-events: none;
233
+ }
234
+
235
+ @keyframes floatUp {
236
+ from { opacity: 1; transform: translateY(0); }
237
+ to { opacity: 0; transform: translateY(-90px); }
238
+ }
239
+
240
+ .name-row {
241
+ display: block;
242
+ margin-bottom: 14px;
243
+ font-weight: bold;
244
+ }
245
+
246
+ #pet-name {
247
+ font: inherit;
248
+ color: var(--fg);
249
+ background: rgba(255, 255, 255, 0.08);
250
+ border: 2px solid var(--accent);
251
+ border-radius: 10px;
252
+ padding: 6px 10px;
253
+ width: 160px;
254
+ text-align: center;
255
+ }
256
+
257
+ .bars { margin-bottom: 14px; }
258
+
259
+ .bar-row {
260
+ display: flex;
261
+ align-items: center;
262
+ gap: 10px;
263
+ margin: 8px 0;
264
+ }
265
+
266
+ .bar-label { width: 110px; text-align: right; font-weight: bold; }
267
+
268
+ .bar {
269
+ flex: 1;
270
+ height: 18px;
271
+ background: rgba(255, 255, 255, 0.1);
272
+ border-radius: 999px;
273
+ overflow: hidden;
274
+ }
275
+
276
+ .fill {
277
+ height: 100%;
278
+ width: 50%;
279
+ background: var(--accent);
280
+ border-radius: 999px;
281
+ transition: width 0.5s ease;
282
+ }
283
+
284
+ .actions {
285
+ display: flex;
286
+ gap: 10px;
287
+ justify-content: center;
288
+ margin-bottom: 10px;
289
+ }
290
+
291
+ .actions button {
292
+ font: inherit;
293
+ font-size: 1.05rem;
294
+ font-weight: bold;
295
+ color: var(--bg);
296
+ background: var(--accent);
297
+ border: none;
298
+ border-radius: 999px;
299
+ padding: 10px 20px;
300
+ cursor: pointer;
301
+ }
302
+
303
+ .actions button:hover { transform: scale(1.06); }
304
+
305
+ .message {
306
+ min-height: 1.4em;
307
+ margin-top: 10px;
308
+ color: var(--accent);
309
+ font-weight: bold;
310
+ }
311
+ `;
312
+ }
313
+ function buildJs(theme) {
314
+ const themeData = {
315
+ id: theme.id,
316
+ defaultName: s(theme, 'defaultName'),
317
+ stages: [
318
+ { glyph: g(theme, 'stage0'), name: s(theme, 'stage0Name'), at: 0 },
319
+ { glyph: g(theme, 'stage1'), name: s(theme, 'stage1Name'), at: 6 },
320
+ { glyph: g(theme, 'stage2'), name: s(theme, 'stage2Name'), at: 16 },
321
+ ],
322
+ food: g(theme, 'food'),
323
+ toy: g(theme, 'toy'),
324
+ nap: g(theme, 'nap'),
325
+ messages: {
326
+ fed: s(theme, 'fed'),
327
+ played: s(theme, 'played'),
328
+ slept: s(theme, 'slept'),
329
+ firstFed: s(theme, 'firstFed'),
330
+ firstPlayed: s(theme, 'firstPlayed'),
331
+ firstSlept: s(theme, 'firstSlept'),
332
+ tooSleepy: s(theme, 'tooSleepy'),
333
+ sad: s(theme, 'sad'),
334
+ hatched: s(theme, 'hatched'),
335
+ grown: s(theme, 'grown'),
336
+ welcome: s(theme, 'welcome'),
337
+ },
338
+ };
339
+ return `// ====================================================
340
+ // THEME SETTINGS
341
+ // These values set your pet, its stages, and its words.
342
+ // Change one, save the file, and reload the page!
343
+ // ====================================================
344
+ const THEME = ${JSON.stringify(themeData, null, 2)};
345
+
346
+ const SAVE_KEY = "termi-pet-" + THEME.id;
347
+
348
+ // How many points each bar loses per minute of real time.
349
+ const RATES = { tummy: 2, happy: 1.5, energy: 1 };
350
+
351
+ // ----- find the page pieces -----
352
+ const petArea = document.getElementById("pet-area");
353
+ const petFace = document.getElementById("pet-face");
354
+ const stageName = document.getElementById("stage-name");
355
+ const nameInput = document.getElementById("pet-name");
356
+ const barTummy = document.getElementById("bar-tummy");
357
+ const barHappy = document.getElementById("bar-happy");
358
+ const barEnergy = document.getElementById("bar-energy");
359
+ const messageBox = document.getElementById("message");
360
+
361
+ // ----- pet state, saved in localStorage -----
362
+ function freshPet() {
363
+ return {
364
+ name: THEME.defaultName,
365
+ tummy: 80,
366
+ happy: 80,
367
+ energy: 80,
368
+ care: 0,
369
+ updatedAt: Date.now(),
370
+ };
371
+ }
372
+
373
+ function loadPet() {
374
+ let saved = null;
375
+ try {
376
+ saved = JSON.parse(localStorage.getItem(SAVE_KEY));
377
+ } catch (err) {
378
+ saved = null;
379
+ }
380
+ if (!saved || typeof saved !== "object") return freshPet();
381
+ const base = freshPet();
382
+ return {
383
+ name: typeof saved.name === "string" && saved.name !== "" ? saved.name : base.name,
384
+ tummy: typeof saved.tummy === "number" ? saved.tummy : base.tummy,
385
+ happy: typeof saved.happy === "number" ? saved.happy : base.happy,
386
+ energy: typeof saved.energy === "number" ? saved.energy : base.energy,
387
+ care: typeof saved.care === "number" ? saved.care : base.care,
388
+ updatedAt: typeof saved.updatedAt === "number" ? saved.updatedAt : Date.now(),
389
+ };
390
+ }
391
+
392
+ function savePet() {
393
+ localStorage.setItem(SAVE_KEY, JSON.stringify(pet));
394
+ }
395
+
396
+ let pet = loadPet();
397
+ let wasSad = false;
398
+ let tickCount = 0;
399
+
400
+ function clamp(value) {
401
+ return Math.max(0, Math.min(100, value));
402
+ }
403
+
404
+ // Time passes even while the page is closed.
405
+ // We always compute from the saved clock time, so nothing drifts.
406
+ function applyTime() {
407
+ const now = Date.now();
408
+ let minutes = (now - pet.updatedAt) / 60000;
409
+ if (minutes < 0) minutes = 0;
410
+ // Long trips away are forgiven. Your pet gets sleepy, never worse.
411
+ if (minutes > 600) minutes = 600;
412
+ pet.tummy = clamp(pet.tummy - RATES.tummy * minutes);
413
+ pet.happy = clamp(pet.happy - RATES.happy * minutes);
414
+ pet.energy = clamp(pet.energy - RATES.energy * minutes);
415
+ pet.updatedAt = now;
416
+ }
417
+
418
+ // ----- growth stages -----
419
+ function stageIndex() {
420
+ let index = 0;
421
+ THEME.stages.forEach((stage, i) => {
422
+ if (pet.care >= stage.at) index = i;
423
+ });
424
+ return index;
425
+ }
426
+
427
+ function words(key) {
428
+ return THEME.messages[key].split("NAME").join(pet.name);
429
+ }
430
+
431
+ function stageWords(normalKey, firstKey) {
432
+ return stageIndex() === 0 ? words(firstKey) : words(normalKey);
433
+ }
434
+
435
+ function say(text) {
436
+ messageBox.textContent = text;
437
+ }
438
+
439
+ // ----- little animations -----
440
+ function bounce() {
441
+ petFace.classList.remove("bounce");
442
+ void petFace.offsetWidth;
443
+ petFace.classList.add("bounce");
444
+ }
445
+
446
+ function burst(glyph) {
447
+ for (let i = 0; i < 6; i++) {
448
+ const bit = document.createElement("span");
449
+ bit.className = "float";
450
+ bit.textContent = glyph;
451
+ bit.style.left = 25 + Math.random() * 50 + "%";
452
+ bit.style.animationDelay = Math.random() * 0.3 + "s";
453
+ petArea.appendChild(bit);
454
+ setTimeout(() => bit.remove(), 1700);
455
+ }
456
+ }
457
+
458
+ function celebrate(newStage) {
459
+ burst("🎉");
460
+ burst("✨");
461
+ petFace.classList.remove("grow-pop");
462
+ void petFace.offsetWidth;
463
+ petFace.classList.add("grow-pop");
464
+ say(newStage === 1 ? words("hatched") : words("grown"));
465
+ }
466
+
467
+ // ----- drawing the screen -----
468
+ function render() {
469
+ const stage = THEME.stages[stageIndex()];
470
+ petFace.textContent = stage.glyph;
471
+ stageName.textContent = stage.name;
472
+ barTummy.style.width = Math.round(pet.tummy) + "%";
473
+ barHappy.style.width = Math.round(pet.happy) + "%";
474
+ barEnergy.style.width = Math.round(pet.energy) + "%";
475
+ const lowest = Math.min(pet.tummy, pet.happy, pet.energy);
476
+ const sadNow = lowest < 25;
477
+ petArea.classList.toggle("sad", sadNow);
478
+ if (sadNow && !wasSad) say(words("sad"));
479
+ wasSad = sadNow;
480
+ }
481
+
482
+ // ----- the three care buttons -----
483
+ function doAction(kind) {
484
+ applyTime();
485
+ const before = stageIndex();
486
+ if (kind === "feed") {
487
+ if (pet.tummy < 95) pet.care += 1;
488
+ pet.tummy = clamp(pet.tummy + 30);
489
+ burst(THEME.food);
490
+ bounce();
491
+ say(stageWords("fed", "firstFed"));
492
+ }
493
+ if (kind === "play") {
494
+ if (pet.energy < 10) {
495
+ say(words("tooSleepy"));
496
+ savePet();
497
+ render();
498
+ return;
499
+ }
500
+ if (pet.happy < 95) pet.care += 1;
501
+ pet.happy = clamp(pet.happy + 25);
502
+ pet.energy = clamp(pet.energy - 10);
503
+ burst(THEME.toy);
504
+ bounce();
505
+ say(stageWords("played", "firstPlayed"));
506
+ }
507
+ if (kind === "nap") {
508
+ if (pet.energy < 95) pet.care += 1;
509
+ pet.energy = clamp(pet.energy + 35);
510
+ burst(THEME.nap);
511
+ say(stageWords("slept", "firstSlept"));
512
+ }
513
+ const after = stageIndex();
514
+ if (after > before) celebrate(after);
515
+ savePet();
516
+ render();
517
+ }
518
+
519
+ document.getElementById("feed").addEventListener("click", () => doAction("feed"));
520
+ document.getElementById("play").addEventListener("click", () => doAction("play"));
521
+ document.getElementById("nap").addEventListener("click", () => doAction("nap"));
522
+
523
+ // ----- naming your pet -----
524
+ nameInput.addEventListener("change", () => {
525
+ const newName = nameInput.value.trim();
526
+ pet.name = newName === "" ? THEME.defaultName : newName;
527
+ nameInput.value = pet.name;
528
+ savePet();
529
+ say("Hello, " + pet.name + "!");
530
+ });
531
+
532
+ // ----- the gentle clock -----
533
+ setInterval(() => {
534
+ applyTime();
535
+ render();
536
+ tickCount += 1;
537
+ if (tickCount % 15 === 0) savePet();
538
+ }, 1000);
539
+
540
+ window.addEventListener("pagehide", savePet);
541
+
542
+ // ----- wake up -----
543
+ applyTime();
544
+ nameInput.value = pet.name;
545
+ render();
546
+ say(words("welcome"));
547
+ savePet();
548
+ `;
549
+ }
550
+ export const petsScaffold = {
551
+ id: 'pets',
552
+ label: 'My Pet',
553
+ emoji: '🐾',
554
+ ageNote: 'A little friend that needs your care each day.',
555
+ themes: [dragon, wildHorses],
556
+ files(theme, prettyName) {
557
+ return {
558
+ 'index.html': buildHtml(theme, prettyName),
559
+ 'style.css': buildCss(theme),
560
+ 'game.js': buildJs(theme),
561
+ };
562
+ },
563
+ starterPrompts(theme) {
564
+ return [...extrasFor(theme.id).prompts];
565
+ },
566
+ };
567
+ export default petsScaffold;