@readme/cli 0.0.31 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +10 -0
  2. package/bin/readme.js +1 -1
  3. package/package.json +34 -24
  4. package/src/bootstrap.js +9 -13
  5. package/src/cli.js +17 -11
  6. package/src/commands/components.js +35 -18
  7. package/src/commands/dev.js +12 -8
  8. package/src/commands/eyes.js +12 -8
  9. package/src/commands/import.js +2263 -2066
  10. package/src/commands/lint.js +33 -29
  11. package/src/commands/oas-bundle.js +89 -0
  12. package/src/commands/oas-sync.js +96 -93
  13. package/src/commands/oas-validate.js +10 -7
  14. package/src/commands/pretty.js +177 -164
  15. package/src/commands/setup.js +24 -26
  16. package/src/commands/versions.js +6 -2
  17. package/src/dev/app/[...slug]/page.js +1 -0
  18. package/src/dev/app/layout.js +18 -11
  19. package/src/dev/app/page.js +1 -0
  20. package/src/dev/lib/docs.js +27 -19
  21. package/src/dev/next.config.mjs +2 -2
  22. package/src/gha.js +7 -7
  23. package/src/prompts/index.js +16 -6
  24. package/src/utils/eyes.js +143 -51
  25. package/src/utils/git.js +3 -3
  26. package/src/utils/lint.js +39 -38
  27. package/src/utils/llms.js +94 -91
  28. package/src/utils/markdown.js +256 -0
  29. package/src/utils/marketplace.js +12 -5
  30. package/src/utils/multi-spinner.js +5 -7
  31. package/src/utils/promise-pool.js +7 -5
  32. package/src/utils/reporter.js +88 -91
  33. package/src/utils/setup-templates.js +12 -6
  34. package/src/utils/styles.js +2 -1
  35. package/src/utils/tamagotchi.js +257 -91
  36. package/src/utils/tips.js +18 -9
  37. package/src/utils/url-segs.js +46 -41
  38. package/src/validators/components.js +55 -63
  39. package/src/validators/content.js +5 -10
  40. package/src/validators/duplicates.js +6 -2
  41. package/src/validators/frontmatter.js +19 -21
  42. package/src/validators/links.js +4 -8
  43. package/src/validators/oas-reference.js +7 -14
  44. package/src/validators/oas-schema.js +2 -0
  45. package/src/validators/ordering.js +5 -5
  46. package/src/validators/recipes.js +4 -12
  47. package/src/commands/import-oas.test.js +0 -277
  48. package/src/commands/import.classify.test.js +0 -172
  49. package/src/commands/import.landing.test.js +0 -217
  50. package/src/commands/import.slug-planner.test.js +0 -86
  51. package/src/commands/import.staging.test.js +0 -158
  52. package/src/commands/import.test.js +0 -382
  53. package/src/utils/llms.test.js +0 -302
  54. package/src/utils/url-segs.test.js +0 -239
@@ -1,8 +1,10 @@
1
1
  import fs from 'node:fs';
2
- import path from 'node:path';
3
2
  import os from 'node:os';
3
+ import path from 'node:path';
4
4
  import readline from 'node:readline';
5
+
5
6
  import chalk from 'chalk';
7
+
6
8
  import { eyes, expressions, palettes, setPalette } from './eyes.js';
7
9
 
8
10
  // ── Data directory (XDG spec) ───────────────────────────
@@ -21,7 +23,16 @@ function getSavePath() {
21
23
  const MAX_STAT = 10;
22
24
  const DECAY_INTERVAL_MS = 1000 * 60 * 5; // stats decay every 5 minutes of real time
23
25
 
24
- const ALL_TRICKS = ['hide and seek', 'spin', 'fetch', 'bow', 'chameleon', 'dance', 'owl impression', 'writes an OAS file'];
26
+ const ALL_TRICKS = [
27
+ 'hide and seek',
28
+ 'spin',
29
+ 'fetch',
30
+ 'bow',
31
+ 'chameleon',
32
+ 'dance',
33
+ 'owl impression',
34
+ 'writes an OAS file',
35
+ ];
25
36
 
26
37
  function newPet(name = 'Eyes', color = 'blue') {
27
38
  return {
@@ -29,12 +40,12 @@ function newPet(name = 'Eyes', color = 'blue') {
29
40
  color,
30
41
  born: Date.now(),
31
42
  lastVisit: Date.now(),
32
- hunger: 8, // 0 = starving, 10 = full
43
+ hunger: 8, // 0 = starving, 10 = full
33
44
  happiness: 8, // 0 = miserable, 10 = ecstatic
34
- energy: 8, // 0 = exhausted, 10 = wide awake
45
+ energy: 8, // 0 = exhausted, 10 = wide awake
35
46
  sleeping: false,
36
- age: 0, // total real-time minutes alive
37
- tricks: [], // learned trick names
47
+ age: 0, // total real-time minutes alive
48
+ tricks: [], // learned trick names
38
49
  };
39
50
  }
40
51
 
@@ -83,7 +94,7 @@ function trickPhrase(trick) {
83
94
  }
84
95
 
85
96
  const ok = (message, extra = {}) => ({ message, ok: true, ...extra });
86
- const fail = (message) => ({ message, ok: false });
97
+ const fail = message => ({ message, ok: false });
87
98
 
88
99
  function feed(pet) {
89
100
  if (pet.hunger >= MAX_STAT) return fail('Already full!');
@@ -169,11 +180,16 @@ function getMood(pet) {
169
180
 
170
181
  function getExpression(mood) {
171
182
  switch (mood) {
172
- case 'sleeping': return 'closed';
173
- case 'tired': return 'squint';
174
- case 'sad': return 'squint';
175
- case 'happy': return 'right';
176
- default: return 'right';
183
+ case 'sleeping':
184
+ return 'closed';
185
+ case 'tired':
186
+ return 'squint';
187
+ case 'sad':
188
+ return 'squint';
189
+ case 'happy':
190
+ return 'right';
191
+ default:
192
+ return 'right';
177
193
  }
178
194
  }
179
195
 
@@ -202,22 +218,80 @@ function getAnimFrames(mood) {
202
218
  };
203
219
  case 'tired':
204
220
  return {
205
- frames: ['half-blink', 'half-blink', 'half-blink-left', 'half-blink', 'half-blink', 'half-blink-right', 'squint', 'half-blink', 'half-blink-left', 'half-blink', 'half-blink', 'half-blink-right', 'closed', 'half-blink', 'half-blink'],
221
+ frames: [
222
+ 'half-blink',
223
+ 'half-blink',
224
+ 'half-blink-left',
225
+ 'half-blink',
226
+ 'half-blink',
227
+ 'half-blink-right',
228
+ 'squint',
229
+ 'half-blink',
230
+ 'half-blink-left',
231
+ 'half-blink',
232
+ 'half-blink',
233
+ 'half-blink-right',
234
+ 'closed',
235
+ 'half-blink',
236
+ 'half-blink',
237
+ ],
206
238
  durations: [800, 600, 500, 1000, 400, 500, 150, 800, 500, 600, 800, 500, 200, 300, 1200],
207
239
  };
208
240
  case 'sad':
209
241
  return {
210
- frames: ['left', 'left', 'right', 'right', 'left', 'half-blink', 'squint', 'half-blink', 'left', 'left', 'right', 'left'],
242
+ frames: [
243
+ 'left',
244
+ 'left',
245
+ 'right',
246
+ 'right',
247
+ 'left',
248
+ 'half-blink',
249
+ 'squint',
250
+ 'half-blink',
251
+ 'left',
252
+ 'left',
253
+ 'right',
254
+ 'left',
255
+ ],
211
256
  durations: [800, 400, 200, 800, 600, 80, 400, 80, 600, 400, 300, 800],
212
257
  };
213
258
  case 'happy':
214
259
  return {
215
- frames: ['right', 'right', 'right:up', 'right:up', 'right', 'left', 'right', 'half-blink', 'squint', 'closed', 'squint', 'half-blink', 'right', 'right'],
260
+ frames: [
261
+ 'right',
262
+ 'right',
263
+ 'right:up',
264
+ 'right:up',
265
+ 'right',
266
+ 'left',
267
+ 'right',
268
+ 'half-blink',
269
+ 'squint',
270
+ 'closed',
271
+ 'squint',
272
+ 'half-blink',
273
+ 'right',
274
+ 'right',
275
+ ],
216
276
  durations: [600, 300, 150, 150, 300, 400, 400, 50, 50, 50, 50, 50, 300, 800],
217
277
  };
218
278
  default:
219
279
  return {
220
- frames: ['right', 'right', 'right', 'left', 'left', 'right', 'half-blink', 'squint', 'closed', 'squint', 'half-blink', 'right', 'right'],
280
+ frames: [
281
+ 'right',
282
+ 'right',
283
+ 'right',
284
+ 'left',
285
+ 'left',
286
+ 'right',
287
+ 'half-blink',
288
+ 'squint',
289
+ 'closed',
290
+ 'squint',
291
+ 'half-blink',
292
+ 'right',
293
+ 'right',
294
+ ],
221
295
  durations: [1200, 200, 400, 120, 600, 120, 50, 50, 50, 50, 50, 200, 1000],
222
296
  };
223
297
  }
@@ -246,7 +320,7 @@ function boxVisLen(s) {
246
320
  let len = 0;
247
321
  for (const ch of stripped) {
248
322
  const code = ch.codePointAt(0);
249
- len += code >= 0x1F000 ? 2 : 1;
323
+ len += code >= 0x1f000 ? 2 : 1;
250
324
  }
251
325
  return len;
252
326
  }
@@ -259,15 +333,15 @@ function boxLine(content = '', bg = null) {
259
333
  }
260
334
 
261
335
  function boxTop() {
262
- return chalk.dim('┌' + '─'.repeat(BOX_WIDTH - 2) + '┐');
336
+ return chalk.dim(`┌${'─'.repeat(BOX_WIDTH - 2)}┐`);
263
337
  }
264
338
 
265
339
  function boxBottom() {
266
- return chalk.dim('└' + '─'.repeat(BOX_WIDTH - 2) + '┘');
340
+ return chalk.dim(`└${'─'.repeat(BOX_WIDTH - 2)}┘`);
267
341
  }
268
342
 
269
343
  function boxDivider() {
270
- return chalk.dim('├' + '─'.repeat(BOX_WIDTH - 2) + '┤');
344
+ return chalk.dim(`├${'─'.repeat(BOX_WIDTH - 2)}┤`);
271
345
  }
272
346
 
273
347
  const STATUS_LEFT = 22; // inner width of left column
@@ -282,7 +356,7 @@ function statusRow(leftContent, rightContent) {
282
356
  }
283
357
 
284
358
  function statusDivider() {
285
- return chalk.dim('─'.repeat(STATUS_LEFT) + '┼' + '─'.repeat(STATUS_RIGHT));
359
+ return chalk.dim(`${'─'.repeat(STATUS_LEFT)}┼${'─'.repeat(STATUS_RIGHT)}`);
286
360
  }
287
361
 
288
362
  function renderStatus(pet) {
@@ -290,10 +364,7 @@ function renderStatus(pet) {
290
364
  const moodEmoji = { sleeping: 'sleeping', tired: 'tired', sad: 'lonely', happy: 'happy!', normal: 'content' }[mood];
291
365
 
292
366
  const lines = [
293
- statusRow(
294
- ` ${chalk.bold(pet.name)}`,
295
- ` ${chalk.dim('vitals')}`,
296
- ),
367
+ statusRow(` ${chalk.bold(pet.name)}`, ` ${chalk.dim('vitals')}`),
297
368
  statusRow(
298
369
  ` ${chalk.dim('age:')} ${formatAge(pet.age)}`,
299
370
  ` ${chalk.hex('#ff6b6b')('hunger')} ${statBar(pet.hunger, MAX_STAT, chalk.hex('#ff6b6b'))} ${String(pet.hunger).padStart(2)}/${MAX_STAT}`,
@@ -350,9 +421,10 @@ async function askSetup() {
350
421
  console.log('');
351
422
 
352
423
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
353
- const ask = (q) => new Promise((resolve) => {
354
- rl.question(q, (answer) => resolve(answer.trim()));
355
- });
424
+ const ask = q =>
425
+ new Promise(resolve => {
426
+ rl.question(q, answer => resolve(answer.trim()));
427
+ });
356
428
 
357
429
  // Ask name
358
430
  const name = (await ask(chalk.hex('#63D2FF')(' A new friend hatched! What will you name them? '))) || 'Eyes';
@@ -444,7 +516,7 @@ export async function startGame() {
444
516
  for (const ch of stripped) {
445
517
  const code = ch.codePointAt(0);
446
518
  // Emoji (surrogate pairs / high codepoints) are 2 cols, most symbols are 1
447
- len += code >= 0x1F000 ? 2 : 1;
519
+ len += code >= 0x1f000 ? 2 : 1;
448
520
  }
449
521
  return len;
450
522
  }
@@ -496,7 +568,9 @@ export async function startGame() {
496
568
  } else if (trickSceneOverlay && (trickSceneOverlay.right || trickSceneOverlay[r])) {
497
569
  if (trickSceneOverlay.left) leftStr = padRight(trickSceneOverlay.left[r] || '', effLeft);
498
570
  if (trickSceneOverlay.right) rightStr = padRight(trickSceneOverlay.right[r] || '', effRight);
499
- if (!trickSceneOverlay.left && !trickSceneOverlay.right) rightStr = padRight(' ' + (trickSceneOverlay[r] || ''), effRight);
571
+ if (!trickSceneOverlay.left && !trickSceneOverlay.right) {
572
+ rightStr = padRight(` ${trickSceneOverlay[r] || ''}`, effRight);
573
+ }
500
574
  }
501
575
 
502
576
  return leftStr + iconLine + rightStr;
@@ -514,31 +588,28 @@ export async function startGame() {
514
588
  const pos = PAD_LEFT + 2 + offset;
515
589
  const pad = ' '.repeat(Math.max(0, pos));
516
590
  const skin = chalk.hex('#FFCC88');
517
- return [
518
- pad + skin(' ╷╷╷╷'),
519
- pad + skin('╭┴┴┴┴╮'),
520
- ];
591
+ return [pad + skin(' ╷╷╷╷'), pad + skin('╭┴┴┴┴╮')];
521
592
  }
522
593
 
523
594
  // Render with box border
524
595
  const footer = chalk.dim('[r] reset [q] quit');
525
596
  const footerVis = '[r] reset [q] quit'.length;
526
597
  console.log('');
527
- console.log(' ' + ' '.repeat(BOX_WIDTH - 2 - footerVis + 1) + footer);
528
- console.log(' ' + boxTop());
598
+ console.log(` ${' '.repeat(BOX_WIDTH - 2 - footerVis + 1)}${footer}`);
599
+ console.log(` ${boxTop()}`);
529
600
  const sceneBg = petState.sleeping ? '#05051a' : null;
530
601
  const handLines = getPetHand();
531
602
  if (handLines.length) {
532
603
  // Hand takes 2 rows, so skip the empty top row and trim the last scene row
533
- console.log(' ' + boxLine(handLines[0], sceneBg));
534
- console.log(' ' + boxLine(handLines[1], sceneBg));
604
+ console.log(` ${boxLine(handLines[0], sceneBg)}`);
605
+ console.log(` ${boxLine(handLines[1], sceneBg)}`);
535
606
  for (let i = 0; i < sceneRows.length - 1; i++) {
536
- console.log(' ' + boxLine(sceneRows[i], sceneBg));
607
+ console.log(` ${boxLine(sceneRows[i], sceneBg)}`);
537
608
  }
538
609
  } else {
539
- console.log(' ' + boxLine('', sceneBg));
610
+ console.log(` ${boxLine('', sceneBg)}`);
540
611
  for (const row of sceneRows) {
541
- console.log(' ' + boxLine(row, sceneBg));
612
+ console.log(` ${boxLine(row, sceneBg)}`);
542
613
  }
543
614
  }
544
615
  // Message centered between scene and vitals
@@ -547,25 +618,27 @@ export async function startGame() {
547
618
  if (boxVisLen(displayMsg) > inner) {
548
619
  // Strip ANSI, truncate, re-apply no color (plain truncation)
549
620
  const plain = displayMsg.replace(/\x1b\[[0-9;]*m/g, '');
550
- displayMsg = plain.slice(0, inner - 1) + '…';
621
+ displayMsg = `${plain.slice(0, inner - 1)}…`;
551
622
  }
552
623
  const msgVis = boxVisLen(displayMsg);
553
624
  const msgPadL = Math.max(0, Math.floor((inner - msgVis) / 2));
554
625
  const msgPadR = Math.max(0, inner - msgVis - msgPadL);
555
- console.log(' ' + boxDivider());
556
- console.log(' ' + chalk.dim('│') + ' '.repeat(msgPadL) + displayMsg + ' '.repeat(msgPadR) + chalk.dim('│'));
557
- console.log(' ' + chalk.dim('├' + '─'.repeat(STATUS_LEFT) + '┬' + '─'.repeat(STATUS_RIGHT) + '┤'));
626
+ console.log(` ${boxDivider()}`);
627
+ console.log(` ${chalk.dim('│')}${' '.repeat(msgPadL)}${displayMsg}${' '.repeat(msgPadR)}${chalk.dim('│')}`);
628
+ console.log(` ${chalk.dim(`├${'─'.repeat(STATUS_LEFT)}┬${'─'.repeat(STATUS_RIGHT)}┤`)}`);
558
629
  for (const line of statusLines) {
559
- console.log(' ' + boxLine(line));
630
+ console.log(` ${boxLine(line)}`);
560
631
  }
561
- console.log(' ' + chalk.dim('├' + '─'.repeat(STATUS_LEFT) + '┴' + '─'.repeat(STATUS_RIGHT) + '┤'));
632
+ console.log(` ${chalk.dim(`├${'─'.repeat(STATUS_LEFT)}┴${'─'.repeat(STATUS_RIGHT)}┤`)}`);
562
633
  for (const line of actionLines) {
563
- console.log(' ' + boxLine(line));
634
+ console.log(` ${boxLine(line)}`);
564
635
  }
565
- console.log(' ' + boxBottom());
636
+ console.log(` ${boxBottom()}`);
566
637
  if (confirmingReset) {
567
638
  console.log('');
568
- console.log(' ' + chalk.hex('#ff6b6b')(`Reset ${petState.name}? They'll be gone forever!`) + chalk.dim(' [y] yes [n] no'));
639
+ console.log(
640
+ ` ${chalk.hex('#ff6b6b')(`Reset ${petState.name}? They'll be gone forever!`)}${chalk.dim(' [y] yes [n] no')}`,
641
+ );
569
642
  } else {
570
643
  console.log('');
571
644
  }
@@ -590,15 +663,30 @@ export async function startGame() {
590
663
  }
591
664
 
592
665
  const leftGrid = [
593
- [{ col: 3, seed: 0 }, { col: PAD_LEFT - 4, seed: 8 }],
666
+ [
667
+ { col: 3, seed: 0 },
668
+ { col: PAD_LEFT - 4, seed: 8 },
669
+ ],
594
670
  [{ col: 7, seed: 5 }],
595
- [{ col: 2, seed: 2 }, { col: PAD_LEFT - 3, seed: 9 }],
671
+ [
672
+ { col: 2, seed: 2 },
673
+ { col: PAD_LEFT - 3, seed: 9 },
674
+ ],
596
675
  ];
597
676
 
598
677
  const rightGrid = [
599
- [{ col: 2, seed: 3 }, { col: Math.min(10, PAD_RIGHT - 3), seed: 6 }],
600
- [{ col: 5, seed: 1 }, { col: Math.min(12, PAD_RIGHT - 4), seed: 10 }],
601
- [{ col: 1, seed: 7 }, { col: Math.min(8, PAD_RIGHT - 2), seed: 4 }],
678
+ [
679
+ { col: 2, seed: 3 },
680
+ { col: Math.min(10, PAD_RIGHT - 3), seed: 6 },
681
+ ],
682
+ [
683
+ { col: 5, seed: 1 },
684
+ { col: Math.min(12, PAD_RIGHT - 4), seed: 10 },
685
+ ],
686
+ [
687
+ { col: 1, seed: 7 },
688
+ { col: Math.min(8, PAD_RIGHT - 2), seed: 4 },
689
+ ],
602
690
  ];
603
691
 
604
692
  const leftLines = leftGrid.map(row => {
@@ -677,11 +765,11 @@ export async function startGame() {
677
765
  if (heartFrame < 0) return { left: null, right: null };
678
766
  const hearts = [
679
767
  { startFrame: 0, side: 'right', startRow: 2, offset: 2 },
680
- { startFrame: 1, side: 'left', startRow: 2, offset: 2 },
768
+ { startFrame: 1, side: 'left', startRow: 2, offset: 2 },
681
769
  { startFrame: 3, side: 'right', startRow: 2, offset: 6 },
682
- { startFrame: 4, side: 'left', startRow: 1, offset: 5 },
770
+ { startFrame: 4, side: 'left', startRow: 1, offset: 5 },
683
771
  { startFrame: 6, side: 'right', startRow: 2, offset: 10 },
684
- { startFrame: 7, side: 'left', startRow: 2, offset: 9 },
772
+ { startFrame: 7, side: 'left', startRow: 2, offset: 9 },
685
773
  ];
686
774
  const leftLines = ['', '', '', ''];
687
775
  const rightLines = ['', '', '', ''];
@@ -748,7 +836,7 @@ export async function startGame() {
748
836
  const cells = s.side === 'left' ? leftCells[s.r] : rightCells[s.r];
749
837
  if (s.col < cells.length) cells[s.col] = chalk.hex(color)(ch);
750
838
  }
751
- const toStr = (cells) => cells.map(c => c || ' ').join('');
839
+ const toStr = cells => cells.map(c => c || ' ').join('');
752
840
  return {
753
841
  left: leftCells.map(toStr),
754
842
  right: rightCells.map(toStr),
@@ -757,7 +845,21 @@ export async function startGame() {
757
845
 
758
846
  async function animatePlay() {
759
847
  playAnimating = true;
760
- const bounceFrames = ['right:up', 'right:up', 'right', 'right', 'right:up', 'right:up', 'right', 'right', 'right:up', 'right:up', 'right', 'right', 'right'];
848
+ const bounceFrames = [
849
+ 'right:up',
850
+ 'right:up',
851
+ 'right',
852
+ 'right',
853
+ 'right:up',
854
+ 'right:up',
855
+ 'right',
856
+ 'right',
857
+ 'right:up',
858
+ 'right:up',
859
+ 'right',
860
+ 'right',
861
+ 'right',
862
+ ];
761
863
  for (playFrame = 0; playFrame <= 12; playFrame++) {
762
864
  frameOverride = bounceFrames[playFrame % bounceFrames.length];
763
865
  draw();
@@ -772,8 +874,12 @@ export async function startGame() {
772
874
  let teachAnimating = false;
773
875
 
774
876
  async function animateTrick(name) {
775
- const wait = (ms) => new Promise(r => setTimeout(r, ms));
776
- const setFrame = async (f, ms) => { frameOverride = f; draw(); await wait(ms); };
877
+ const wait = ms => new Promise(r => setTimeout(r, ms));
878
+ const setFrame = async (f, ms) => {
879
+ frameOverride = f;
880
+ draw();
881
+ await wait(ms);
882
+ };
777
883
 
778
884
  switch (name) {
779
885
  case 'hide and seek': {
@@ -783,10 +889,14 @@ export async function startGame() {
783
889
  await setFrame('right', 180);
784
890
  }
785
891
  // peek — bob up slightly then back down
786
- iconShift = 2; await setFrame('right', 200);
787
- iconShift = 3; await setFrame('right', 300);
788
- iconShift = 2; await setFrame('right', 150);
789
- iconShift = 3; await setFrame('right', 400);
892
+ iconShift = 2;
893
+ await setFrame('right', 200);
894
+ iconShift = 3;
895
+ await setFrame('right', 300);
896
+ iconShift = 2;
897
+ await setFrame('right', 150);
898
+ iconShift = 3;
899
+ await setFrame('right', 400);
790
900
  // come back up
791
901
  for (const shift of [2, 1, 0]) {
792
902
  iconShift = shift;
@@ -795,14 +905,15 @@ export async function startGame() {
795
905
  break;
796
906
  }
797
907
  case 'spin': {
798
- for (const f of ['left', 'up-left', 'up-right', 'right', 'up-right', 'up-left', 'left', 'up-left', 'right'])
908
+ for (const f of ['left', 'up-left', 'up-right', 'right', 'up-right', 'up-left', 'left', 'up-left', 'right']) {
799
909
  await setFrame(f, 110);
910
+ }
800
911
  break;
801
912
  }
802
913
  case 'fetch': {
803
914
  iconHShift = -8;
804
915
  const method = chalk.hex('#63D2FF')('POST');
805
- const pending = chalk.dim('○') + ' ' + method + ' /api/pets';
916
+ const pending = `${chalk.dim('○')} ${method} /api/pets`;
806
917
  const d = chalk.dim;
807
918
  const json = [
808
919
  d(' "id": 1,'),
@@ -842,8 +953,16 @@ export async function startGame() {
842
953
  break;
843
954
  }
844
955
  case 'bow': {
845
- for (const [f, ms] of [['right', 200], ['right:up', 300], ['right:up', 300], ['squint', 500], ['right:up', 300], ['right', 200]])
956
+ for (const [f, ms] of [
957
+ ['right', 200],
958
+ ['right:up', 300],
959
+ ['right:up', 300],
960
+ ['squint', 500],
961
+ ['right:up', 300],
962
+ ['right', 200],
963
+ ]) {
846
964
  await setFrame(f, ms);
965
+ }
847
966
  break;
848
967
  }
849
968
  case 'chameleon': {
@@ -857,19 +976,41 @@ export async function startGame() {
857
976
  case 'dance': {
858
977
  const notes = ['♩', '♪', '♫', '♬'];
859
978
  const nc = chalk.hex('#ffd93d');
860
- const place = (width, col, note) => ' '.repeat(Math.min(col, width - 1)) + nc(note) + ' '.repeat(Math.max(0, width - col - 1));
979
+ const place = (width, col, note) =>
980
+ ' '.repeat(Math.min(col, width - 1)) + nc(note) + ' '.repeat(Math.max(0, width - col - 1));
861
981
  // Each frame: scattered note positions on left and right, shifting each beat
862
- const frames = ['left:up', 'left', 'right:up', 'right', 'left:up', 'left', 'right:up', 'right', 'right:up', 'right'];
982
+ const frames = [
983
+ 'left:up',
984
+ 'left',
985
+ 'right:up',
986
+ 'right',
987
+ 'left:up',
988
+ 'left',
989
+ 'right:up',
990
+ 'right',
991
+ 'right:up',
992
+ 'right',
993
+ ];
863
994
  const spots = [
864
995
  { lr: [14, 3], ll: [2, 11], rr: [4, 15], rl: [8, 1] },
865
- { lr: [6, 16], ll: [9, 4], rr: [12, 2], rl: [14, 7] },
996
+ { lr: [6, 16], ll: [9, 4], rr: [12, 2], rl: [14, 7] },
866
997
  ];
867
998
  for (let i = 0; i < frames.length; i++) {
868
999
  const s = spots[i % 2];
869
- const ni = (k) => notes[(i + k) % notes.length];
1000
+ const ni = k => notes[(i + k) % notes.length];
870
1001
  trickSceneOverlay = {
871
- left: [place(PAD_LEFT, s.ll[0], ni(0)), place(PAD_LEFT, s.ll[1], ni(2)), place(PAD_LEFT, s.lr[0], ni(1)), place(PAD_LEFT, s.lr[1], ni(3))],
872
- right: [place(PAD_RIGHT, s.rr[0], ni(2)), place(PAD_RIGHT, s.rl[0], ni(0)), place(PAD_RIGHT, s.rr[1], ni(3)), place(PAD_RIGHT, s.rl[1], ni(1))],
1002
+ left: [
1003
+ place(PAD_LEFT, s.ll[0], ni(0)),
1004
+ place(PAD_LEFT, s.ll[1], ni(2)),
1005
+ place(PAD_LEFT, s.lr[0], ni(1)),
1006
+ place(PAD_LEFT, s.lr[1], ni(3)),
1007
+ ],
1008
+ right: [
1009
+ place(PAD_RIGHT, s.rr[0], ni(2)),
1010
+ place(PAD_RIGHT, s.rl[0], ni(0)),
1011
+ place(PAD_RIGHT, s.rr[1], ni(3)),
1012
+ place(PAD_RIGHT, s.rl[1], ni(1)),
1013
+ ],
873
1014
  };
874
1015
  await setFrame(frames[i], 130);
875
1016
  }
@@ -879,8 +1020,14 @@ export async function startGame() {
879
1020
  case 'owl impression': {
880
1021
  setPalette('owl');
881
1022
  const hoot = chalk.hex('#ffd93d');
882
- for (const [f, ms] of [['left', 200], ['right', 200], ['left', 200], ['right', 200]])
1023
+ for (const [f, ms] of [
1024
+ ['left', 200],
1025
+ ['right', 200],
1026
+ ['left', 200],
1027
+ ['right', 200],
1028
+ ]) {
883
1029
  await setFrame(f, ms);
1030
+ }
884
1031
  const rpad = () => ' '.repeat(2 + Math.floor(Math.random() * 8));
885
1032
  trickSceneOverlay = { right: [rpad() + hoot('hoot!'), '', '', ''] };
886
1033
  await setFrame('up-right', 500);
@@ -911,8 +1058,9 @@ export async function startGame() {
911
1058
  break;
912
1059
  }
913
1060
  default: {
914
- for (const f of ['right:up', 'right', 'right:up', 'right'])
1061
+ for (const f of ['right:up', 'right', 'right:up', 'right']) {
915
1062
  await setFrame(f, 150);
1063
+ }
916
1064
  }
917
1065
  }
918
1066
  frameOverride = null;
@@ -975,8 +1123,15 @@ export async function startGame() {
975
1123
  });
976
1124
  }
977
1125
 
978
- const actionKeyMap = new Map([[feed, 'f'], [play, 'p'], [petAction, 'h'], [teach, 't']]);
979
- function getnapKey() { return petState.sleeping ? 'w' : 's'; }
1126
+ const actionKeyMap = new Map([
1127
+ [feed, 'f'],
1128
+ [play, 'p'],
1129
+ [petAction, 'h'],
1130
+ [teach, 't'],
1131
+ ]);
1132
+ function getnapKey() {
1133
+ return petState.sleeping ? 'w' : 's';
1134
+ }
980
1135
 
981
1136
  function doAction(actionFn) {
982
1137
  if (actionBusy) return;
@@ -987,7 +1142,7 @@ export async function startGame() {
987
1142
  savePet(petState);
988
1143
  animFrame = 0;
989
1144
  actionBusy = true;
990
- activeAction = actionFn === nap ? getnapKey() : (actionKeyMap.get(actionFn) || null);
1145
+ activeAction = actionFn === nap ? getnapKey() : actionKeyMap.get(actionFn) || null;
991
1146
 
992
1147
  function done() {
993
1148
  activeAction = null;
@@ -1001,7 +1156,10 @@ export async function startGame() {
1001
1156
  done();
1002
1157
  } else if (actionFn === nap) {
1003
1158
  // Only nap/wake gets the eye open/close animation
1004
- animateSleep(petState.sleeping).then(() => { draw(); done(); });
1159
+ animateSleep(petState.sleeping).then(() => {
1160
+ draw();
1161
+ done();
1162
+ });
1005
1163
  } else if (actionFn === feed) {
1006
1164
  animateFeed().then(done);
1007
1165
  } else if (actionFn === petAction) {
@@ -1010,9 +1168,8 @@ export async function startGame() {
1010
1168
  animatePlay().then(done);
1011
1169
  } else if (actionFn === teach) {
1012
1170
  const tricksAfter = (petState.tricks || []).length;
1013
- const learnedTrick = tricksAfter > tricksBefore
1014
- ? petState.tricks[petState.tricks.length - 1]
1015
- : (result.trick || null);
1171
+ const learnedTrick =
1172
+ tricksAfter > tricksBefore ? petState.tricks[petState.tricks.length - 1] : result.trick || null;
1016
1173
  const learned = tricksAfter > tricksBefore;
1017
1174
  animateTeach(learnedTrick).then(() => {
1018
1175
  activeAction = null;
@@ -1034,14 +1191,16 @@ export async function startGame() {
1034
1191
  }
1035
1192
 
1036
1193
  // Input handler
1037
- process.stdin.on('data', (key) => {
1194
+ process.stdin.on('data', key => {
1038
1195
  if (stopped) return;
1039
1196
 
1040
1197
  if (confirmingReset) {
1041
1198
  if (key === 'y' || key === 'Y') {
1042
1199
  cleanup();
1043
1200
  const savePath = getSavePath();
1044
- try { fs.unlinkSync(savePath); } catch {}
1201
+ try {
1202
+ fs.unlinkSync(savePath);
1203
+ } catch {}
1045
1204
  console.log('');
1046
1205
  console.log(` ${chalk.dim('Save data cleared. A new friend will hatch next time!')}`);
1047
1206
  console.log('');
@@ -1072,8 +1231,14 @@ export async function startGame() {
1072
1231
  case 't':
1073
1232
  doAction(teach);
1074
1233
  break;
1075
- case '1': case '2': case '3': case '4':
1076
- case '5': case '6': case '7': case '8': {
1234
+ case '1':
1235
+ case '2':
1236
+ case '3':
1237
+ case '4':
1238
+ case '5':
1239
+ case '6':
1240
+ case '7':
1241
+ case '8': {
1077
1242
  const idx = parseInt(key) - 1;
1078
1243
  const trickName = ALL_TRICKS[idx];
1079
1244
  const known = (petState.tricks || []).filter(t => ALL_TRICKS.includes(t));
@@ -1097,7 +1262,7 @@ export async function startGame() {
1097
1262
  draw();
1098
1263
  cleanup();
1099
1264
  process.exit();
1100
- break;
1265
+ // oxlint-disable-next-line no-fallthrough -- Can't add a `break` above this because it's unreachable.
1101
1266
  default:
1102
1267
  break;
1103
1268
  }
@@ -1128,12 +1293,13 @@ export async function startGame() {
1128
1293
  const moodAfter = getMood(petState);
1129
1294
  if (moodBefore !== moodAfter) {
1130
1295
  if (moodAfter === 'tired') message = chalk.hex('#63D2FF')(`${petState.name} is getting sleepy...`);
1131
- else if (moodAfter === 'sad' && petState.hunger <= 1) message = chalk.hex('#ff6b6b')(`${petState.name} is hungry!`);
1296
+ else if (moodAfter === 'sad' && petState.hunger <= 1) {
1297
+ message = chalk.hex('#ff6b6b')(`${petState.name} is hungry!`);
1298
+ }
1132
1299
  }
1133
1300
  }
1134
1301
  }
1135
1302
  }
1136
1303
 
1137
-
1138
1304
  await gameLoop();
1139
1305
  }