UNPKG

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
"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