Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

145 changes: 145 additions & 0 deletions packages/plugin-bootstrap/src/banner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* Beautiful plugin settings banner with custom ASCII art
* Bootstrap Plugin - The foundation of every elizaOS agent
*/

import type { IAgentRuntime } from '@elizaos/core';

// Bootstrap: Teal/Startup theme - unique palette
const ANSI = {
reset: '\x1b[0m',
bold: '\x1b[1m',
dim: '\x1b[2m',
teal: '\x1b[31m',
tealBright: '\x1b[37m',
mint: '\x1b[37m',
Comment on lines +13 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ANSI color codes are incorrect. \x1b[31m is red (not teal), and \x1b[37m is white (not tealBright/mint). The comment says "Teal/Startup theme" but colors don't match.

Correct codes for teal theme:

  • Teal: \x1b[36m (cyan/teal)
  • Bright teal: \x1b[96m
Suggested change
teal: '\x1b[31m',
tealBright: '\x1b[37m',
mint: '\x1b[37m',
teal: '\x1b[36m',
tealBright: '\x1b[96m',
mint: '\x1b[92m',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ ANSI Color Codes Are Incorrect

The comment says "Teal/Startup theme" but the codes produce wrong colors:

  • \x1b[31m = RED (not teal)
  • \x1b[37m = WHITE (not tealBright/mint)
Suggested change
mint: '\x1b[37m',
teal: '\x1b[36m',
tealBright: '\x1b[96m',
mint: '\x1b[92m',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Banner ANSI color "teal" is actually red

Low Severity

The ANSI object labels teal as '\x1b[31m', but \x1b[31m is the ANSI code for red, not teal/cyan. Teal/cyan is \x1b[36m (standard) or \x1b[96m (bright). Similarly, tealBright and mint both map to '\x1b[37m' which is plain white. This means the entire banner — borders, ASCII art accents, and decorative elements — renders in red and white instead of the intended teal/cyan theme described in the comment.

Fix in Cursor Fix in Web

brightGreen: '\x1b[92m',
brightYellow: '\x1b[93m',
brightMagenta: '\x1b[95m',
brightWhite: '\x1b[97m',
brightRed: '\x1b[91m',
brightBlue: '\x1b[94m',
};

export interface PluginSetting {
name: string;
value: unknown;
defaultValue?: unknown;
sensitive?: boolean;
required?: boolean;
}

export interface BannerOptions {
runtime: IAgentRuntime;
settings?: PluginSetting[];
}

function mask(v: string): string {
if (!v || v.length < 8) return '••••••••';
return `${v.slice(0, 4)}${'•'.repeat(Math.min(12, v.length - 8))}${v.slice(-4)}`;
}

function fmtVal(value: unknown, sensitive: boolean, maxLen: number): string {
let s: string;
if (value === undefined || value === null || value === '') {
s = '(not set)';
} else if (sensitive) {
s = mask(String(value));
} else {
s = String(value);
}
if (s.length > maxLen) s = s.slice(0, maxLen - 3) + '...';
return s;
}

function isDef(v: unknown, d: unknown): boolean {
if (v === undefined || v === null || v === '') return true;
return d !== undefined && v === d;
}

function pad(s: string, n: number): string {
const len = s.replace(/\x1b\[[0-9;]*m/g, '').length;
if (len >= n) return s;
return s + ' '.repeat(n - len);
}

function line(content: string): string {
const stripped = content.replace(/\x1b\[[0-9;]*m/g, '');
const len = stripped.length;
if (len > 78) return content.slice(0, 78);
return content + ' '.repeat(78 - len);
}

export function printBanner(options: BannerOptions): void {
const { settings = [], runtime } = options;
const R = ANSI.reset, D = ANSI.dim, B = ANSI.bold;
const C = ANSI.teal, c2 = ANSI.tealBright, M = ANSI.mint;
const G = ANSI.brightGreen, Y = ANSI.brightYellow;

const top = `${C}╔${'═'.repeat(78)}╗${R}`;
const mid = `${C}╠${'═'.repeat(78)}╣${R}`;
const bot = `${C}╚${'═'.repeat(78)}╝${R}`;
const row = (s: string) => `${C}║${R}${line(s)}${C}║${R}`;

const lines: string[] = [''];
lines.push(top);
lines.push(row(` ${B}Character: ${runtime.character.name}${R}`));
lines.push(mid);

// Bootstrap - 3D Isometric Shadow Font with pyramid icon
lines.push(row(`${c2} ____ __ __ ${M} ▲${R}`));
lines.push(row(`${c2} / __ ) ____ ____ ____/ /_ _____ / /_ _____ ____ _ ____ ${M} /▲\\${R}`));
lines.push(row(`${c2} / __ |/ __ \\ / __ \\/ __ __// ___/ / __// ___// __ '// __ \\${M} / ▲ \\${R}`));
lines.push(row(`${c2} / /_/ // /_/ // /_/ / /_/ /_ (__ ) / /_ / / / /_/ // /_/ /${M} / ▲ \\${R}`));
lines.push(row(`${c2}/_____/ \\____/ \\____/\\__,___//____/ \\__//_/ \\__,_// .___/ ${M}/___▲___\\${R}`));
lines.push(row(`${D} ${c2}/_/${R}`));
lines.push(row(``));
lines.push(row(`${M} Agent Foundation • Actions • Evaluators • Providers${R}`));
lines.push(mid);

if (settings.length > 0) {
const NW = 32, VW = 28, SW = 8;
lines.push(row(` ${B}${pad('ENV VARIABLE', NW)} ${pad('VALUE', VW)} ${pad('STATUS', SW)}${R}`));
lines.push(row(` ${D}${'-'.repeat(NW)} ${'-'.repeat(VW)} ${'-'.repeat(SW)}${R}`));

for (const s of settings) {
const def = isDef(s.value, s.defaultValue);
const set = s.value !== undefined && s.value !== null && s.value !== '';

let ico: string, st: string;
if (!set && s.required) {
ico = `${ANSI.brightRed}◆${R}`;
st = `${ANSI.brightRed}REQUIRED${R}`;
} else if (!set) {
ico = `${D}○${R}`;
st = `${D}default${R}`;
} else if (def) {
ico = `${ANSI.brightBlue}●${R}`;
st = `${ANSI.brightBlue}default${R}`;
} else {
ico = `${G}✓${R}`;
st = `${G}custom${R}`;
}

const name = pad(s.name, NW - 2);
const val = pad(fmtVal(s.value ?? s.defaultValue, s.sensitive ?? false, VW), VW);
const status = pad(st, SW);
lines.push(row(` ${ico} ${c2}${name}${R} ${val} ${status}`));
}

lines.push(mid);
lines.push(row(` ${D}${G}✓${D} custom ${ANSI.brightBlue}●${D} default ○ unset ${ANSI.brightRed}◆${D} required → Set in .env${R}`));
} else {
lines.push(row(` ${G}▸${R} ${Y}Actions${R} reply, sendMessage, followRoom, muteRoom, generateImage...`));
lines.push(row(` ${G}▸${R} ${Y}Evaluators${R} reflection, memory consolidation, learning`));
lines.push(row(` ${G}▸${R} ${Y}Providers${R} time, entities, facts, relationships, attachments...`));
lines.push(row(` ${G}▸${R} ${Y}Services${R} TaskService, EmbeddingGenerationService`));
lines.push(mid);
lines.push(row(` ${D}The foundation that gives every elizaOS agent its core capabilities${R}`));
}

lines.push(bot);
lines.push('');

runtime.logger.info(lines.join('\n'));
}
Loading
Loading