dna-template-cli
Version:
DNA Template CLI v0.3.4 - Enhanced Commands Added (enhanced-create, enhanced-list, enhanced-validate)
371 lines (363 loc) • 13.8 kB
JavaScript
"use strict";
/**
* @fileoverview Progress Tracking Mock - Simplified implementation for CLI commands
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.progressTracker = exports.ProgressTracker = void 0;
const tslib_1 = require("tslib");
const fs = tslib_1.__importStar(require("fs-extra"));
class ProgressTracker {
constructor() {
this.session = null;
this.sessionFile = '.dna-current-session.json';
this.historyFile = '.dna-sessions.json';
}
async startSession(options) {
if (this.session) {
throw new Error('Session already active. End current session first.');
}
this.session = {
id: `session-${Date.now()}`,
startTime: new Date(),
epic: options.epic,
story: options.story,
type: options.type || 'feature',
filesModified: 0,
testsAdded: 0,
coverage: 0,
qualityGatesStatus: 'pending',
status: 'active',
notes: options.notes ? [options.notes] : [],
metrics: {
linesAdded: 0,
linesRemoved: 0,
filesCreated: 0,
filesDeleted: 0,
testCoverage: 0,
qualityScore: 0,
buildTime: 0,
errorCount: 0,
},
};
await this.saveSession();
await this.createFeatureBranch();
return this.session;
}
async updateProgress(updates) {
if (!this.session) {
throw new Error('No active session');
}
// Update session with new values
Object.assign(this.session, updates);
// Add notes if provided
if (updates.notes && Array.isArray(updates.notes)) {
this.session.notes.push(...updates.notes);
}
await this.saveSession();
}
async endSession(status = 'completed', notes) {
if (!this.session) {
throw new Error('No active session');
}
this.session.endTime = new Date();
this.session.status = status;
if (notes) {
this.session.notes.push(notes);
}
await this.saveSession();
// Generate report
const report = this.generateSessionReport();
// Save to history
await this.saveToHistory();
// Clear current session
this.session = null;
await this.clearCurrentSession();
return report;
}
async getStatus() {
if (!this.session) {
await this.loadSession();
}
return this.session;
}
async generateReport(format = 'json') {
if (!this.session) {
throw new Error('No active session');
}
const report = this.generateSessionReport();
if (format === 'markdown') {
const markdown = this.generateMarkdownReport(report);
await fs.writeFile('session-report.md', markdown);
}
else {
await fs.writeJson('session-report.json', report, { spaces: 2 });
}
return report;
}
async getSessionHistory(limit) {
try {
if (await fs.pathExists(this.historyFile)) {
const history = await fs.readJson(this.historyFile);
return limit ? history.slice(-limit) : history;
}
}
catch (error) {
// Return empty array if file doesn't exist or is corrupted
}
return [];
}
async getSessionAnalytics() {
if (!this.session)
return null;
return this.generateSessionAnalytics();
}
async pauseSession(reason) {
if (!this.session) {
throw new Error('No active session to pause');
}
const pauseNote = `Session paused${reason ? `: ${reason}` : ''} at ${new Date().toISOString()}`;
this.session.notes.push(pauseNote);
await this.saveSession();
}
async resumeSession(notes) {
if (!this.session) {
throw new Error('No session to resume');
}
const resumeNote = `Session resumed${notes ? `: ${notes}` : ''} at ${new Date().toISOString()}`;
this.session.notes.push(resumeNote);
await this.saveSession();
}
async addMilestone(milestone, details) {
if (!this.session) {
throw new Error('No active session');
}
const milestoneNote = `🎯 Milestone: ${milestone}${details ? ` - ${details}` : ''} (${new Date().toISOString()})`;
this.session.notes.push(milestoneNote);
await this.saveSession();
}
async setQualityGate(status, details) {
if (!this.session) {
throw new Error('No active session');
}
this.session.qualityGatesStatus = status;
const gateNote = `Quality gate ${status}${details ? `: ${details}` : ''} (${new Date().toISOString()})`;
this.session.notes.push(gateNote);
await this.saveSession();
}
generateSessionReport() {
if (!this.session) {
throw new Error('No active session');
}
const duration = this.session.endTime ?
this.session.endTime.getTime() - this.session.startTime.getTime() :
Date.now() - this.session.startTime.getTime();
const summary = {
productivity: this.calculateProductivity(),
quality: this.calculateQuality(),
coverage: this.session.coverage,
efficiency: this.calculateEfficiency(duration),
};
const recommendations = this.generateRecommendations(summary);
const analytics = this.generateSessionAnalytics();
return {
session: this.session,
duration,
summary,
recommendations,
analytics,
};
}
calculateProductivity() {
if (!this.session)
return 0;
const baseScore = Math.min(this.session.filesModified * 10, 100);
const testBonus = Math.min(this.session.testsAdded * 5, 20);
return Math.min(baseScore + testBonus, 100);
}
calculateQuality() {
if (!this.session)
return 0;
let score = 50; // Base score
if (this.session.qualityGatesStatus === 'passed')
score += 30;
if (this.session.coverage >= 80)
score += 20;
if (this.session.metrics.errorCount === 0)
score += 10;
return Math.min(score, 100);
}
calculateEfficiency(duration) {
if (!this.session)
return 0;
const hours = duration / (1000 * 60 * 60);
const filesPerHour = this.session.filesModified / hours;
// Efficiency based on files modified per hour
return Math.min(filesPerHour * 20, 100);
}
generateRecommendations(summary) {
const recommendations = [];
if (summary.coverage < 80) {
recommendations.push('Consider adding more tests to reach 80% coverage threshold');
}
if (summary.quality < 70) {
recommendations.push('Focus on quality gates and reduce technical debt');
}
if (summary.productivity < 50) {
recommendations.push('Break down tasks into smaller, more manageable pieces');
}
if (summary.efficiency < 30) {
recommendations.push('Consider using more automation tools and templates');
}
return recommendations;
}
generateSessionAnalytics() {
if (!this.session) {
throw new Error('No active session');
}
// Mock time distribution based on session activity
const timeDistribution = {
coding: 65, // 65% coding
testing: 20, // 20% testing
debugging: 10, // 10% debugging
documentation: 5 // 5% documentation
};
// Calculate velocity metrics
const duration = this.session.endTime ?
this.session.endTime.getTime() - this.session.startTime.getTime() :
Date.now() - this.session.startTime.getTime();
const hours = duration / (1000 * 60 * 60);
const averageFileTime = this.session.filesModified > 0 ? hours / this.session.filesModified : 0;
const testingRatio = this.session.filesModified > 0 ? this.session.testsAdded / this.session.filesModified : 0;
// Mock quality trend based on current quality score
let codeQualityTrend = 'stable';
const qualityScore = this.calculateQuality();
if (qualityScore >= 80)
codeQualityTrend = 'improving';
else if (qualityScore < 60)
codeQualityTrend = 'declining';
// Mock comparison to average (simulate user having historical data)
const comparisonToAverage = {
productivity: Math.random() * 40 - 20, // -20% to +20% difference
quality: Math.random() * 30 - 15, // -15% to +15% difference
efficiency: Math.random() * 50 - 25 // -25% to +25% difference
};
return {
timeDistribution,
velocityMetrics: {
averageFileTime: Math.round(averageFileTime * 100) / 100, // Round to 2 decimal places
testingRatio: Math.round(testingRatio * 100) / 100,
codeQualityTrend
},
comparisonToAverage
};
}
generateMarkdownReport(report) {
const session = report.session;
const duration = Math.round(report.duration / (1000 * 60)); // minutes
return `# Development Session Report
## Session Details
- **ID**: ${session.id}
- **Type**: ${session.type}
- **Epic**: ${session.epic || 'N/A'}
- **Story**: ${session.story || 'N/A'}
- **Duration**: ${duration} minutes
- **Status**: ${session.status}
## Metrics
- **Files Modified**: ${session.filesModified}
- **Tests Added**: ${session.testsAdded}
- **Coverage**: ${session.coverage}%
- **Quality Gates**: ${session.qualityGatesStatus}
## Summary Scores
- **Productivity**: ${report.summary.productivity.toFixed(1)}/100
- **Quality**: ${report.summary.quality.toFixed(1)}/100
- **Coverage**: ${report.summary.coverage.toFixed(1)}%
- **Efficiency**: ${report.summary.efficiency.toFixed(1)}/100
## Session Analytics
### Time Distribution
- **Coding**: ${report.analytics.timeDistribution.coding}%
- **Testing**: ${report.analytics.timeDistribution.testing}%
- **Debugging**: ${report.analytics.timeDistribution.debugging}%
- **Documentation**: ${report.analytics.timeDistribution.documentation}%
### Velocity Metrics
- **Average File Time**: ${report.analytics.velocityMetrics.averageFileTime} hours/file
- **Testing Ratio**: ${report.analytics.velocityMetrics.testingRatio} tests/file
- **Code Quality Trend**: ${report.analytics.velocityMetrics.codeQualityTrend}
### Comparison to Personal Average
- **Productivity**: ${report.analytics.comparisonToAverage.productivity > 0 ? '+' : ''}${report.analytics.comparisonToAverage.productivity.toFixed(1)}%
- **Quality**: ${report.analytics.comparisonToAverage.quality > 0 ? '+' : ''}${report.analytics.comparisonToAverage.quality.toFixed(1)}%
- **Efficiency**: ${report.analytics.comparisonToAverage.efficiency > 0 ? '+' : ''}${report.analytics.comparisonToAverage.efficiency.toFixed(1)}%
## Recommendations
${report.recommendations.map(rec => `- ${rec}`).join('\n')}
## Notes
${session.notes.map(note => `- ${note}`).join('\n')}
`;
}
async saveSession() {
if (!this.session)
return;
await fs.writeJson(this.sessionFile, this.session, { spaces: 2 });
}
async loadSession() {
try {
if (await fs.pathExists(this.sessionFile)) {
this.session = await fs.readJson(this.sessionFile);
// Convert date strings back to Date objects
if (this.session) {
this.session.startTime = new Date(this.session.startTime);
if (this.session.endTime) {
this.session.endTime = new Date(this.session.endTime);
}
}
}
}
catch (error) {
// Ignore errors, no active session
}
}
async saveToHistory() {
if (!this.session)
return;
let history = [];
try {
if (await fs.pathExists(this.historyFile)) {
history = await fs.readJson(this.historyFile);
}
}
catch (error) {
// Start with empty history if file is corrupted
}
history.push(this.session);
await fs.writeJson(this.historyFile, history, { spaces: 2 });
}
async clearCurrentSession() {
try {
if (await fs.pathExists(this.sessionFile)) {
await fs.remove(this.sessionFile);
}
}
catch (error) {
// Ignore errors
}
}
async createFeatureBranch() {
if (!this.session || !this.session.epic)
return;
try {
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
let branchName = `feature/${this.session.epic}`;
if (this.session.story) {
branchName += `/${this.session.story}`;
}
await execAsync(`git checkout -b ${branchName}`);
}
catch (error) {
// Ignore git errors - branch might already exist or not in git repo
}
}
}
exports.ProgressTracker = ProgressTracker;
// Singleton instance
exports.progressTracker = new ProgressTracker();
//# sourceMappingURL=progress-tracking.js.map