farmwork 1.0.0 β†’ 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,649 @@
1
+ import terminalKit from "terminal-kit";
2
+
3
+ const term = terminalKit.terminal;
4
+
5
+ // Farm-themed color palette
6
+ const colors = {
7
+ primary: term.green,
8
+ secondary: term.yellow,
9
+ accent: term.cyan,
10
+ success: term.brightGreen,
11
+ warning: term.brightYellow,
12
+ error: term.red,
13
+ muted: term.gray,
14
+ highlight: term.brightWhite,
15
+ };
16
+
17
+ // Farm emoji set - all farm-themed!
18
+ const emojis = {
19
+ // Equipment & Buildings
20
+ tractor: "🚜",
21
+ barn: "🏑",
22
+ basket: "🧺",
23
+ // Crops & Plants
24
+ corn: "🌽",
25
+ wheat: "🌾",
26
+ seedling: "🌱",
27
+ tomato: "πŸ…",
28
+ carrot: "πŸ₯•",
29
+ potato: "πŸ₯”",
30
+ lettuce: "πŸ₯¬",
31
+ pumpkin: "πŸŽƒ",
32
+ sunflower: "🌻",
33
+ leaf: "πŸ‚",
34
+ herb: "🌿",
35
+ wilted: "πŸ₯€",
36
+ // Animals
37
+ cow: "πŸ„",
38
+ pig: "🐷",
39
+ chicken: "πŸ”",
40
+ rooster: "πŸ“",
41
+ horse: "🐴",
42
+ sheep: "πŸ‘",
43
+ dog: "πŸ•",
44
+ bee: "🐝",
45
+ owl: "πŸ¦‰",
46
+ // Weather & Nature
47
+ sun: "β˜€οΈ",
48
+ rain: "🌧️",
49
+ water: "πŸ’§",
50
+ // Fruits
51
+ apple: "🍎",
52
+ strawberry: "πŸ“",
53
+ grapes: "πŸ‡",
54
+ peach: "πŸ‘",
55
+ cherry: "πŸ’",
56
+ };
57
+
58
+ // ASCII Art
59
+ const TRACTOR_ART = `
60
+ _______________
61
+ | ___________ |
62
+ | | FARMWORK | |
63
+ | |___________| |
64
+ |_______________|
65
+ / 🌾 🌾 🌾 \\
66
+ / 🌽 🌽 🌽 \\
67
+ /_____________________\\
68
+ ()) _|__|_ (()
69
+ (()) | | (())
70
+ \\/ |____| \\/
71
+ `;
72
+
73
+ const BARN_ART = `
74
+ /\\
75
+ / \\
76
+ / \\
77
+ /______\\
78
+ | FARM |
79
+ | WORK |
80
+ |________|
81
+ `;
82
+
83
+ const SMALL_TRACTOR = `
84
+ __
85
+ |==|___
86
+ [| | \\
87
+ [|__|____\\>
88
+ () ()
89
+ `;
90
+
91
+ // Main logo with apple and FARMWORK text
92
+ const LOGO_ART = {
93
+ apple: [
94
+ " ,--./,-.",
95
+ " / # \\",
96
+ " | |",
97
+ " \\ /",
98
+ " `._,._,'",
99
+ ],
100
+ text: [
101
+ "β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•— β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•— β–ˆβ–ˆβ•—",
102
+ "β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•",
103
+ "β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• ",
104
+ "β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•— ",
105
+ "β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•—",
106
+ "β•šβ•β• β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β• β•šβ•β•β•β•šβ•β•β• β•šβ•β•β•β•β•β• β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β•",
107
+ ],
108
+ tagline: "🌱 Agentic Development Harness",
109
+ };
110
+
111
+ // Compact logo for smaller displays
112
+ const LOGO_COMPACT = {
113
+ fruit: "🍎",
114
+ text: [
115
+ " _____ _ ____ __ ____ _____ ____ _ __",
116
+ "| ___/ \\ | _ \\| \\/ \\ \\ / / _ \\| _ \\| |/ /",
117
+ "| |_ / _ \\ | |_) | |\\/| |\\ \\ /\\ / / | | | |_) | ' / ",
118
+ "| _/ ___ \\| _ <| | | | \\ V V /| |_| | _ <| . \\ ",
119
+ "|_|/_/ \\_\\_| \\_\\_| |_| \\_/\\_/ \\___/|_| \\_\\_|\\_\\",
120
+ ],
121
+ tagline: "Agentic Development Harness",
122
+ };
123
+
124
+ // Animation frames for various states
125
+ const GROWING_FRAMES = ["🌱", "🌿", "🌳", "🌲", "🌴"];
126
+ const HARVEST_FRAMES = ["🌾", "🌾🌾", "🌾🌾🌾", "🌾🌾", "🌾"];
127
+ const TRACTOR_FRAMES = ["🚜 ", " 🚜 ", " 🚜", " 🚜 "];
128
+ const WEATHER_FRAMES = [
129
+ "β˜€οΈ ",
130
+ "🌀️",
131
+ "β›…",
132
+ "πŸŒ₯️",
133
+ "☁️",
134
+ "🌧️",
135
+ "β›ˆοΈ",
136
+ "🌧️",
137
+ "☁️",
138
+ "πŸŒ₯️",
139
+ "β›…",
140
+ "🌀️",
141
+ ];
142
+
143
+ class FarmTerminal {
144
+ constructor() {
145
+ this.term = term;
146
+ this.colors = colors;
147
+ this.emojis = emojis;
148
+ this.spinnerInterval = null;
149
+ this.progressBar = null;
150
+ }
151
+
152
+ // Clear screen
153
+ clear() {
154
+ term.clear();
155
+ }
156
+
157
+ // Move cursor
158
+ moveTo(x, y) {
159
+ term.moveTo(x, y);
160
+ }
161
+
162
+ // Print styled header
163
+ header(text, style = "primary") {
164
+ const width = Math.min(term.width || 60, 60);
165
+ const padding = Math.floor((width - text.length - 4) / 2);
166
+ const paddedText =
167
+ " ".repeat(Math.max(0, padding)) +
168
+ text +
169
+ " ".repeat(Math.max(0, padding));
170
+
171
+ term("\n");
172
+ colors[style]("β•”" + "═".repeat(width - 2) + "β•—\n");
173
+ colors[style]("β•‘");
174
+ term.bold.brightWhite(paddedText.slice(0, width - 4).padEnd(width - 4));
175
+ colors[style]("β•‘\n");
176
+ colors[style]("β•š" + "═".repeat(width - 2) + "╝\n");
177
+ term("\n");
178
+ }
179
+
180
+ // Print section header
181
+ section(text, emoji = "🌱") {
182
+ term("\n");
183
+ term.bold.cyan(`${emoji} ${text}\n`);
184
+ term.gray("─".repeat(Math.min(text.length + 4, 50)) + "\n");
185
+ }
186
+
187
+ // Print a styled box
188
+ box(title, content, style = "primary") {
189
+ const width = Math.min(term.width || 50, 50);
190
+ const lines = Array.isArray(content) ? content : [content];
191
+
192
+ term("\n");
193
+ colors[style]("β”Œ" + "─".repeat(width - 2) + "┐\n");
194
+ colors[style]("β”‚");
195
+ term.bold(` ${title}`.padEnd(width - 2));
196
+ colors[style]("β”‚\n");
197
+ colors[style]("β”œ" + "─".repeat(width - 2) + "─\n");
198
+
199
+ for (const line of lines) {
200
+ colors[style]("β”‚");
201
+ term(` ${line}`.slice(0, width - 3).padEnd(width - 2));
202
+ colors[style]("β”‚\n");
203
+ }
204
+
205
+ colors[style]("β””" + "─".repeat(width - 2) + "β”˜\n");
206
+ }
207
+
208
+ // Table display
209
+ table(headers, rows) {
210
+ const width = Math.min(term.width || 60, 60);
211
+ const colWidth = Math.floor((width - 4) / headers.length);
212
+
213
+ term("\n");
214
+ term.gray("β”Œ" + headers.map(() => "─".repeat(colWidth)).join("┬") + "┐\n");
215
+ term.gray("β”‚");
216
+ for (const h of headers) {
217
+ term.bold.white(` ${h}`.slice(0, colWidth - 1).padEnd(colWidth));
218
+ }
219
+ term.gray("β”‚\n");
220
+ term.gray("β”œ" + headers.map(() => "─".repeat(colWidth)).join("β”Ό") + "─\n");
221
+
222
+ for (const row of rows) {
223
+ term.gray("β”‚");
224
+ for (let i = 0; i < headers.length; i++) {
225
+ const cell = row[i] || "";
226
+ term(` ${cell}`.slice(0, colWidth - 1).padEnd(colWidth));
227
+ }
228
+ term.gray("β”‚\n");
229
+ }
230
+
231
+ term.gray("β””" + headers.map(() => "─".repeat(colWidth)).join("β”΄") + "β”˜\n");
232
+ }
233
+
234
+ // Animated spinner with custom message
235
+ async spin(message, asyncFn) {
236
+ const frames = ["🌱", "🌿", "🌳", "🌲", "🌴", "🌲", "🌳", "🌿"];
237
+ let frameIndex = 0;
238
+ let lastLine = "";
239
+
240
+ const interval = setInterval(() => {
241
+ if (lastLine) {
242
+ term.column(1);
243
+ term.eraseLine();
244
+ }
245
+ lastLine = ` ${frames[frameIndex]} ${message}`;
246
+ term.yellow(lastLine);
247
+ frameIndex = (frameIndex + 1) % frames.length;
248
+ }, 120);
249
+
250
+ try {
251
+ const result = await asyncFn();
252
+ clearInterval(interval);
253
+ term.column(1);
254
+ term.eraseLine();
255
+ term.green(` 🌿 ${message}\n`);
256
+ return result;
257
+ } catch (error) {
258
+ clearInterval(interval);
259
+ term.column(1);
260
+ term.eraseLine();
261
+ term.red(` πŸ₯€ ${message}\n`);
262
+ throw error;
263
+ }
264
+ }
265
+
266
+ // Simple spinner without async
267
+ startSpinner(message) {
268
+ const frames = ["🌱", "🌿", "🌳", "🌲", "🌴", "🌲", "🌳", "🌿"];
269
+ let frameIndex = 0;
270
+
271
+ this.spinnerInterval = setInterval(() => {
272
+ term.column(1);
273
+ term.eraseLine();
274
+ term.yellow(` ${frames[frameIndex]} ${message}`);
275
+ frameIndex = (frameIndex + 1) % frames.length;
276
+ }, 120);
277
+ }
278
+
279
+ stopSpinner(message, success = true) {
280
+ if (this.spinnerInterval) {
281
+ clearInterval(this.spinnerInterval);
282
+ this.spinnerInterval = null;
283
+ }
284
+ term.column(1);
285
+ term.eraseLine();
286
+ if (success) {
287
+ term.green(` 🌿 ${message}\n`);
288
+ } else {
289
+ term.red(` πŸ₯€ ${message}\n`);
290
+ }
291
+ }
292
+
293
+ // Tractor animation (drives across screen)
294
+ async tractorAnimation(message, durationMs = 2000) {
295
+ const tractorFrames = ["πŸšœπŸ’¨", "🚜 πŸ’¨", "🚜 πŸ’¨", "🚜 "];
296
+ const width = Math.min(term.width || 40, 40) - 10;
297
+ let pos = 0;
298
+ let frameIndex = 0;
299
+ const steps = width;
300
+ const interval = durationMs / steps;
301
+
302
+ return new Promise((resolve) => {
303
+ const timer = setInterval(() => {
304
+ term.column(1);
305
+ term.eraseLine();
306
+ const spaces = " ".repeat(pos);
307
+ term.yellow(`${spaces}${tractorFrames[frameIndex]} ${message}`);
308
+ pos++;
309
+ frameIndex = (frameIndex + 1) % tractorFrames.length;
310
+
311
+ if (pos >= width) {
312
+ clearInterval(timer);
313
+ term.column(1);
314
+ term.eraseLine();
315
+ term.green(` 🚜 ${message} 🌾\n`);
316
+ resolve();
317
+ }
318
+ }, interval);
319
+ });
320
+ }
321
+
322
+ // Progress bar for multi-step operations
323
+ createProgressBar(options = {}) {
324
+ const { title = "Progress", items = 10, width = 40 } = options;
325
+
326
+ term("\n");
327
+ this.progressBar = term.progressBar({
328
+ title: ` ${emojis.tractor} ${title}`,
329
+ eta: true,
330
+ percent: true,
331
+ items,
332
+ width: Math.min(width, term.width - 20),
333
+ barStyle: term.green,
334
+ barBracketStyle: term.white,
335
+ percentStyle: term.yellow,
336
+ etaStyle: term.gray,
337
+ });
338
+
339
+ return this.progressBar;
340
+ }
341
+
342
+ updateProgress(value) {
343
+ if (this.progressBar) {
344
+ this.progressBar.update(value);
345
+ }
346
+ }
347
+
348
+ // Slow typing effect for dramatic text
349
+ async slowType(text, speed = 50) {
350
+ return new Promise((resolve) => {
351
+ term.slowTyping(
352
+ text,
353
+ {
354
+ flashStyle: term.brightWhite,
355
+ delay: speed,
356
+ style: term.green,
357
+ },
358
+ resolve,
359
+ );
360
+ });
361
+ }
362
+
363
+ // Status indicator (pass/fail/warn)
364
+ status(label, state, details = "") {
365
+ const icons = {
366
+ pass: { icon: "🌱", color: term.green },
367
+ fail: { icon: "πŸ‚", color: term.red },
368
+ warn: { icon: "πŸ‹", color: term.yellow },
369
+ info: { icon: "πŸ’§", color: term.cyan },
370
+ };
371
+
372
+ const { icon, color } = icons[state] || icons.info;
373
+ color(` ${icon} ${label}`);
374
+ if (details) {
375
+ term.gray(` ${details}`);
376
+ }
377
+ term("\n");
378
+ }
379
+
380
+ // Score display with visual bar
381
+ score(label, value, max = 10) {
382
+ const percentage = value / max;
383
+ const barWidth = 20;
384
+ const filled = Math.round(percentage * barWidth);
385
+ const empty = barWidth - filled;
386
+
387
+ const color =
388
+ percentage >= 0.8
389
+ ? term.green
390
+ : percentage >= 0.5
391
+ ? term.yellow
392
+ : term.red;
393
+ const emoji = percentage >= 0.8 ? "🌳" : percentage >= 0.5 ? "🌿" : "🌱";
394
+
395
+ term(` ${emoji} ${label.padEnd(18)}`);
396
+ term.gray("[");
397
+ color("β–ˆ".repeat(filled));
398
+ term.gray("β–‘".repeat(empty));
399
+ term.gray("] ");
400
+ color(`${value}/${max}\n`);
401
+ }
402
+
403
+ // Metric display
404
+ metric(label, value, icon = "🌾") {
405
+ term(` ${icon} ${label.padEnd(25)}`);
406
+ term.bold.white(`${value}\n`);
407
+ }
408
+
409
+ // Success message with celebration
410
+ success(message) {
411
+ term("\n");
412
+ term.green.bold(` 🌻 ${message} 🌻\n`);
413
+ term("\n");
414
+ }
415
+
416
+ // Error message
417
+ error(message) {
418
+ term("\n");
419
+ term.red.bold(` πŸ₯€ ${message}\n`);
420
+ term("\n");
421
+ }
422
+
423
+ // Warning message
424
+ warn(message) {
425
+ term.yellow(` πŸ‹ ${message}\n`);
426
+ }
427
+
428
+ // Info message
429
+ info(message) {
430
+ term.cyan(` πŸ“ ${message}\n`);
431
+ }
432
+
433
+ // Print tractor ASCII art
434
+ printTractor() {
435
+ term.green(TRACTOR_ART);
436
+ }
437
+
438
+ // Print small tractor
439
+ printSmallTractor() {
440
+ term.green(SMALL_TRACTOR);
441
+ }
442
+
443
+ // Print barn ASCII art
444
+ printBarn() {
445
+ term.yellow(BARN_ART);
446
+ }
447
+
448
+ // Display the main FARMWORK logo with fruit
449
+ logo(compact = false) {
450
+ term("\n");
451
+
452
+ if (compact || (term.width && term.width < 80)) {
453
+ // Compact version for narrow terminals
454
+ term.green(` ${LOGO_COMPACT.fruit} `);
455
+ term.bold.brightGreen("FARMWORK\n");
456
+ term.gray(` ${LOGO_COMPACT.tagline}\n`);
457
+ } else {
458
+ // Full ASCII art logo
459
+ // Draw the apple in red
460
+ for (const line of LOGO_ART.apple) {
461
+ term.red(` ${line}\n`);
462
+ }
463
+ term("\n");
464
+
465
+ // Draw FARMWORK text in green gradient effect
466
+ const greenShades = [
467
+ term.green,
468
+ term.brightGreen,
469
+ term.brightGreen,
470
+ term.green,
471
+ term.green,
472
+ term.dim.green,
473
+ ];
474
+ for (let i = 0; i < LOGO_ART.text.length; i++) {
475
+ term(" ");
476
+ greenShades[i](LOGO_ART.text[i] + "\n");
477
+ }
478
+
479
+ // Tagline
480
+ term("\n");
481
+ term.gray(` ${LOGO_ART.tagline}\n`);
482
+ }
483
+ term("\n");
484
+ }
485
+
486
+ // Animated logo reveal (types out the text)
487
+ async logoAnimated() {
488
+ term("\n");
489
+
490
+ // Draw apple with slight delay per line
491
+ for (const line of LOGO_ART.apple) {
492
+ term.red(` ${line}\n`);
493
+ await new Promise((r) => setTimeout(r, 50));
494
+ }
495
+ term("\n");
496
+
497
+ // Reveal FARMWORK text line by line
498
+ const greenShades = [
499
+ term.green,
500
+ term.brightGreen,
501
+ term.brightGreen,
502
+ term.green,
503
+ term.green,
504
+ term.dim.green,
505
+ ];
506
+ for (let i = 0; i < LOGO_ART.text.length; i++) {
507
+ term(" ");
508
+ greenShades[i](LOGO_ART.text[i] + "\n");
509
+ await new Promise((r) => setTimeout(r, 80));
510
+ }
511
+
512
+ term("\n");
513
+ term.gray(` ${LOGO_ART.tagline}\n`);
514
+ term("\n");
515
+ }
516
+
517
+ // Phrase list display
518
+ phrases(phraseList) {
519
+ term("\n");
520
+ term.bold.cyan(" Quick Phrases:\n\n");
521
+
522
+ for (const { phrase, description, emoji } of phraseList) {
523
+ term(` ${emoji} `);
524
+ term.yellow.bold(phrase.padEnd(18));
525
+ term.gray(` ${emojis.arrow} ${description}\n`);
526
+ }
527
+ term("\n");
528
+ }
529
+
530
+ // Divider line
531
+ divider(char = "─", width = 50) {
532
+ term.gray(char.repeat(Math.min(width, term.width || 50)) + "\n");
533
+ }
534
+
535
+ // Newline helper
536
+ nl(count = 1) {
537
+ term("\n".repeat(count));
538
+ }
539
+
540
+ // Raw text output
541
+ print(text) {
542
+ term(text);
543
+ }
544
+
545
+ // Colored text helpers
546
+ green(text) {
547
+ term.green(text);
548
+ }
549
+ yellow(text) {
550
+ term.yellow(text);
551
+ }
552
+ red(text) {
553
+ term.red(text);
554
+ }
555
+ cyan(text) {
556
+ term.cyan(text);
557
+ }
558
+ gray(text) {
559
+ term.gray(text);
560
+ }
561
+ white(text) {
562
+ term.white(text);
563
+ }
564
+ bold(text) {
565
+ term.bold(text);
566
+ }
567
+
568
+ // Wait for keypress
569
+ async waitForKey(message = "Press any key to continue...") {
570
+ term.gray(`\n ${message}`);
571
+ return new Promise((resolve) => {
572
+ term.grabInput(true);
573
+ term.once("key", (key) => {
574
+ term.grabInput(false);
575
+ term("\n");
576
+ resolve(key);
577
+ });
578
+ });
579
+ }
580
+
581
+ // Countdown animation
582
+ async countdown(seconds, message = "Starting in") {
583
+ for (let i = seconds; i > 0; i--) {
584
+ term.column(1);
585
+ term.eraseLine();
586
+ term.yellow(` πŸ“ ${message} ${i}...`);
587
+ await new Promise((r) => setTimeout(r, 1000));
588
+ }
589
+ term.column(1);
590
+ term.eraseLine();
591
+ term.green(` 🚜 Go!\n`);
592
+ }
593
+
594
+ // Analyzing animation
595
+ async analyzing(message = "Analyzing", durationMs = 1500) {
596
+ const dots = [" ", ". ", ".. ", "..."];
597
+ let dotIndex = 0;
598
+ const startTime = Date.now();
599
+
600
+ return new Promise((resolve) => {
601
+ const interval = setInterval(() => {
602
+ term.column(1);
603
+ term.eraseLine();
604
+ term.cyan(` πŸ¦‰ ${message}${dots[dotIndex]}`);
605
+ dotIndex = (dotIndex + 1) % dots.length;
606
+
607
+ if (Date.now() - startTime >= durationMs) {
608
+ clearInterval(interval);
609
+ term.column(1);
610
+ term.eraseLine();
611
+ term.green(` 🌿 ${message} complete\n`);
612
+ resolve();
613
+ }
614
+ }, 200);
615
+ });
616
+ }
617
+
618
+ // Planting animation (items appearing one by one)
619
+ async planting(items, message = "Planting") {
620
+ term.yellow(`\n 🌱 ${message}:\n`);
621
+
622
+ for (const item of items) {
623
+ await new Promise((r) => setTimeout(r, 150));
624
+ term.green(` 🌿 ${item}\n`);
625
+ }
626
+ }
627
+
628
+ // Harvest animation (gathering results)
629
+ async harvesting(items, message = "Harvesting") {
630
+ term.yellow(`\n 🌾 ${message}:\n`);
631
+
632
+ for (const item of items) {
633
+ await new Promise((r) => setTimeout(r, 100));
634
+ term.cyan(` ${emojis.wheat} ${item}\n`);
635
+ }
636
+ }
637
+ }
638
+
639
+ // Export singleton instance and class
640
+ export const farmTerm = new FarmTerminal();
641
+ export {
642
+ FarmTerminal,
643
+ term,
644
+ colors,
645
+ emojis,
646
+ TRACTOR_ART,
647
+ BARN_ART,
648
+ SMALL_TRACTOR,
649
+ };