@poolzin/pool-bot 2026.3.7 → 2026.3.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/.buildstamp +1 -1
  3. package/dist/agents/error-classifier.js +302 -0
  4. package/dist/agents/skills/security.js +217 -0
  5. package/dist/build-info.json +3 -3
  6. package/dist/cli/lazy-commands.example.js +113 -0
  7. package/dist/cli/lazy-commands.js +329 -0
  8. package/dist/cli/program/command-registry.js +13 -0
  9. package/dist/cli/program/register.skills.js +4 -0
  10. package/dist/config/config.js +1 -0
  11. package/dist/config/secrets-integration.js +88 -0
  12. package/dist/context-engine/index.js +33 -0
  13. package/dist/context-engine/legacy.js +181 -0
  14. package/dist/context-engine/registry.js +86 -0
  15. package/dist/context-engine/summarizing.js +293 -0
  16. package/dist/context-engine/types.js +7 -0
  17. package/dist/infra/abort-pattern.js +106 -0
  18. package/dist/infra/retry.js +94 -0
  19. package/dist/secrets/index.js +28 -0
  20. package/dist/secrets/resolver.js +185 -0
  21. package/dist/secrets/runtime.js +142 -0
  22. package/dist/secrets/types.js +11 -0
  23. package/dist/security/dangerous-tools.js +80 -0
  24. package/dist/security/types.js +12 -0
  25. package/dist/skills/commands.js +351 -0
  26. package/dist/skills/index.js +167 -0
  27. package/dist/skills/loader.js +282 -0
  28. package/dist/skills/parser.js +461 -0
  29. package/dist/skills/registry.js +397 -0
  30. package/dist/skills/security.js +318 -0
  31. package/dist/skills/types.js +21 -0
  32. package/dist/test-utils/index.js +219 -0
  33. package/dist/tui/index.js +595 -0
  34. package/docs/INTEGRATION_PLAN.md +475 -0
  35. package/docs/INTEGRATION_SUMMARY.md +215 -0
  36. package/docs/integrations/HEXSTRIKE_PLAN.md +796 -0
  37. package/docs/integrations/INTEGRATION_PLAN.md +424 -0
  38. package/docs/integrations/PAGE_AGENT_PLAN.md +370 -0
  39. package/docs/integrations/XYOPS_PLAN.md +978 -0
  40. package/docs/skills/IMPLEMENTATION_SUMMARY.md +145 -0
  41. package/docs/skills/SKILL.md +524 -0
  42. package/docs/skills.md +405 -0
  43. package/package.json +1 -1
  44. package/skills/example-skill/SKILL.md +195 -0
@@ -0,0 +1,595 @@
1
+ /**
2
+ * Terminal UI (TUI) Components
3
+ *
4
+ * Modern, interactive terminal interface components for PoolBot.
5
+ * Provides rich terminal experiences with progress bars, spinners,
6
+ * tables, and interactive prompts.
7
+ */
8
+ import { stdin, stdout } from "node:process";
9
+ import { createInterface } from "node:readline";
10
+ /**
11
+ * Detect terminal capabilities
12
+ */
13
+ export function detectCapabilities() {
14
+ const isTTY = stdout.isTTY ?? false;
15
+ const env = process.env;
16
+ // Check color support
17
+ const term = env.TERM ?? "";
18
+ const colorterm = env.COLORTERM ?? "";
19
+ const trueColor = colorterm === "truecolor" || colorterm === "24bit";
20
+ const colors256 = trueColor || term.includes("256") || term.includes("color");
21
+ // Check Unicode support
22
+ const lang = env.LANG ?? "";
23
+ const lcAll = env.LC_ALL ?? "";
24
+ const unicode = /utf-?8/i.test(lang) || /utf-?8/i.test(lcAll) || process.platform !== "win32";
25
+ return {
26
+ unicode,
27
+ colors256,
28
+ trueColor,
29
+ width: stdout.columns ?? 80,
30
+ height: stdout.rows ?? 24,
31
+ cursor: isTTY,
32
+ isTTY,
33
+ };
34
+ }
35
+ /**
36
+ * ANSI color codes
37
+ */
38
+ export const Colors = {
39
+ // Reset
40
+ reset: "\x1b[0m",
41
+ // Foreground colors
42
+ black: "\x1b[30m",
43
+ red: "\x1b[31m",
44
+ green: "\x1b[32m",
45
+ yellow: "\x1b[33m",
46
+ blue: "\x1b[34m",
47
+ magenta: "\x1b[35m",
48
+ cyan: "\x1b[36m",
49
+ white: "\x1b[37m",
50
+ gray: "\x1b[90m",
51
+ // Bright colors
52
+ brightRed: "\x1b[91m",
53
+ brightGreen: "\x1b[92m",
54
+ brightYellow: "\x1b[93m",
55
+ brightBlue: "\x1b[94m",
56
+ brightMagenta: "\x1b[95m",
57
+ brightCyan: "\x1b[96m",
58
+ brightWhite: "\x1b[97m",
59
+ // Background colors
60
+ bgBlack: "\x1b[40m",
61
+ bgRed: "\x1b[41m",
62
+ bgGreen: "\x1b[42m",
63
+ bgYellow: "\x1b[43m",
64
+ bgBlue: "\x1b[44m",
65
+ bgMagenta: "\x1b[45m",
66
+ bgCyan: "\x1b[46m",
67
+ bgWhite: "\x1b[47m",
68
+ // Styles
69
+ bold: "\x1b[1m",
70
+ dim: "\x1b[2m",
71
+ italic: "\x1b[3m",
72
+ underline: "\x1b[4m",
73
+ strikethrough: "\x1b[9m",
74
+ };
75
+ /**
76
+ * Colorize text with ANSI codes
77
+ */
78
+ export function colorize(text, ...codes) {
79
+ const caps = detectCapabilities();
80
+ if (!caps.colors256)
81
+ return text;
82
+ return codes.join("") + text + Colors.reset;
83
+ }
84
+ /**
85
+ * Spinner characters for different styles
86
+ */
87
+ export const SpinnerChars = {
88
+ dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
89
+ line: ["-", "\\", "|", "/"],
90
+ arrow: ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"],
91
+ bounce: ["( ● )", "( ● )", "( ● )", "( ● )", "( ●)", "( ● )", "( ● )", "( ● )", "( ● )", "(● )"],
92
+ pulse: ["█", "▉", "▊", "▋", "▌", "▍", "▎", "▏"],
93
+ };
94
+ /**
95
+ * Terminal spinner for loading states
96
+ */
97
+ export class Spinner {
98
+ frames;
99
+ interval;
100
+ text;
101
+ color;
102
+ currentFrame = 0;
103
+ timer = null;
104
+ active = false;
105
+ caps;
106
+ constructor(options) {
107
+ this.caps = detectCapabilities();
108
+ this.frames = SpinnerChars[options.style ?? "dots"];
109
+ this.interval = options.interval ?? 80;
110
+ this.text = options.text;
111
+ this.color = options.color ?? Colors.cyan;
112
+ }
113
+ /**
114
+ * Start the spinner
115
+ */
116
+ start() {
117
+ if (this.active || !this.caps.isTTY)
118
+ return this;
119
+ this.active = true;
120
+ this.render();
121
+ this.timer = setInterval(() => {
122
+ this.currentFrame = (this.currentFrame + 1) % this.frames.length;
123
+ this.render();
124
+ }, this.interval);
125
+ return this;
126
+ }
127
+ /**
128
+ * Stop the spinner
129
+ */
130
+ stop(finalText) {
131
+ if (!this.active)
132
+ return;
133
+ if (this.timer) {
134
+ clearInterval(this.timer);
135
+ this.timer = null;
136
+ }
137
+ this.active = false;
138
+ // Clear line and show final text
139
+ if (finalText) {
140
+ stdout.write("\r\x1b[K" + finalText + "\n");
141
+ }
142
+ else {
143
+ stdout.write("\r\x1b[K");
144
+ }
145
+ }
146
+ /**
147
+ * Update spinner text
148
+ */
149
+ update(text) {
150
+ this.text = text;
151
+ if (this.active) {
152
+ this.render();
153
+ }
154
+ }
155
+ /**
156
+ * Render current frame
157
+ */
158
+ render() {
159
+ if (!this.caps.isTTY)
160
+ return;
161
+ const frame = this.frames[this.currentFrame];
162
+ const coloredFrame = this.caps.colors256 ? this.color + frame + Colors.reset : frame;
163
+ stdout.write(`\r\x1b[K${coloredFrame} ${this.text}`);
164
+ }
165
+ }
166
+ /**
167
+ * Terminal progress bar
168
+ */
169
+ export class ProgressBar {
170
+ total;
171
+ current = 0;
172
+ width;
173
+ completeChar;
174
+ incompleteChar;
175
+ showPercent;
176
+ showValue;
177
+ prefix;
178
+ completeColor;
179
+ incompleteColor;
180
+ caps;
181
+ constructor(options) {
182
+ this.caps = detectCapabilities();
183
+ this.total = options.total;
184
+ this.width = options.width ?? 40;
185
+ this.completeChar = options.completeChar ?? (this.caps.unicode ? "█" : "=");
186
+ this.incompleteChar = options.incompleteChar ?? (this.caps.unicode ? "░" : "-");
187
+ this.showPercent = options.showPercent ?? true;
188
+ this.showValue = options.showValue ?? false;
189
+ this.prefix = options.prefix ?? "";
190
+ this.completeColor = options.completeColor ?? Colors.green;
191
+ this.incompleteColor = options.incompleteColor ?? Colors.gray;
192
+ }
193
+ /**
194
+ * Update progress
195
+ */
196
+ update(value) {
197
+ this.current = Math.min(value, this.total);
198
+ this.render();
199
+ }
200
+ /**
201
+ * Increment progress
202
+ */
203
+ increment(amount = 1) {
204
+ this.update(this.current + amount);
205
+ }
206
+ /**
207
+ * Complete the progress bar
208
+ */
209
+ complete(message) {
210
+ this.update(this.total);
211
+ if (message) {
212
+ stdout.write(" " + message);
213
+ }
214
+ stdout.write("\n");
215
+ }
216
+ /**
217
+ * Render the progress bar
218
+ */
219
+ render() {
220
+ if (!this.caps.isTTY)
221
+ return;
222
+ const percent = this.current / this.total;
223
+ const filled = Math.round(this.width * percent);
224
+ const empty = this.width - filled;
225
+ const completeStr = this.completeChar.repeat(filled);
226
+ const incompleteStr = this.incompleteChar.repeat(empty);
227
+ const coloredComplete = this.caps.colors256
228
+ ? this.completeColor + completeStr + Colors.reset
229
+ : completeStr;
230
+ const coloredIncomplete = this.caps.colors256
231
+ ? this.incompleteColor + incompleteStr + Colors.reset
232
+ : incompleteStr;
233
+ let suffix = "";
234
+ if (this.showPercent) {
235
+ suffix += ` ${(percent * 100).toFixed(1)}%`;
236
+ }
237
+ if (this.showValue) {
238
+ suffix += ` [${this.current}/${this.total}]`;
239
+ }
240
+ const prefix = this.prefix ? this.prefix + " " : "";
241
+ stdout.write(`\r\x1b[K${prefix}${coloredComplete}${coloredIncomplete}${suffix}`);
242
+ }
243
+ }
244
+ /**
245
+ * Border characters for different styles
246
+ */
247
+ const BorderChars = {
248
+ simple: {
249
+ topLeft: "+",
250
+ topRight: "+",
251
+ bottomLeft: "+",
252
+ bottomRight: "+",
253
+ horizontal: "-",
254
+ vertical: "|",
255
+ cross: "+",
256
+ leftT: "+",
257
+ rightT: "+",
258
+ topT: "+",
259
+ bottomT: "+",
260
+ },
261
+ rounded: {
262
+ topLeft: "╭",
263
+ topRight: "╮",
264
+ bottomLeft: "╰",
265
+ bottomRight: "╯",
266
+ horizontal: "─",
267
+ vertical: "│",
268
+ cross: "┼",
269
+ leftT: "├",
270
+ rightT: "┤",
271
+ topT: "┬",
272
+ bottomT: "┴",
273
+ },
274
+ heavy: {
275
+ topLeft: "┏",
276
+ topRight: "┓",
277
+ bottomLeft: "┗",
278
+ bottomRight: "┛",
279
+ horizontal: "━",
280
+ vertical: "┃",
281
+ cross: "╋",
282
+ leftT: "┣",
283
+ rightT: "┫",
284
+ topT: "┳",
285
+ bottomT: "┻",
286
+ },
287
+ none: {
288
+ topLeft: "",
289
+ topRight: "",
290
+ bottomLeft: "",
291
+ bottomRight: "",
292
+ horizontal: "",
293
+ vertical: "",
294
+ cross: "",
295
+ leftT: "",
296
+ rightT: "",
297
+ topT: "",
298
+ bottomT: "",
299
+ },
300
+ };
301
+ /**
302
+ * Terminal table renderer
303
+ */
304
+ export class Table {
305
+ columns;
306
+ data;
307
+ showHeader;
308
+ borderStyle;
309
+ rowSeparator;
310
+ caps;
311
+ constructor(options) {
312
+ this.caps = detectCapabilities();
313
+ this.columns = options.columns;
314
+ this.data = options.data;
315
+ this.showHeader = options.showHeader ?? true;
316
+ this.borderStyle = options.borderStyle ?? "simple";
317
+ this.rowSeparator = options.rowSeparator ?? false;
318
+ }
319
+ /**
320
+ * Render the table
321
+ */
322
+ render() {
323
+ const borders = BorderChars[this.borderStyle];
324
+ const colWidths = this.calculateWidths();
325
+ const lines = [];
326
+ // Top border
327
+ if (this.borderStyle !== "none") {
328
+ lines.push(this.renderHorizontalLine(borders.topLeft, borders.topRight, borders.horizontal, borders.topT, colWidths));
329
+ }
330
+ // Header row
331
+ if (this.showHeader) {
332
+ const headerCells = this.columns.map((col, i) => {
333
+ const width = colWidths[i];
334
+ const text = this.truncate(col.header, width);
335
+ const aligned = this.align(text, width, col.align ?? "left");
336
+ const colored = col.headerColor && this.caps.colors256
337
+ ? col.headerColor + aligned + Colors.reset
338
+ : aligned;
339
+ return colored;
340
+ });
341
+ lines.push(this.renderRow(headerCells, borders.vertical));
342
+ // Header separator
343
+ if (this.borderStyle !== "none") {
344
+ lines.push(this.renderHorizontalLine(borders.leftT, borders.rightT, borders.horizontal, borders.cross, colWidths));
345
+ }
346
+ }
347
+ // Data rows
348
+ for (let i = 0; i < this.data.length; i++) {
349
+ const row = this.data[i];
350
+ const cells = row.map((cell, j) => {
351
+ const width = colWidths[j];
352
+ const text = this.truncate(cell ?? "", width);
353
+ return this.align(text, width, this.columns[j]?.align ?? "left");
354
+ });
355
+ lines.push(this.renderRow(cells, borders.vertical));
356
+ // Row separator
357
+ if (this.rowSeparator && i < this.data.length - 1 && this.borderStyle !== "none") {
358
+ lines.push(this.renderHorizontalLine(borders.leftT, borders.rightT, borders.horizontal, borders.cross, colWidths));
359
+ }
360
+ }
361
+ // Bottom border
362
+ if (this.borderStyle !== "none") {
363
+ lines.push(this.renderHorizontalLine(borders.bottomLeft, borders.bottomRight, borders.horizontal, borders.bottomT, colWidths));
364
+ }
365
+ return lines.join("\n");
366
+ }
367
+ /**
368
+ * Print the table to stdout
369
+ */
370
+ print() {
371
+ console.log(this.render());
372
+ }
373
+ calculateWidths() {
374
+ const caps = detectCapabilities();
375
+ const maxWidth = caps.width - 4; // Padding for borders
376
+ return this.columns.map((col, i) => {
377
+ if (col.width)
378
+ return col.width;
379
+ // Calculate based on content
380
+ const headerLen = col.header.length;
381
+ const maxDataLen = this.data.reduce((max, row) => {
382
+ const cellLen = (row[i] ?? "").length;
383
+ return Math.max(max, cellLen);
384
+ }, 0);
385
+ return Math.min(Math.max(headerLen, maxDataLen) + 2, Math.floor(maxWidth / this.columns.length));
386
+ });
387
+ }
388
+ renderHorizontalLine(left, right, horizontal, cross, widths) {
389
+ const segments = widths.map(w => horizontal.repeat(w));
390
+ return left + segments.join(cross) + right;
391
+ }
392
+ renderRow(cells, vertical) {
393
+ return vertical + cells.join(vertical) + vertical;
394
+ }
395
+ truncate(text, width) {
396
+ if (text.length <= width)
397
+ return text;
398
+ return text.slice(0, width - 3) + "...";
399
+ }
400
+ align(text, width, align) {
401
+ const padding = width - text.length;
402
+ switch (align) {
403
+ case "center": {
404
+ const left = Math.floor(padding / 2);
405
+ const right = padding - left;
406
+ return " ".repeat(left) + text + " ".repeat(right);
407
+ }
408
+ case "right":
409
+ return " ".repeat(padding) + text;
410
+ case "left":
411
+ default:
412
+ return text + " ".repeat(padding);
413
+ }
414
+ }
415
+ }
416
+ /**
417
+ * Interactive prompts
418
+ */
419
+ export class Prompts {
420
+ /**
421
+ * Confirm yes/no prompt
422
+ */
423
+ static async confirm(options) {
424
+ const defaultText = options.default === false ? "y/N" : "Y/n";
425
+ const rl = createInterface({ input: stdin, output: stdout });
426
+ try {
427
+ const answer = await new Promise((resolve) => {
428
+ rl.question(`${options.message} [${defaultText}] `, resolve);
429
+ });
430
+ const normalized = answer.trim().toLowerCase();
431
+ if (normalized === "")
432
+ return options.default ?? true;
433
+ return normalized === "y" || normalized === "yes";
434
+ }
435
+ finally {
436
+ rl.close();
437
+ }
438
+ }
439
+ /**
440
+ * Text input prompt
441
+ */
442
+ static async input(options) {
443
+ const rl = createInterface({ input: stdin, output: stdout });
444
+ try {
445
+ const prompt = options.default
446
+ ? `${options.message} (${options.default}) `
447
+ : `${options.message} `;
448
+ const answer = await new Promise((resolve) => {
449
+ rl.question(prompt, resolve);
450
+ });
451
+ const value = answer.trim() || options.default || "";
452
+ if (options.validate) {
453
+ const result = options.validate(value);
454
+ if (result !== true) {
455
+ console.log(colorize(`Error: ${result}`, Colors.red));
456
+ return Prompts.input(options);
457
+ }
458
+ }
459
+ return value;
460
+ }
461
+ finally {
462
+ rl.close();
463
+ }
464
+ }
465
+ /**
466
+ * Select from list prompt
467
+ */
468
+ static async select(options) {
469
+ const available = options.choices.filter(c => !c.disabled);
470
+ console.log(options.message);
471
+ available.forEach((choice, i) => {
472
+ const marker = choice.value === options.default ? colorize("›", Colors.cyan) : " ";
473
+ console.log(` ${marker} ${i + 1}) ${choice.name}`);
474
+ });
475
+ const rl = createInterface({ input: stdin, output: stdout });
476
+ try {
477
+ const answer = await new Promise((resolve) => {
478
+ rl.question("Select: ", resolve);
479
+ });
480
+ const num = parseInt(answer.trim(), 10);
481
+ if (num >= 1 && num <= available.length) {
482
+ return available[num - 1].value;
483
+ }
484
+ // Try matching by value
485
+ const match = available.find(c => c.value.toLowerCase() === answer.trim().toLowerCase());
486
+ if (match)
487
+ return match.value;
488
+ console.log(colorize("Invalid selection", Colors.red));
489
+ return Prompts.select(options);
490
+ }
491
+ finally {
492
+ rl.close();
493
+ }
494
+ }
495
+ }
496
+ /**
497
+ * Status indicators
498
+ */
499
+ export const Status = {
500
+ success: colorize("✓", Colors.green),
501
+ error: colorize("✗", Colors.red),
502
+ warning: colorize("⚠", Colors.yellow),
503
+ info: colorize("ℹ", Colors.blue),
504
+ pending: colorize("○", Colors.gray),
505
+ };
506
+ /**
507
+ * Log a message with a status indicator
508
+ */
509
+ export function logStatus(status, message) {
510
+ console.log(`${Status[status]} ${message}`);
511
+ }
512
+ /**
513
+ * Draw a box around content
514
+ */
515
+ export function box(content, options = {}) {
516
+ const caps = detectCapabilities();
517
+ const lines = content.split("\n");
518
+ const maxLineLen = Math.max(...lines.map(l => l.length));
519
+ const width = options.width ?? maxLineLen + 4;
520
+ const padding = options.padding ?? 1;
521
+ const borders = {
522
+ simple: { tl: "+", tr: "+", bl: "+", br: "+", h: "-", v: "|" },
523
+ rounded: { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│" },
524
+ heavy: { tl: "┏", tr: "┓", bl: "┗", br: "┛", h: "━", v: "┃" },
525
+ double: { tl: "╔", tr: "╗", bl: "╚", br: "╝", h: "═", v: "║" },
526
+ }[options.style ?? "rounded"];
527
+ const result = [];
528
+ // Top border with title
529
+ let topBorder = borders.tl + borders.h.repeat(width - 2) + borders.tr;
530
+ if (options.title) {
531
+ const title = caps.colors256 && options.titleColor
532
+ ? options.titleColor + options.title + Colors.reset
533
+ : options.title;
534
+ const titleWithPadding = ` ${title} `;
535
+ const before = Math.floor((width - titleWithPadding.length) / 2);
536
+ topBorder = borders.tl + borders.h.repeat(before) + titleWithPadding + borders.h.repeat(width - before - titleWithPadding.length - 2) + borders.tr;
537
+ }
538
+ result.push(topBorder);
539
+ // Padding top
540
+ for (let i = 0; i < padding; i++) {
541
+ result.push(borders.v + " ".repeat(width - 2) + borders.v);
542
+ }
543
+ // Content
544
+ for (const line of lines) {
545
+ const contentWidth = Math.max(0, width - line.length - 2 - padding * 2);
546
+ const padded = " ".repeat(padding) + line + " ".repeat(contentWidth);
547
+ result.push(borders.v + padded + borders.v);
548
+ }
549
+ // Padding bottom
550
+ for (let i = 0; i < padding; i++) {
551
+ result.push(borders.v + " ".repeat(width - 2) + borders.v);
552
+ }
553
+ // Bottom border
554
+ result.push(borders.bl + borders.h.repeat(width - 2) + borders.br);
555
+ return result.join("\n");
556
+ }
557
+ /**
558
+ * Create a multi-step wizard
559
+ */
560
+ export class Wizard {
561
+ steps = [];
562
+ _currentStep = 0;
563
+ /**
564
+ * Add a step to the wizard
565
+ */
566
+ step(name, run) {
567
+ this.steps.push({ name, run });
568
+ return this;
569
+ }
570
+ /**
571
+ * Run the wizard
572
+ */
573
+ async run() {
574
+ console.log(box("Starting Setup Wizard", { title: "Wizard", style: "rounded" }));
575
+ for (let i = 0; i < this.steps.length; i++) {
576
+ this._currentStep = i;
577
+ const step = this.steps[i];
578
+ console.log(`\n${colorize(`Step ${i + 1}/${this.steps.length}:`, Colors.cyan)} ${step.name}`);
579
+ try {
580
+ const success = await step.run();
581
+ if (!success) {
582
+ logStatus("error", `Step "${step.name}" failed`);
583
+ return false;
584
+ }
585
+ logStatus("success", `Step "${step.name}" completed`);
586
+ }
587
+ catch (error) {
588
+ logStatus("error", `Step "${step.name}" error: ${error instanceof Error ? error.message : String(error)}`);
589
+ return false;
590
+ }
591
+ }
592
+ console.log("\n" + box("Wizard completed successfully!", { style: "rounded", title: "✓ Complete" }));
593
+ return true;
594
+ }
595
+ }