@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
97 lines (96 loc) • 3.81 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import chalk from "chalk";
import { AnalyticsService } from "../../features/analytics/core/analytics-service.js";
async function displayAnalyticsDashboard(projectPath) {
const service = new AnalyticsService(projectPath || process.cwd());
try {
await service.syncFromTaskStore();
const state = await service.getDashboardState();
const { metrics, recentTasks, teamMetrics } = state;
console.clear();
console.log(chalk.bold.cyan("\nStackMemory Analytics Dashboard\n"));
console.log(chalk.gray("\u2500".repeat(50)));
console.log(chalk.bold.white("\nKey Metrics\n"));
const metricsDisplay = [
["Total Tasks", metrics.totalTasks],
["Completed", chalk.green(metrics.completedTasks)],
["In Progress", chalk.yellow(metrics.inProgressTasks)],
["Blocked", chalk.red(metrics.blockedTasks)],
["Completion Rate", `${metrics.completionRate.toFixed(1)}%`],
["Avg Time to Complete", formatDuration(metrics.averageTimeToComplete)],
["Effort Accuracy", `${metrics.effortAccuracy.toFixed(0)}%`],
["Blocking Issues", metrics.blockingIssuesCount]
];
metricsDisplay.forEach(([label, value]) => {
console.log(` ${chalk.gray(String(label).padEnd(20))} ${value}`);
});
if (metrics.velocityTrend.length > 0) {
console.log(chalk.bold.white("\n\u{1F4C9} Velocity Trend (last 7 days)\n"));
const maxVelocity = Math.max(...metrics.velocityTrend);
const scale = maxVelocity > 0 ? 10 / maxVelocity : 1;
metrics.velocityTrend.slice(-7).forEach((velocity, i) => {
const bar = "\u2588".repeat(Math.round(velocity * scale));
const day = /* @__PURE__ */ new Date();
day.setDate(day.getDate() - (6 - i));
console.log(
` ${day.toLocaleDateString("en", { weekday: "short" }).padEnd(4)} ${bar} ${velocity}`
);
});
}
if (recentTasks.length > 0) {
console.log(chalk.bold.white("\nRecent Tasks\n"));
recentTasks.slice(0, 5).forEach((task) => {
const statePrefix = {
completed: "[DONE]",
in_progress: "[PROG]",
blocked: "[BLCK]",
todo: "[TODO]"
}[task.state];
const priorityColor = {
urgent: chalk.red,
high: chalk.yellow,
medium: chalk.blue,
low: chalk.gray
}[task.priority];
console.log(
` ${statePrefix} ${priorityColor(`[${task.priority.toUpperCase()}]`)} ${task.title.slice(0, 50)}`
);
});
}
if (teamMetrics.length > 0) {
console.log(chalk.bold.white("\n\u{1F465} Team Performance\n"));
teamMetrics.slice(0, 3).forEach((member) => {
const bar = "\u2593".repeat(Math.round(member.contributionPercentage / 10));
console.log(
` ${member.userName.padEnd(15)} ${bar} ${member.contributionPercentage.toFixed(0)}% (${member.individualMetrics.completedTasks} tasks)`
);
});
}
console.log(chalk.gray("\n" + "\u2500".repeat(50)));
console.log(
chalk.gray(`Last updated: ${state.lastUpdated.toLocaleString()}`)
);
console.log();
} finally {
service.close();
}
}
function formatDuration(ms) {
if (ms === 0) return "N/A";
const hours = Math.floor(ms / 36e5);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}d ${hours % 24}h`;
if (hours > 0) return `${hours}h`;
return "<1h";
}
if (import.meta.url === `file://${process.argv[1]}`) {
displayAnalyticsDashboard().catch(console.error);
}
export {
displayAnalyticsDashboard
};
//# sourceMappingURL=viewer.js.map