chittycan
Version:
Your completely autonomous network that grows with you - DNA ownership platform with encrypted vaults, PDX portability, and ChittyFoundation governance
385 lines ⢠14.6 kB
JavaScript
/**
* Usage Analytics Dashboard
*
* Rich CLI dashboard showing:
* - Productivity patterns
* - Time saved metrics
* - Growth over time
* - CLI expertise levels
* - Workflow efficiency
*/
import chalk from "chalk";
import { loadUsageStats } from "./usage-tracker.js";
import { loadWorkflows } from "./custom-workflows.js";
/**
* Show comprehensive analytics dashboard
*/
export function showAnalyticsDashboard() {
const stats = loadUsageStats();
const workflows = loadWorkflows();
if (stats.commands.length === 0) {
console.log(chalk.yellow("\nš No usage data yet. Start using ChittyCan to see your analytics!\n"));
return;
}
const summary = calculateAnalyticsSummary(stats, workflows);
const insights = generateInsights(summary, stats);
const timePatterns = analyzeTimePatterns(stats);
// Header
console.log();
console.log(chalk.bold.cyan("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
console.log(chalk.bold.cyan(" š ChittyCan Analytics Dashboard"));
console.log(chalk.bold.cyan(" Your Journey to CLI Mastery"));
console.log(chalk.bold.cyan("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
console.log();
// Overview Section
showOverviewSection(summary);
// Productivity Section
showProductivitySection(summary, stats);
// Expertise Section
showExpertiseSection(summary);
// Time Patterns Section
showTimePatternsSection(timePatterns);
// Workflows Section
showWorkflowsSection(workflows);
// Insights Section
showInsightsSection(insights);
// Growth Visualization
showGrowthVisualization(stats);
console.log(chalk.bold.cyan("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
console.log();
}
/**
* Show overview section
*/
function showOverviewSection(summary) {
console.log(chalk.bold("š Overview"));
console.log();
const cols = [
{ label: "Total Commands", value: summary.totalCommands.toString(), icon: "šÆ" },
{ label: "Unique CLIs", value: summary.uniqueCLIs.toString(), icon: "š§" },
{ label: "Time Saved", value: `~${summary.timeSaved}min`, icon: "ā±ļø" },
{ label: "Productivity Score", value: summary.productivityScore.toString(), icon: "šÆ" }
];
// Display in 2x2 grid
for (let i = 0; i < cols.length; i += 2) {
const left = cols[i];
const right = cols[i + 1];
console.log(` ${left.icon} ${chalk.white(left.label.padEnd(20))} ${chalk.green.bold(left.value.padStart(10))} ` +
`${right.icon} ${chalk.white(right.label.padEnd(20))} ${chalk.green.bold(right.value.padStart(10))}`);
}
console.log();
// Streak
if (summary.streakDays > 0) {
console.log(` š„ ${chalk.yellow.bold(summary.streakDays)} day streak! Keep it up!`);
console.log();
}
}
/**
* Show productivity section
*/
function showProductivitySection(summary, stats) {
console.log(chalk.bold("šŖ Productivity"));
console.log();
const avgPerDay = summary.avgCommandsPerDay.toFixed(1);
console.log(` ${chalk.white("Average commands/day:")} ${chalk.green(avgPerDay)}`);
if (summary.growthRate > 0) {
console.log(` ${chalk.white("Growth rate:")} ${chalk.green("+" + summary.growthRate.toFixed(1) + "%")} š`);
}
else if (summary.growthRate < 0) {
console.log(` ${chalk.white("Growth rate:")} ${chalk.red(summary.growthRate.toFixed(1) + "%")} š`);
}
console.log(` ${chalk.white("Top CLI:")} ${chalk.cyan(summary.topCLI)}`);
// Success rate
const successful = stats.commands.filter((c) => c.successful).length;
const successRate = ((successful / stats.commands.length) * 100).toFixed(1);
console.log(` ${chalk.white("Success rate:")} ${chalk.green(successRate + "%")}`);
console.log();
}
/**
* Show expertise section
*/
function showExpertiseSection(summary) {
console.log(chalk.bold("š CLI Expertise"));
console.log();
const sorted = Object.values(summary.expertiseLevels)
.sort((a, b) => b.commandCount - a.commandCount)
.slice(0, 5);
for (const expertise of sorted) {
const levelColor = expertise.level === "expert" ? chalk.magenta :
expertise.level === "advanced" ? chalk.cyan :
expertise.level === "intermediate" ? chalk.blue :
chalk.gray;
const levelBadge = expertise.level === "expert" ? "š" :
expertise.level === "advanced" ? "š" :
expertise.level === "intermediate" ? "ā" :
"š";
console.log(` ${levelBadge} ${chalk.white(expertise.cli.padEnd(12))} ` +
`${levelColor(expertise.level.padEnd(15))} ` +
`${chalk.dim(expertise.commandCount + " commands")}`);
}
console.log();
}
/**
* Show time patterns section
*/
function showTimePatternsSection(patterns) {
console.log(chalk.bold("š Time Patterns"));
console.log();
// Group by time of day
const morning = patterns.filter(p => p.hour >= 6 && p.hour < 12);
const afternoon = patterns.filter(p => p.hour >= 12 && p.hour < 18);
const evening = patterns.filter(p => p.hour >= 18 && p.hour < 24);
const night = patterns.filter(p => p.hour >= 0 && p.hour < 6);
const timeGroups = [
{ name: "Morning", icon: "š
", patterns: morning },
{ name: "Afternoon", icon: "āļø", patterns: afternoon },
{ name: "Evening", icon: "š", patterns: evening },
{ name: "Night", icon: "š", patterns: night }
];
for (const group of timeGroups) {
const total = group.patterns.reduce((sum, p) => sum + p.commandCount, 0);
if (total > 0) {
const topCLI = group.patterns.sort((a, b) => b.commandCount - a.commandCount)[0]?.topCLI || "N/A";
console.log(` ${group.icon} ${chalk.white(group.name.padEnd(12))} ` +
`${chalk.green(total.toString().padStart(4))} commands ` +
`${chalk.dim("(top: " + topCLI + ")")}`);
}
}
console.log();
}
/**
* Show workflows section
*/
function showWorkflowsSection(workflows) {
console.log(chalk.bold("š§ Workflows"));
console.log();
if (workflows.length === 0) {
console.log(chalk.dim(" No workflows created yet. Run: can chitty workflows"));
}
else {
const sorted = workflows.sort((a, b) => b.usageCount - a.usageCount).slice(0, 3);
for (const wf of sorted) {
console.log(` ${chalk.cyan("can chitty " + wf.trigger.padEnd(20))} ` +
`${chalk.dim("used " + wf.usageCount + " times")}`);
}
if (workflows.length > 3) {
console.log(chalk.dim(` ... and ${workflows.length - 3} more`));
}
}
console.log();
}
/**
* Show insights section
*/
function showInsightsSection(insights) {
console.log(chalk.bold("š” Insights & Suggestions"));
console.log();
for (const insight of insights.slice(0, 3)) {
const icon = insight.type === "positive" ? "š" :
insight.type === "achievement" ? "š" :
"š";
console.log(` ${icon} ${chalk.white(insight.title)}`);
console.log(` ${chalk.dim(insight.message)}`);
if (insight.metric) {
console.log(` ${chalk.cyan(insight.metric)}`);
}
console.log();
}
}
/**
* Show growth visualization
*/
function showGrowthVisualization(stats) {
console.log(chalk.bold("š Command History (Last 7 Days)"));
console.log();
const last7Days = getLast7Days();
const commandsByDay = groupCommandsByDay(stats.commands, last7Days);
const maxCommands = Math.max(...Object.values(commandsByDay));
const maxBarWidth = 40;
for (const day of last7Days) {
const count = commandsByDay[day] || 0;
const barWidth = maxCommands > 0 ? Math.round((count / maxCommands) * maxBarWidth) : 0;
const bar = "ā".repeat(barWidth);
console.log(` ${chalk.dim(day.padEnd(10))} ${chalk.green(bar)} ${chalk.white(count)}`);
}
console.log();
}
/**
* Calculate analytics summary
*/
function calculateAnalyticsSummary(stats, workflows) {
const totalCommands = stats.commands.length;
const uniqueCLIs = Object.keys(stats.cliUsageCount).length;
// Estimate time saved (avg 2 min per command without ChittyCan)
const timeSaved = totalCommands * 2;
// Calculate avg commands per day
const firstCmd = stats.commands[0];
const lastCmd = stats.commands[stats.commands.length - 1];
const daysDiff = Math.max(1, Math.ceil((new Date(lastCmd.timestamp).getTime() - new Date(firstCmd.timestamp).getTime()) / (1000 * 60 * 60 * 24)));
const avgCommandsPerDay = totalCommands / daysDiff;
// Productivity score (0-100)
const productivityScore = Math.min(100, Math.round((uniqueCLIs * 10) + (avgCommandsPerDay * 5) + (workflows.length * 5)));
// Top CLI
const topCLI = Object.entries(stats.cliUsageCount)
.sort((a, b) => b[1] - a[1])[0]?.[0] || "N/A";
// Growth rate
const growthRate = calculateGrowthRate(stats);
// Streak
const streakDays = calculateStreak(stats.commands);
// Expertise levels
const expertiseLevels = calculateExpertiseLevels(stats);
return {
totalCommands,
uniqueCLIs,
timeSaved,
avgCommandsPerDay,
productivityScore,
topCLI,
growthRate,
streakDays,
expertiseLevels
};
}
/**
* Calculate expertise levels for each CLI
*/
function calculateExpertiseLevels(stats) {
const levels = {};
for (const [cli, count] of Object.entries(stats.cliUsageCount)) {
const cliCommands = stats.commands.filter((c) => c.cli === cli);
const uniquePatterns = new Set(cliCommands.map((c) => c.interpretedCommand)).size;
const lastUsed = cliCommands[cliCommands.length - 1]?.timestamp || "";
let level;
if (count >= 50 && uniquePatterns >= 15)
level = "expert";
else if (count >= 25 && uniquePatterns >= 10)
level = "advanced";
else if (count >= 10 && uniquePatterns >= 5)
level = "intermediate";
else
level = "beginner";
levels[cli] = {
cli,
level,
commandCount: count,
uniquePatterns,
lastUsed
};
}
return levels;
}
/**
* Calculate growth rate (% change over last period)
*/
function calculateGrowthRate(stats) {
if (stats.commands.length < 20)
return 0;
const midpoint = Math.floor(stats.commands.length / 2);
const firstHalf = stats.commands.slice(0, midpoint).length;
const secondHalf = stats.commands.slice(midpoint).length;
return ((secondHalf - firstHalf) / firstHalf) * 100;
}
/**
* Calculate current streak in days
*/
function calculateStreak(commands) {
if (commands.length === 0)
return 0;
let streak = 1;
let lastDate = new Date(commands[commands.length - 1].timestamp).toDateString();
for (let i = commands.length - 2; i >= 0; i--) {
const currentDate = new Date(commands[i].timestamp).toDateString();
if (currentDate !== lastDate) {
const dayDiff = Math.abs((new Date(currentDate).getTime() - new Date(lastDate).getTime()) / (1000 * 60 * 60 * 24));
if (dayDiff <= 1) {
streak++;
lastDate = currentDate;
}
else {
break;
}
}
}
return streak;
}
/**
* Analyze time patterns
*/
function analyzeTimePatterns(stats) {
const patterns = {};
for (const cmd of stats.commands) {
const hour = new Date(cmd.timestamp).getHours();
if (!patterns[hour]) {
patterns[hour] = { hour, commandCount: 0, topCLI: cmd.cli };
}
patterns[hour].commandCount++;
}
return Object.values(patterns);
}
/**
* Generate productivity insights
*/
function generateInsights(summary, stats) {
const insights = [];
// Achievement: First expert level
const experts = Object.values(summary.expertiseLevels).filter(e => e.level === "expert");
if (experts.length > 0) {
insights.push({
type: "achievement",
title: `Expert in ${experts[0].cli}!`,
message: `You've mastered ${experts[0].cli} with ${experts[0].commandCount} commands`,
metric: "š Achievement Unlocked"
});
}
// Positive: High success rate
const successRate = stats.commands.filter((c) => c.successful).length / stats.commands.length;
if (successRate > 0.95) {
insights.push({
type: "positive",
title: "Nearly Perfect!",
message: `${(successRate * 100).toFixed(1)}% of your commands succeed on first try`,
metric: "Keep up the great work!"
});
}
// Suggestion: Diversify
if (summary.uniqueCLIs < 3) {
insights.push({
type: "suggestion",
title: "Try More Tools",
message: "You're getting good at a few CLIs. Consider exploring more tools!",
metric: `Current: ${summary.uniqueCLIs} CLIs`
});
}
// Suggestion: Create workflows
const workflowCount = stats.commands.length / 10;
insights.push({
type: "suggestion",
title: "Automate Repetitive Tasks",
message: "Run 'can chitty suggestions' to see workflow recommendations"
});
return insights;
}
/**
* Get last 7 days as array
*/
function getLast7Days() {
const days = [];
for (let i = 6; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
days.push(date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
}
return days;
}
/**
* Group commands by day
*/
function groupCommandsByDay(commands, days) {
const grouped = {};
for (const cmd of commands) {
const day = new Date(cmd.timestamp).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
grouped[day] = (grouped[day] || 0) + 1;
}
return grouped;
}
//# sourceMappingURL=analytics-dashboard.js.map