From 39749ab20ef2dc7ca8d22b0df378ffa2748b5c23 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Wed, 6 May 2026 04:16:59 -0700 Subject: [PATCH 1/5] feat: bare-invocation banner with quick-start cheat sheet When a human runs `agentscore-pay` with no args in a TTY, render a compact AGENTSCORE PAY block-char logo plus tagline and quick-start cheat sheet. Silent on non-TTY (agents in shell-tool loops capture clean output) and on structured/JSON modes. Falls back to plain text when the terminal is narrower than 60 cols. Stderr-only. Co-Authored-By: Claude Opus 4.7 (1M context) --- package.json | 2 +- src/banner.ts | 49 ++++++++++++++++++++++++++++++++++++ src/index.ts | 7 ++++++ tests/banner.test.ts | 60 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 src/banner.ts create mode 100644 tests/banner.test.ts diff --git a/package.json b/package.json index 526ff1f..972b817 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@agent-score/pay", - "version": "0.1.0-rc.17", + "version": "0.1.0-rc.18", "description": "CLI wallet for one-shell-command agent payments across x402 (Base) and MPP (Tempo, Solana)", "type": "module", "main": "./dist/index.js", diff --git a/src/banner.ts b/src/banner.ts new file mode 100644 index 0000000..471dbc2 --- /dev/null +++ b/src/banner.ts @@ -0,0 +1,49 @@ +const BANNER_ART = [ + ' ▄▀█ █▀▀ █▀▀ █▄ █ ▀█▀ █▀▀ █▀▀ █▀█ █▀█ █▀▀ █▀█ ▄▀█ █▄█', + ' █▀█ █▄█ ██▄ █ ▀█ █ ▄▄█ █▄▄ █▄█ █▀▄ ██▄ █▀▀ █▀█ █', +].join('\n'); + +const TAGLINE = 'Universal agent-payment CLI'; + +const QUICK_START = [ + 'Quick start:', + ' agentscore-pay init Create encrypted wallet across base, solana, tempo', + ' agentscore-pay balance Check USDC across chains', + ' agentscore-pay pay POST Pay any 402 / MPP merchant', + ' agentscore-pay fund options List funding methods for a wallet', + ' agentscore-pay agent-guide Full guide for LLM agents', + '', + 'Run `agentscore-pay --help` for the full command list.', +].join('\n'); + +const MIN_BANNER_COLS = 60; +const PLAIN_BRAND = 'AgentScore Pay'; +const ART_FG_ANSI = '\x1b[38;5;39m'; +const ANSI_RESET = '\x1b[0m'; + +function shouldUseColor(): boolean { + if (process.env.NO_COLOR) return false; + return true; +} + +export function renderBanner({ + cols, + color, +}: { + cols: number; + color: boolean; +}): string { + if (cols < MIN_BANNER_COLS) { + return `${PLAIN_BRAND}\n${TAGLINE}\n\n${QUICK_START}\n`; + } + const art = color + ? BANNER_ART.split('\n').map((line) => `${ART_FG_ANSI}${line}${ANSI_RESET}`).join('\n') + : BANNER_ART; + return `${art}\n\n${TAGLINE}\n\n${QUICK_START}\n`; +} + +export function printBanner(): void { + if (!process.stderr.isTTY) return; + const cols = process.stderr.columns ?? 80; + process.stderr.write(renderBanner({ cols, color: shouldUseColor() })); +} diff --git a/src/index.ts b/src/index.ts index bbf6000..9c18dd5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import { printBanner } from './banner'; import { run } from './cli'; import { getNoticeIfNewer, refreshCacheAwaited, refreshCacheInBackground } from './update-check'; @@ -15,6 +16,12 @@ async function emitUpdateNoticeIfAvailable(): Promise { } } +if (process.argv.length === 2 && process.stderr.isTTY) { + printBanner(); + refreshCacheInBackground(); + process.exit(0); +} + run() .then(async () => { await emitUpdateNoticeIfAvailable(); diff --git a/tests/banner.test.ts b/tests/banner.test.ts new file mode 100644 index 0000000..58db8db --- /dev/null +++ b/tests/banner.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest'; +import { renderBanner } from '../src/banner'; + +describe('banner', () => { + it('renders block-art logo when terminal is wide enough', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).toContain('▄▀█'); + expect(out).toContain('Universal agent-payment CLI'); + expect(out).toContain('Quick start:'); + expect(out).toContain('agentscore-pay init'); + }); + + it('falls back to plain-text brand on narrow terminals', () => { + const out = renderBanner({ cols: 40, color: false }); + expect(out).not.toContain('▄▀█'); + expect(out).toContain('AgentScore Pay'); + expect(out).toContain('Universal agent-payment CLI'); + expect(out).toContain('Quick start:'); + }); + + it('uses full brand prefix, never bare Pay or PAY.SH', () => { + const wide = renderBanner({ cols: 100, color: false }); + const narrow = renderBanner({ cols: 40, color: false }); + expect(wide).not.toContain('PAY.SH'); + expect(narrow).not.toContain('PAY.SH'); + expect(narrow).toContain('AgentScore Pay'); + }); + + it('omits ANSI escapes when color is disabled', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).not.toContain('\x1b['); + }); + + it('includes ANSI escapes when color is enabled', () => { + const out = renderBanner({ cols: 100, color: true }); + expect(out).toContain('\x1b['); + }); + + it('plain-text fallback never includes ANSI escapes regardless of color flag', () => { + const out = renderBanner({ cols: 40, color: true }); + expect(out).not.toContain('\x1b['); + }); + + it('art width fits within 60 cols (the threshold for falling back)', () => { + const out = renderBanner({ cols: 100, color: false }); + const artLines = out.split('\n').filter((line) => line.includes('▄') || line.includes('▀') || line.includes('█')); + for (const line of artLines) { + expect(line.length).toBeLessThanOrEqual(60); + } + }); + + it('quick-start references the binary, not the brand', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).toContain('agentscore-pay init'); + expect(out).toContain('agentscore-pay balance'); + expect(out).toContain('agentscore-pay pay POST '); + expect(out).toContain('agentscore-pay fund options'); + expect(out).toContain('agentscore-pay agent-guide'); + }); +}); From 2bea7e043ebb49b9054d294465487411d81e296d Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Wed, 6 May 2026 04:43:57 -0700 Subject: [PATCH 2/5] feat(banner): switch to shade-style block art with forest gradient Generated via oh-my-logo (one-shot, dev-time). Embeds a baked true-color (24-bit RGB) forest gradient that renders across columns in any color-capable terminal. NO_COLOR strips to plain shade chars. Width threshold raised to 70 cols (art is 67 wide). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/banner.ts | 34 +++++++++++++++++++++++++--------- tests/banner.test.ts | 11 ++++++----- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/banner.ts b/src/banner.ts index 471dbc2..267ed50 100644 --- a/src/banner.ts +++ b/src/banner.ts @@ -1,6 +1,26 @@ -const BANNER_ART = [ - ' ▄▀█ █▀▀ █▀▀ █▄ █ ▀█▀ █▀▀ █▀▀ █▀█ █▀█ █▀▀ █▀█ ▄▀█ █▄█', - ' █▀█ █▄█ ██▄ █ ▀█ █ ▄▄█ █▄▄ █▄█ █▀▄ ██▄ █▀▀ █▀█ █', +const BANNER_ART_COLORED = [ + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m░\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m░\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m░\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m░\x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m░\x1b[38;2;36;96;100m░\x1b[38;2;38;98;101m░\x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m░\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m░\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m░\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m░\x1b[38;2;57;119;108m░\x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m░\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m░\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m░\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m░\x1b[38;2;85;148;118m░\x1b[38;2;86;149;118m░\x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m░\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m░\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m░\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m░\x1b[38;2;113;178;128m░\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m░\x1b[38;2;22;81;95m█\x1b[38;2;23;83;96m█\x1b[38;2;25;84;96m░\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m░\x1b[38;2;29;89;98m█\x1b[38;2;30;90;98m█\x1b[38;2;32;92;99m█\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m█\x1b[38;2;38;98;101m█\x1b[38;2;39;99;101m█\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m█\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m█\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m█\x1b[38;2;57;119;108m█\x1b[38;2;59;120;108m█\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m█\x1b[38;2;65;126;110m█\x1b[38;2;66;128;111m█\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m█\x1b[38;2;72;134;113m█\x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m█\x1b[38;2;79;142;116m█\x1b[38;2;80;143;116m░\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m█\x1b[38;2;86;149;118m█\x1b[38;2;87;151;119m█\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m█\x1b[38;2;99;163;123m█\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m░\x1b[38;2;104;169;125m█\x1b[38;2;106;170;125m█\x1b[38;2;107;172;126m░\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m█\x1b[38;2;112;176;127m░\x1b[38;2;113;178;128m█\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m \x1b[38;2;23;83;96m \x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m \x1b[38;2;30;90;98m \x1b[38;2;32;92;99m \x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m \x1b[38;2;38;98;101m \x1b[38;2;39;99;101m \x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m█\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m \x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m \x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m█\x1b[38;2;56;117;107m \x1b[38;2;57;119;108m \x1b[38;2;59;120;108m \x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m \x1b[38;2;65;126;110m \x1b[38;2;66;128;111m \x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m█\x1b[38;2;70;133;113m \x1b[38;2;72;134;113m \x1b[38;2;73;136;114m█\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m \x1b[38;2;79;142;116m \x1b[38;2;80;143;116m█\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m \x1b[38;2;86;149;118m \x1b[38;2;87;151;119m \x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m \x1b[38;2;99;163;123m \x1b[38;2;100;164;123m█\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m \x1b[38;2;106;170;125m \x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m█\x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m█\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m█\x1b[38;2;23;83;96m█\x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m█\x1b[38;2;32;92;99m█\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m█\x1b[38;2;38;98;101m█\x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m \x1b[38;2;45;105;103m█\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m \x1b[38;2;56;117;107m█\x1b[38;2;57;119;108m█\x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m█\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m█\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m█\x1b[38;2;79;142;116m█\x1b[38;2;80;143;116m \x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m█\x1b[38;2;86;149;118m█\x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m█\x1b[38;2;99;163;123m█\x1b[38;2;100;164;123m \x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m█\x1b[38;2;106;170;125m█\x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m \x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m \x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m \x1b[38;2;23;83;96m \x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m \x1b[38;2;32;92;99m█\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m \x1b[38;2;38;98;101m \x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m \x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m \x1b[38;2;57;119;108m \x1b[38;2;59;120;108m█\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m█\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m█\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m \x1b[38;2;79;142;116m \x1b[38;2;80;143;116m█\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m \x1b[38;2;86;149;118m \x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m \x1b[38;2;99;163;123m \x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m \x1b[38;2;106;170;125m \x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m░\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m█\x1b[38;2;30;90;98m█\x1b[38;2;32;92;99m \x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m█\x1b[38;2;38;98;101m█\x1b[38;2;39;99;101m█\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m█\x1b[38;2;56;117;107m█\x1b[38;2;57;119;108m█\x1b[38;2;59;120;108m \x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m█\x1b[38;2;65;126;110m█\x1b[38;2;66;128;111m█\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m \x1b[38;2;70;133;113m█\x1b[38;2;72;134;113m█\x1b[38;2;73;136;114m \x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m█\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m█\x1b[38;2;86;149;118m█\x1b[38;2;87;151;119m█\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m░\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m \x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m \x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m \x1b[38;2;29;89;98m \x1b[38;2;30;90;98m \x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m \x1b[38;2;36;96;100m \x1b[38;2;38;98;101m \x1b[38;2;39;99;101m \x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m \x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m \x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m \x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m \x1b[38;2;56;117;107m \x1b[38;2;57;119;108m \x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m \x1b[38;2;63;125;110m \x1b[38;2;65;126;110m \x1b[38;2;66;128;111m \x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m \x1b[38;2;72;134;113m \x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m \x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m \x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m \x1b[38;2;85;148;118m \x1b[38;2;86;149;118m \x1b[38;2;87;151;119m \x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m \x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m \x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m \x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m \x1b[38;2;113;178;128m░\x1b[39m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m░\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m░\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m░\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m░\x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m░\x1b[38;2;36;96;100m░\x1b[38;2;38;98;101m░\x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m░\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m░\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m░\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m░\x1b[38;2;57;119;108m░\x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m░\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m░\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m░\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m░\x1b[38;2;85;148;118m░\x1b[38;2;86;149;118m░\x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m░\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m░\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m░\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m░\x1b[38;2;113;178;128m░\x1b[39m', + '', + '', + '\x1b[0m', +].join('\n'); + +const BANNER_ART_PLAIN = [ + '░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░', + '░░██░░░███░████░█░░█░███░░███░████░░██░░███░░████░░░░░███░░░██░░█░█', + '░█ █░█ ░█ ░██░█░ █ ░█ ░█ ░█ █░█ █░█ ░░░░░█ █░█ █░███', + '░████░█░██░███░░█ ██░░█░░ ██░░█░░░░█░░█░███ ░███░░░░░░███ ░████░ █', + '░█ █░█░ █░█ ░░█░ █░░█░░░ █░█░░░░█░░█░█ █░█ ░░░░░░█ ░░█ █░░█░', + '░█░░█░███ ░████░█░░█░░█░░███ ░████░ ██ ░█░░█░████░░░░░█░░░░█░░█░░█░', + '░ ░░ ░ ░░ ░ ░░ ░░ ░░ ░░ ░░ ░░ ░░ ░ ░░░░░ ░░░░ ░░ ░░ ░', + '░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░', ].join('\n'); const TAGLINE = 'Universal agent-payment CLI'; @@ -16,10 +36,8 @@ const QUICK_START = [ 'Run `agentscore-pay --help` for the full command list.', ].join('\n'); -const MIN_BANNER_COLS = 60; +const MIN_BANNER_COLS = 70; const PLAIN_BRAND = 'AgentScore Pay'; -const ART_FG_ANSI = '\x1b[38;5;39m'; -const ANSI_RESET = '\x1b[0m'; function shouldUseColor(): boolean { if (process.env.NO_COLOR) return false; @@ -36,9 +54,7 @@ export function renderBanner({ if (cols < MIN_BANNER_COLS) { return `${PLAIN_BRAND}\n${TAGLINE}\n\n${QUICK_START}\n`; } - const art = color - ? BANNER_ART.split('\n').map((line) => `${ART_FG_ANSI}${line}${ANSI_RESET}`).join('\n') - : BANNER_ART; + const art = color ? BANNER_ART_COLORED : BANNER_ART_PLAIN; return `${art}\n\n${TAGLINE}\n\n${QUICK_START}\n`; } diff --git a/tests/banner.test.ts b/tests/banner.test.ts index 58db8db..a15c569 100644 --- a/tests/banner.test.ts +++ b/tests/banner.test.ts @@ -4,7 +4,8 @@ import { renderBanner } from '../src/banner'; describe('banner', () => { it('renders block-art logo when terminal is wide enough', () => { const out = renderBanner({ cols: 100, color: false }); - expect(out).toContain('▄▀█'); + expect(out).toContain('░'); + expect(out).toContain('█'); expect(out).toContain('Universal agent-payment CLI'); expect(out).toContain('Quick start:'); expect(out).toContain('agentscore-pay init'); @@ -12,7 +13,7 @@ describe('banner', () => { it('falls back to plain-text brand on narrow terminals', () => { const out = renderBanner({ cols: 40, color: false }); - expect(out).not.toContain('▄▀█'); + expect(out).not.toContain('░'); expect(out).toContain('AgentScore Pay'); expect(out).toContain('Universal agent-payment CLI'); expect(out).toContain('Quick start:'); @@ -41,11 +42,11 @@ describe('banner', () => { expect(out).not.toContain('\x1b['); }); - it('art width fits within 60 cols (the threshold for falling back)', () => { + it('plain art width fits within 70 cols (the threshold for falling back)', () => { const out = renderBanner({ cols: 100, color: false }); - const artLines = out.split('\n').filter((line) => line.includes('▄') || line.includes('▀') || line.includes('█')); + const artLines = out.split('\n').filter((line) => line.includes('░') || line.includes('█')); for (const line of artLines) { - expect(line.length).toBeLessThanOrEqual(60); + expect(line.length).toBeLessThanOrEqual(70); } }); From 3823afcf822091460f0fcf510602b1f27b7282d1 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Wed, 6 May 2026 04:49:10 -0700 Subject: [PATCH 3/5] fix(banner): drop trailing blank lines in colored art The colored art had two empty entries plus a standalone reset entry after the last visible line, which produced ~3 blank lines between the bottom of the logo and the tagline. Inlined the reset on the last art line so the gap collapses to a single blank line. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/banner.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/banner.ts b/src/banner.ts index 267ed50..ee230eb 100644 --- a/src/banner.ts +++ b/src/banner.ts @@ -6,10 +6,7 @@ const BANNER_ART_COLORED = [ '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m \x1b[38;2;23;83;96m \x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m \x1b[38;2;32;92;99m█\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m \x1b[38;2;38;98;101m \x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m \x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m \x1b[38;2;57;119;108m \x1b[38;2;59;120;108m█\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m█\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m█\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m \x1b[38;2;79;142;116m \x1b[38;2;80;143;116m█\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m \x1b[38;2;86;149;118m \x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m \x1b[38;2;99;163;123m \x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m \x1b[38;2;106;170;125m \x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m░\x1b[39m', '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m█\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m█\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m█\x1b[38;2;29;89;98m█\x1b[38;2;30;90;98m█\x1b[38;2;32;92;99m \x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m█\x1b[38;2;36;96;100m█\x1b[38;2;38;98;101m█\x1b[38;2;39;99;101m█\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m█\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m█\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m█\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m█\x1b[38;2;56;117;107m█\x1b[38;2;57;119;108m█\x1b[38;2;59;120;108m \x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m█\x1b[38;2;63;125;110m█\x1b[38;2;65;126;110m█\x1b[38;2;66;128;111m█\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m \x1b[38;2;70;133;113m█\x1b[38;2;72;134;113m█\x1b[38;2;73;136;114m \x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m█\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m█\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m█\x1b[38;2;85;148;118m█\x1b[38;2;86;149;118m█\x1b[38;2;87;151;119m█\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m█\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m█\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m█\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m█\x1b[38;2;113;178;128m░\x1b[39m', '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m \x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m \x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m \x1b[38;2;29;89;98m \x1b[38;2;30;90;98m \x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m \x1b[38;2;36;96;100m \x1b[38;2;38;98;101m \x1b[38;2;39;99;101m \x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m \x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m \x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m \x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m \x1b[38;2;56;117;107m \x1b[38;2;57;119;108m \x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m \x1b[38;2;63;125;110m \x1b[38;2;65;126;110m \x1b[38;2;66;128;111m \x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m \x1b[38;2;72;134;113m \x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m \x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m \x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m \x1b[38;2;85;148;118m \x1b[38;2;86;149;118m \x1b[38;2;87;151;119m \x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m \x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m \x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m \x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m \x1b[38;2;113;178;128m░\x1b[39m', - '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m░\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m░\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m░\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m░\x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m░\x1b[38;2;36;96;100m░\x1b[38;2;38;98;101m░\x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m░\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m░\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m░\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m░\x1b[38;2;57;119;108m░\x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m░\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m░\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m░\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m░\x1b[38;2;85;148;118m░\x1b[38;2;86;149;118m░\x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m░\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m░\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m░\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m░\x1b[38;2;113;178;128m░\x1b[39m', - '', - '', - '\x1b[0m', + '\x1b[38;2;19;78;94m░\x1b[38;2;20;80;95m░\x1b[38;2;22;81;95m░\x1b[38;2;23;83;96m░\x1b[38;2;25;84;96m░\x1b[38;2;26;86;97m░\x1b[38;2;28;87;97m░\x1b[38;2;29;89;98m░\x1b[38;2;30;90;98m░\x1b[38;2;32;92;99m░\x1b[38;2;33;93;99m░\x1b[38;2;35;95;100m░\x1b[38;2;36;96;100m░\x1b[38;2;38;98;101m░\x1b[38;2;39;99;101m░\x1b[38;2;40;101;102m░\x1b[38;2;42;102;102m░\x1b[38;2;43;104;103m░\x1b[38;2;45;105;103m░\x1b[38;2;46;107;104m░\x1b[38;2;47;108;104m░\x1b[38;2;49;110;105m░\x1b[38;2;50;111;105m░\x1b[38;2;52;113;106m░\x1b[38;2;53;114;106m░\x1b[38;2;55;116;107m░\x1b[38;2;56;117;107m░\x1b[38;2;57;119;108m░\x1b[38;2;59;120;108m░\x1b[38;2;60;122;109m░\x1b[38;2;62;123;109m░\x1b[38;2;63;125;110m░\x1b[38;2;65;126;110m░\x1b[38;2;66;128;111m░\x1b[38;2;67;130;112m░\x1b[38;2;69;131;112m░\x1b[38;2;70;133;113m░\x1b[38;2;72;134;113m░\x1b[38;2;73;136;114m░\x1b[38;2;75;137;114m░\x1b[38;2;76;139;115m░\x1b[38;2;77;140;115m░\x1b[38;2;79;142;116m░\x1b[38;2;80;143;116m░\x1b[38;2;82;145;117m░\x1b[38;2;83;146;117m░\x1b[38;2;85;148;118m░\x1b[38;2;86;149;118m░\x1b[38;2;87;151;119m░\x1b[38;2;89;152;119m░\x1b[38;2;90;154;120m░\x1b[38;2;92;155;120m░\x1b[38;2;93;157;121m░\x1b[38;2;94;158;121m░\x1b[38;2;96;160;122m░\x1b[38;2;97;161;122m░\x1b[38;2;99;163;123m░\x1b[38;2;100;164;123m░\x1b[38;2;102;166;124m░\x1b[38;2;103;167;124m░\x1b[38;2;104;169;125m░\x1b[38;2;106;170;125m░\x1b[38;2;107;172;126m░\x1b[38;2;109;173;126m░\x1b[38;2;110;175;127m░\x1b[38;2;112;176;127m░\x1b[38;2;113;178;128m░\x1b[39m\x1b[0m', ].join('\n'); const BANNER_ART_PLAIN = [ From 926e4c7ab188d529dd5a67fa7e77808d35a34733 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Wed, 6 May 2026 05:08:13 -0700 Subject: [PATCH 4/5] feat(banner): restructure cheat sheet into audience-grouped sections Replaces the flat "Quick start" 5-item list with four role-grouped sections (Pay 402/MPP, Identity, Agents, Account management) plus an Output formats line, matching the layout pattern of related agent- payment CLIs in the ecosystem. Drops the redundant binary prefix from each line and surfaces dual-audience output flags (--json, --format toon, --mcp) at the bare invocation. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/banner.ts | 40 ++++++++++++++++++++++++-------- tests/banner.test.ts | 55 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/src/banner.ts b/src/banner.ts index ee230eb..1d9bf20 100644 --- a/src/banner.ts +++ b/src/banner.ts @@ -22,15 +22,35 @@ const BANNER_ART_PLAIN = [ const TAGLINE = 'Universal agent-payment CLI'; -const QUICK_START = [ - 'Quick start:', - ' agentscore-pay init Create encrypted wallet across base, solana, tempo', - ' agentscore-pay balance Check USDC across chains', - ' agentscore-pay pay POST Pay any 402 / MPP merchant', - ' agentscore-pay fund options List funding methods for a wallet', - ' agentscore-pay agent-guide Full guide for LLM agents', +const COMMANDS = [ + 'Pay any 402 / MPP endpoint:', + ' pay Send a paid HTTP request (multi-rail x402 + MPP)', + ' check Probe a URL for 402 and accepted rails (no money moves)', + ' balance USDC across base, solana, tempo', + ' discover Browse paid services in x402 Bazaar + MPP directory', + ' fund Show receive QR and poll for deposit', '', - 'Run `agentscore-pay --help` for the full command list.', + 'Identity (AgentScore Passport):', + ' passport Verify once with KYC; auto-attaches to gated merchants', + ' reputation Trust-score lookup for any wallet', + ' assess Compliance decision (policy gate + linked wallets)', + ' credentials Operator credentials for non-wallet agents', + '', + 'Agents (LLM tool-loop):', + ' --mcp Expose every command as MCP tools over stdio', + ' agent-guide Structured how-to-use for shell-tool agents', + ' skills add Sync skill files to Claude Code / Cursor / Amp', + '', + 'Account management:', + ' init First-run wallet setup (base + solana + tempo)', + ' wallet Manage encrypted keystore (create / import / export / list)', + ' limits Persistent local spending caps', + ' history Past payments', + '', + 'Output formats:', + ' --json JSON envelope', + ' --format toon TOON envelope (default)', + ' --help Full command list and flags', ].join('\n'); const MIN_BANNER_COLS = 70; @@ -49,10 +69,10 @@ export function renderBanner({ color: boolean; }): string { if (cols < MIN_BANNER_COLS) { - return `${PLAIN_BRAND}\n${TAGLINE}\n\n${QUICK_START}\n`; + return `${PLAIN_BRAND}\n${TAGLINE}\n\n${COMMANDS}\n`; } const art = color ? BANNER_ART_COLORED : BANNER_ART_PLAIN; - return `${art}\n\n${TAGLINE}\n\n${QUICK_START}\n`; + return `${art}\n\n${TAGLINE}\n\n${COMMANDS}\n`; } export function printBanner(): void { diff --git a/tests/banner.test.ts b/tests/banner.test.ts index a15c569..ca68f47 100644 --- a/tests/banner.test.ts +++ b/tests/banner.test.ts @@ -7,8 +7,6 @@ describe('banner', () => { expect(out).toContain('░'); expect(out).toContain('█'); expect(out).toContain('Universal agent-payment CLI'); - expect(out).toContain('Quick start:'); - expect(out).toContain('agentscore-pay init'); }); it('falls back to plain-text brand on narrow terminals', () => { @@ -16,7 +14,6 @@ describe('banner', () => { expect(out).not.toContain('░'); expect(out).toContain('AgentScore Pay'); expect(out).toContain('Universal agent-payment CLI'); - expect(out).toContain('Quick start:'); }); it('uses full brand prefix, never bare Pay or PAY.SH', () => { @@ -50,12 +47,52 @@ describe('banner', () => { } }); - it('quick-start references the binary, not the brand', () => { + it('groups commands by audience role', () => { const out = renderBanner({ cols: 100, color: false }); - expect(out).toContain('agentscore-pay init'); - expect(out).toContain('agentscore-pay balance'); - expect(out).toContain('agentscore-pay pay POST '); - expect(out).toContain('agentscore-pay fund options'); - expect(out).toContain('agentscore-pay agent-guide'); + expect(out).toContain('Pay any 402 / MPP endpoint:'); + expect(out).toContain('Identity (AgentScore Passport):'); + expect(out).toContain('Agents (LLM tool-loop):'); + expect(out).toContain('Account management:'); + expect(out).toContain('Output formats:'); + }); + + it('lists real commands without the binary prefix', () => { + const out = renderBanner({ cols: 100, color: false }); + for (const cmd of [ + 'pay', + 'check', + 'balance', + 'discover', + 'fund', + 'passport', + 'reputation', + 'assess', + 'credentials', + 'agent-guide', + 'skills add', + 'init', + 'wallet', + 'limits', + 'history', + ]) { + expect(out).toContain(cmd); + } + // Lines starting with the binary prefix would be the old format; new format omits it. + const lines = out.split('\n'); + const prefixedLines = lines.filter((l) => l.startsWith(' agentscore-pay ')); + expect(prefixedLines).toHaveLength(0); + }); + + it('surfaces dual-audience output flags', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).toContain('--json'); + expect(out).toContain('--format toon'); + expect(out).toContain('--help'); + expect(out).toContain('--mcp'); + }); + + it('does not reference the unbuilt `fund options` subcommand', () => { + const out = renderBanner({ cols: 100, color: false }); + expect(out).not.toContain('fund options'); }); }); From eea2510d4ba237f378564650c6ca99320c6bd171 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Wed, 6 May 2026 05:22:03 -0700 Subject: [PATCH 5/5] fix(banner): only list commands that work without an API key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops reputation, assess, credentials, associate-wallet, and sessions from the bare-invocation cheat sheet — those route through getClient() which requires AGENTSCORE_API_KEY in env. The bare invocation should work out-of-the-box for first-time users with no credentials yet. Folds passport into Account management (it's a one-time setup like init) so the lonely Identity section can collapse. Adds a regression test that asserts none of the API-key-only commands appear. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/banner.ts | 7 +------ tests/banner.test.ts | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/banner.ts b/src/banner.ts index 1d9bf20..78d51ac 100644 --- a/src/banner.ts +++ b/src/banner.ts @@ -30,12 +30,6 @@ const COMMANDS = [ ' discover Browse paid services in x402 Bazaar + MPP directory', ' fund Show receive QR and poll for deposit', '', - 'Identity (AgentScore Passport):', - ' passport Verify once with KYC; auto-attaches to gated merchants', - ' reputation Trust-score lookup for any wallet', - ' assess Compliance decision (policy gate + linked wallets)', - ' credentials Operator credentials for non-wallet agents', - '', 'Agents (LLM tool-loop):', ' --mcp Expose every command as MCP tools over stdio', ' agent-guide Structured how-to-use for shell-tool agents', @@ -43,6 +37,7 @@ const COMMANDS = [ '', 'Account management:', ' init First-run wallet setup (base + solana + tempo)', + ' passport Verify once with KYC; auto-attaches to gated merchants', ' wallet Manage encrypted keystore (create / import / export / list)', ' limits Persistent local spending caps', ' history Past payments', diff --git a/tests/banner.test.ts b/tests/banner.test.ts index ca68f47..28554f2 100644 --- a/tests/banner.test.ts +++ b/tests/banner.test.ts @@ -50,13 +50,12 @@ describe('banner', () => { it('groups commands by audience role', () => { const out = renderBanner({ cols: 100, color: false }); expect(out).toContain('Pay any 402 / MPP endpoint:'); - expect(out).toContain('Identity (AgentScore Passport):'); expect(out).toContain('Agents (LLM tool-loop):'); expect(out).toContain('Account management:'); expect(out).toContain('Output formats:'); }); - it('lists real commands without the binary prefix', () => { + it('lists real key-free commands without the binary prefix', () => { const out = renderBanner({ cols: 100, color: false }); for (const cmd of [ 'pay', @@ -65,9 +64,6 @@ describe('banner', () => { 'discover', 'fund', 'passport', - 'reputation', - 'assess', - 'credentials', 'agent-guide', 'skills add', 'init', @@ -83,6 +79,17 @@ describe('banner', () => { expect(prefixedLines).toHaveLength(0); }); + it('omits commands that require an API key', () => { + const out = renderBanner({ cols: 100, color: false }); + // These all route through getClient() and throw without AGENTSCORE_API_KEY. + // Bare invocation should only surface commands that work out-of-the-box. + for (const apiKeyOnly of ['reputation', 'assess', 'credentials', 'associate-wallet', 'sessions']) { + const lines = out.split('\n'); + const matches = lines.filter((l) => l.match(new RegExp(`^\\s+${apiKeyOnly}\\s`))); + expect(matches).toHaveLength(0); + } + }); + it('surfaces dual-audience output flags', () => { const out = renderBanner({ cols: 100, color: false }); expect(out).toContain('--json');