trace.ai-cli
Version:
A powerful AI-powered CLI tool
286 lines (247 loc) • 8.99 kB
JavaScript
const chalk = require('chalk');
const gradientModule = require('gradient-string');
const { createSpinner } = require('nanospinner');
const gradient = gradientModule.default || gradientModule;
const STYLED_OUTPUT = Boolean(process.stdout && process.stdout.isTTY);
const INTERACTIVE_OUTPUT = STYLED_OUTPUT && Boolean(process.stdin && process.stdin.isTTY);
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const tone = (formatter) => (value) => {
const text = String(value);
return STYLED_OUTPUT ? formatter(text) : text;
};
const applyGradient = (renderer, text) => {
const value = String(text);
return STYLED_OUTPUT ? renderer(value) : value;
};
const applyGradientMultiline = (renderer, text) => {
const value = String(text);
return STYLED_OUTPUT ? renderer.multiline(value) : value;
};
const palette = {
text: tone(chalk.hex('#E7EEF7')),
muted: tone(chalk.hex('#7E8A9A')),
subtle: tone(chalk.hex('#A7B4C2')),
border: tone(chalk.hex('#4E4A86')),
accent: tone(chalk.hex('#FF5FD2')),
accentSoft: tone(chalk.hex('#8DA2FF')),
success: tone(chalk.hex('#3DDB9A')),
warning: tone(chalk.hex('#FFBF69')),
danger: tone(chalk.hex('#FF7B72')),
focusBg: tone(chalk.bgHex('#261C4D').hex('#F8F5FF')),
chipBg: tone(chalk.bgHex('#1C1A45').hex('#B3C0FF')),
command: tone(chalk.hex('#C793FF').bold),
heading: tone(chalk.hex('#9EB6FF').bold),
label: tone(chalk.hex('#C1B6F8')),
};
const brandGradient = gradient(['#FF5FD2', '#6EA8FF', '#8B5CFF']);
const accentGradient = gradient(['#F472E6', '#7AA2FF', '#A78BFA']);
function divider(width = 72) {
return palette.border('-'.repeat(Math.max(24, Math.min(width, 88))));
}
function highlight(text) {
return palette.focusBg(` ${text} `);
}
function commandRow(cmd, desc, width = 25) {
return ` ${palette.command(cmd.padEnd(width))} ${palette.muted(desc)}`;
}
function sectionHeader(title, width = 58) {
return `${palette.heading(title.toUpperCase())}\n${divider(width)}`;
}
function infoLine(label, value, valueTone = palette.text) {
return `${palette.label(`${label}:`)} ${valueTone(value)}`;
}
function prompt(activeCount, totalCount) {
const context = activeCount > 0 ? palette.chipBg(` ${activeCount}/${totalCount} ctx `) : '';
const spacer = context ? ' ' : '';
return `${applyGradient(brandGradient, 'Trace.AI')}${spacer}${context} ${palette.border('>')} `;
}
function promptPlain(activeCount, totalCount) {
const context = activeCount > 0 ? `[${activeCount}/${totalCount} ctx] ` : '';
return `Trace.AI ${context}> `;
}
function hero(asciiArt, version) {
const lines = [
applyGradientMultiline(brandGradient, asciiArt),
palette.heading('AI powered CLI Platform'),
palette.muted(`v${version} | Powered by Mixkey`),
divider(process.stdout.columns || 72),
];
return lines.join('\n');
}
function welcomeBlock(commands) {
const lines = [
sectionHeader('Quick Start', 54),
`${palette.accent('1.')} ${palette.text('Run ')}${palette.command('/help')}${palette.text(' to inspect the full command surface.')}`,
`${palette.accent('2.')} ${palette.text('Add files or folders as memory before asking targeted questions.')}`,
`${palette.accent('3.')} ${palette.text('Use clear, task-shaped prompts for better agent execution.')}`,
'',
sectionHeader('Available Commands', 54),
...commands.map(({ cmd, desc }) => commandRow(cmd, desc)),
divider(54),
];
return lines.join('\n');
}
function statusBlock({ userName, modeName, contextCount, activeCount }) {
const lines = [
'',
sectionHeader('Session', 54),
`${palette.label('User')} ${palette.text(userName)} ${palette.border('|')} ${palette.label('Mode')} ${palette.accentSoft(modeName)} ${palette.border('|')} ${palette.label('Memories')} ${palette.text(String(contextCount))} ${palette.border('|')} ${palette.label('Active')} ${palette.text(String(activeCount))}`,
divider(54),
];
return lines.join('\n');
}
function memoriesBlock(contexts, activeMemories) {
const lines = [];
if (contexts.length === 0) {
lines.push('', highlight('NO MEMORY'), palette.muted('Add a file, folder, or quoted text to build reusable context.'));
} else {
lines.push('', sectionHeader('Memories', 54));
contexts.forEach((ctx, index) => {
const num = index + 1;
const isActive = activeMemories.has(num);
const preview = ctx.content.substring(0, 80).replace(/\n/g, ' ');
const truncated = ctx.content.length > 80 ? '...' : '';
lines.push(`${isActive ? palette.success('[active]') : palette.border('[idle]')} ${palette.command(`[${num}]`)} ${palette.muted(ctx.timestamp)}`);
lines.push(` ${palette.text(preview)}${palette.muted(truncated)}`);
if (index < contexts.length - 1) lines.push('');
});
lines.push(divider(54));
}
return lines.join('\n');
}
function statsBlock({ minutes, seconds, queryCount, contextCount, width, startedAt }) {
const lines = [
'',
sectionHeader('Session Statistics', 42),
infoLine('Session Duration', `${minutes}m ${seconds}s`),
infoLine('Total Queries', String(queryCount)),
infoLine('Active Memories', String(contextCount)),
infoLine('Terminal Width', String(width)),
infoLine('Started', startedAt),
divider(42),
];
return lines.join('\n');
}
function resultHeader(title, filePath) {
const lines = [
'',
applyGradient(accentGradient, title.toUpperCase()),
filePath ? palette.muted(`Source: ${filePath}`) : null,
divider(60),
].filter(Boolean);
return lines.join('\n');
}
function resultFooter() {
return `${divider(60)}\n`;
}
function errorBlock(message) {
return `\n${palette.danger('Error:')} ${palette.text(message)}\n`;
}
function helpBlock(commands, title = 'All Available Commands', width = 60) {
const lines = [
'',
sectionHeader(title, width),
...commands.map(({ cmd, desc }) => commandRow(cmd, desc)),
divider(width),
];
return lines.join('\n');
}
function dropdownHint(text) {
return palette.muted(text);
}
function dropdownGroup(label) {
return palette.heading(` ${label}/`);
}
function dropdownFileItem(name, selected, checked) {
const prefix = selected ? palette.accent(' > ') : ' ';
const check = checked ? palette.success('[x]') : palette.border('[ ]');
const label = selected ? highlight(name) : palette.text(name);
return `${prefix}${check} ${label}`;
}
function dropdownAgentItem(name, desc, selected) {
const prefix = selected ? palette.accent(' > ') : ' ';
const label = selected ? highlight(name) : palette.text(name);
return `${prefix}${label} ${palette.muted(desc)}`;
}
function dropdownCommandItem(cmd, desc, selected, width = 20) {
const prefix = selected ? palette.accent(' ') : ' ';
const line = `${cmd.padEnd(width)} ${desc}`;
return `${prefix}${selected ? highlight(line) : palette.muted(line)}`;
}
async function playBootSequence(messages) {
if (messages.length === 0) {
return;
}
if (!INTERACTIVE_OUTPUT) {
process.stdout.write(`${palette.success('Ready')} ${palette.text('Trace.AI initialized successfully')}\n`);
return;
}
const spinner = createSpinner(palette.text(messages[0])).start();
for (let index = 1; index < messages.length; index++) {
await wait(360);
spinner.update({ text: palette.text(messages[index]) });
}
await wait(240);
spinner.success({ text: `${palette.success('Ready')} ${palette.text('Trace.AI initialized successfully')}` });
}
async function playTaglineAnimation(text, duration = 700) {
if (!INTERACTIVE_OUTPUT) {
process.stdout.write(`${text}\n`);
return;
}
await wait(Math.min(duration, 180));
process.stdout.write(`${palette.heading(text)}\n`);
}
function startSpinner(message) {
if (!INTERACTIVE_OUTPUT) {
return {
success: ({ text }) => process.stdout.write(`${text}\n`),
error: ({ text }) => process.stdout.write(`${text}\n`),
};
}
return createSpinner(palette.text(message)).start();
}
function stopSpinnerSuccess(spinner, message) {
spinner.success({ text: `${palette.text(message)} ${palette.muted('- complete')}` });
}
function stopSpinnerError(spinner, message) {
spinner.error({ text: `${palette.text(message)} ${palette.muted('- failed')}` });
}
function farewell(asciiArt, summary) {
const lines = [
'',
applyGradientMultiline(accentGradient, asciiArt),
divider(54),
palette.heading('Session Closed'),
...summary.map((line) => palette.muted(line)),
divider(54),
palette.accent('traceai.netlify.app'),
];
return lines.join('\n');
}
module.exports = {
palette,
divider,
prompt,
promptPlain,
hero,
welcomeBlock,
statusBlock,
memoriesBlock,
statsBlock,
resultHeader,
resultFooter,
errorBlock,
helpBlock,
dropdownHint,
dropdownGroup,
dropdownFileItem,
dropdownAgentItem,
dropdownCommandItem,
playBootSequence,
playTaglineAnimation,
startSpinner,
stopSpinnerSuccess,
stopSpinnerError,
farewell,
};