UNPKG

dna-template-cli

Version:

DNA Template CLI v0.3.4 - Enhanced Commands Added (enhanced-create, enhanced-list, enhanced-validate)

429 lines (428 loc) 20.5 kB
"use strict"; /** * @fileoverview Progress Tracking Command for Development Sessions */ Object.defineProperty(exports, "__esModule", { value: true }); exports.trackCommand = void 0; const tslib_1 = require("tslib"); const commander_1 = require("commander"); const fs_extra_1 = tslib_1.__importDefault(require("fs-extra")); const path_1 = tslib_1.__importDefault(require("path")); const chalk_compat_1 = tslib_1.__importDefault(require("../utils/chalk-compat")); const enhanced_logger_1 = require("../utils/enhanced-logger"); const workspace_adapter_1 = require("../adapters/workspace-adapter"); const SESSIONS_FILE = path_1.default.join(process.cwd(), '.dna-sessions.json'); const CURRENT_SESSION_FILE = path_1.default.join(process.cwd(), '.dna-current-session.json'); // Initialize Git automation system const gitAutomation = new workspace_adapter_1.GitAutomationSystem({ autoCommit: true, pushRemote: false, requireTests: false, conventionalCommits: true }); exports.trackCommand = new commander_1.Command('track') .description('Track development session progress') .addCommand(new commander_1.Command('start') .description('Start a new tracking session') .option('-t, --type <type>', 'session type (feature, bugfix, refactor, testing, verification)', 'feature') .option('-e, --epic <epic>', 'epic identifier') .option('-s, --story <story>', 'story identifier') .option('-n, --notes <notes>', 'initial session notes') .action(startSession)) .addCommand(new commander_1.Command('progress') .description('Update session progress') .option('-f, --files-modified <count>', 'number of files modified', parseInt) .option('-t, --tests-added <count>', 'number of tests added', parseInt) .option('--tests-fixed <count>', 'number of tests fixed', parseInt) .option('--quality-gates-status <status>', 'quality gates status (passed/failed/partial)') .option('-c, --coverage <percentage>', 'current test coverage', parseFloat) .option('-n, --notes <notes>', 'progress notes') .action(updateProgress)) .addCommand(new commander_1.Command('end') .description('End current tracking session') .option('--status <status>', 'final session status (completed/failed)', 'completed') .option('--quality-gates-status <status>', 'final quality gates status') .option('-n, --notes <notes>', 'final session notes') .action(endSession)) .addCommand(new commander_1.Command('status') .description('Show current session status') .action(showStatus)) .addCommand(new commander_1.Command('history') .description('Show session history') .option('-l, --limit <count>', 'number of sessions to show', parseInt, 10) .option('--epic <epic>', 'filter by epic') .option('--story <story>', 'filter by story') .action(showHistory)) .addCommand(new commander_1.Command('report') .description('Generate session report') .option('-o, --output <file>', 'output file path') .option('--format <format>', 'report format (json, md, html)', 'md') .action(generateReport)); async function startSession(options) { try { // Check if there's already an active session const currentSession = await getCurrentSession(); if (currentSession) { enhanced_logger_1.enhancedLogger.warn(`Active session found: ${currentSession.id}`); enhanced_logger_1.enhancedLogger.info('Please end the current session before starting a new one'); return; } const session = { id: generateSessionId(), type: options.type, epic: options.epic, story: options.story, startTime: new Date().toISOString(), status: 'active', progress: { filesModified: 0, testsAdded: 0, testsFixed: 0, qualityGatesPassed: 0, qualityGatesFailed: 0 }, metrics: { codeLines: 0, testLines: 0, coverage: 0, performance: {} }, notes: options.notes ? [options.notes] : [] }; await saveCurrentSession(session); await addToSessionHistory(session); // Auto-create feature branch if epic and story are provided if (session.epic && session.story) { try { const branchResult = await gitAutomation.createFeatureBranch(session.story, session.epic); if (branchResult.success && branchResult.branchName) { session.notes.push(`${new Date().toISOString()}: Auto-created branch: ${branchResult.branchName}`); await saveCurrentSession(session); enhanced_logger_1.enhancedLogger.success(`${enhanced_logger_1.ICONS.branch} Created feature branch: ${chalk_compat_1.default.cyan(branchResult.branchName)}`); } else { enhanced_logger_1.enhancedLogger.debug(`Failed to create feature branch: ${branchResult.error}`); } } catch (error) { enhanced_logger_1.enhancedLogger.debug(`Failed to create feature branch: ${error instanceof Error ? error.message : 'Unknown error'}`); } } enhanced_logger_1.enhancedLogger.success(`${enhanced_logger_1.ICONS.rocket} Started tracking session: ${session.id}`); enhanced_logger_1.enhancedLogger.info(`Type: ${chalk_compat_1.default.cyan(session.type)}`); if (session.epic) enhanced_logger_1.enhancedLogger.info(`Epic: ${chalk_compat_1.default.cyan(session.epic)}`); if (session.story) enhanced_logger_1.enhancedLogger.info(`Story: ${chalk_compat_1.default.cyan(session.story)}`); enhanced_logger_1.enhancedLogger.info(`Started: ${chalk_compat_1.default.gray(new Date(session.startTime).toLocaleString())}`); } catch (error) { enhanced_logger_1.enhancedLogger.error(`Failed to start session: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async function updateProgress(options) { try { const session = await getCurrentSession(); if (!session) { enhanced_logger_1.enhancedLogger.error('No active session found. Start a session with: dna-cli track start'); return; } // Update progress if (options.filesModified !== undefined) { session.progress.filesModified += options.filesModified; } if (options.testsAdded !== undefined) { session.progress.testsAdded += options.testsAdded; } if (options.testsFixed !== undefined) { session.progress.testsFixed += options.testsFixed; } if (options.qualityGatesStatus) { if (options.qualityGatesStatus === 'passed') { session.progress.qualityGatesPassed++; } else if (options.qualityGatesStatus === 'failed') { session.progress.qualityGatesFailed++; } } if (options.coverage !== undefined) { session.metrics.coverage = options.coverage; } if (options.notes) { session.notes.push(`${new Date().toISOString()}: ${options.notes}`); } await saveCurrentSession(session); // Trigger auto-commit if configured try { const message = `chore: update session progress - ${session.progress.filesModified} files, ${session.progress.testsAdded} tests`; const commitResult = await gitAutomation.commit(message, { all: true }); if (!commitResult.success) { enhanced_logger_1.enhancedLogger.debug(`Git auto-commit failed: ${commitResult.error}`); } } catch (error) { enhanced_logger_1.enhancedLogger.debug(`Git auto-commit failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } enhanced_logger_1.enhancedLogger.success(`${enhanced_logger_1.ICONS.check} Progress updated`); displaySessionProgress(session); } catch (error) { enhanced_logger_1.enhancedLogger.error(`Failed to update progress: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async function endSession(options) { try { const session = await getCurrentSession(); if (!session) { enhanced_logger_1.enhancedLogger.error('No active session found'); return; } session.endTime = new Date().toISOString(); session.duration = Date.now() - new Date(session.startTime).getTime(); session.status = options.status === 'failed' ? 'failed' : 'completed'; if (options.qualityGatesStatus) { if (options.qualityGatesStatus === 'passed') { session.progress.qualityGatesPassed++; } else if (options.qualityGatesStatus === 'failed') { session.progress.qualityGatesFailed++; } } if (options.notes) { session.notes.push(`${new Date().toISOString()}: ${options.notes}`); } // Trigger feature completion commit if session completed successfully if (session.status === 'completed') { try { const message = `feat: complete ${session.type} ${session.epic ? session.epic + '/' : ''}${session.story || session.id} - Files modified: ${session.progress.filesModified} - Tests added: ${session.progress.testsAdded} - Coverage: ${session.metrics.coverage}% - Quality gates passed: ${session.progress.qualityGatesPassed}`; const commitResult = await gitAutomation.commit(message, { all: true }); if (!commitResult.success) { enhanced_logger_1.enhancedLogger.debug(`Git feature completion commit failed: ${commitResult.error}`); } } catch (error) { enhanced_logger_1.enhancedLogger.debug(`Git feature completion commit failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Update session in history await updateSessionInHistory(session); // Clear current session await fs_extra_1.default.remove(CURRENT_SESSION_FILE); enhanced_logger_1.enhancedLogger.success(`${enhanced_logger_1.ICONS.check} Session completed: ${session.id}`); displaySessionSummary(session); } catch (error) { enhanced_logger_1.enhancedLogger.error(`Failed to end session: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async function showStatus() { try { const session = await getCurrentSession(); if (!session) { enhanced_logger_1.enhancedLogger.info('No active session'); return; } enhanced_logger_1.enhancedLogger.box([ chalk_compat_1.default.bold.cyan('Current Session Status'), '', `${chalk_compat_1.default.bold('Session:')} ${session.id}`, `${chalk_compat_1.default.bold('Type:')} ${session.type}`, session.epic ? `${chalk_compat_1.default.bold('Epic:')} ${session.epic}` : '', session.story ? `${chalk_compat_1.default.bold('Story:')} ${session.story}` : '', `${chalk_compat_1.default.bold('Started:')} ${new Date(session.startTime).toLocaleString()}`, `${chalk_compat_1.default.bold('Duration:')} ${formatDuration(Date.now() - new Date(session.startTime).getTime())}`, '', chalk_compat_1.default.bold('Progress:'), ` Files modified: ${session.progress.filesModified}`, ` Tests added: ${session.progress.testsAdded}`, ` Tests fixed: ${session.progress.testsFixed}`, ` Quality gates passed: ${session.progress.qualityGatesPassed}`, ` Quality gates failed: ${session.progress.qualityGatesFailed}`, '', chalk_compat_1.default.bold('Metrics:'), ` Coverage: ${session.metrics.coverage}%`, ` Code lines: ${session.metrics.codeLines}`, ` Test lines: ${session.metrics.testLines}`, '', session.notes.length > 0 ? chalk_compat_1.default.bold('Recent Notes:') : '', ...session.notes.slice(-3).map(note => ` ${chalk_compat_1.default.gray(note)}`) ].filter(line => line !== ''), { borderColor: 'cyan', borderStyle: 'round' }); } catch (error) { enhanced_logger_1.enhancedLogger.error(`Failed to show status: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async function showHistory(options) { try { const sessions = await getSessionHistory(); let filteredSessions = sessions; // Apply filters if (options.epic) { filteredSessions = filteredSessions.filter(s => s.epic === options.epic); } if (options.story) { filteredSessions = filteredSessions.filter(s => s.story === options.story); } // Limit results filteredSessions = filteredSessions.slice(0, options.limit); if (filteredSessions.length === 0) { enhanced_logger_1.enhancedLogger.info('No sessions found'); return; } enhanced_logger_1.enhancedLogger.info(`${chalk_compat_1.default.bold('Session History')} (${filteredSessions.length} sessions)`); enhanced_logger_1.enhancedLogger.newline(); for (const session of filteredSessions) { const statusIcon = session.status === 'completed' ? chalk_compat_1.default.green(enhanced_logger_1.ICONS.check) : session.status === 'failed' ? chalk_compat_1.default.red(enhanced_logger_1.ICONS.cross) : chalk_compat_1.default.yellow(enhanced_logger_1.ICONS.clock); const durationText = session.duration ? formatDuration(session.duration) : 'In progress'; enhanced_logger_1.enhancedLogger.info(`${statusIcon} ${chalk_compat_1.default.bold(session.id)} (${session.type})`); enhanced_logger_1.enhancedLogger.info(` ${chalk_compat_1.default.gray(session.epic || 'No epic')}${chalk_compat_1.default.gray(session.story || 'No story')}`); enhanced_logger_1.enhancedLogger.info(` ${chalk_compat_1.default.gray(new Date(session.startTime).toLocaleDateString())} - ${chalk_compat_1.default.gray(durationText)}`); enhanced_logger_1.enhancedLogger.info(` Files: ${session.progress.filesModified}, Tests: ${session.progress.testsAdded}, Coverage: ${session.metrics.coverage}%`); enhanced_logger_1.enhancedLogger.newline(); } } catch (error) { enhanced_logger_1.enhancedLogger.error(`Failed to show history: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async function generateReport(options) { try { const sessions = await getSessionHistory(); if (sessions.length === 0) { enhanced_logger_1.enhancedLogger.info('No sessions to report'); return; } const report = generateSessionReport(sessions, options.format); if (options.output) { await fs_extra_1.default.writeFile(options.output, report); enhanced_logger_1.enhancedLogger.success(`Report saved to: ${options.output}`); } else { console.log(report); } } catch (error) { enhanced_logger_1.enhancedLogger.error(`Failed to generate report: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Helper functions async function getCurrentSession() { try { if (await fs_extra_1.default.pathExists(CURRENT_SESSION_FILE)) { return await fs_extra_1.default.readJSON(CURRENT_SESSION_FILE); } } catch (error) { // File doesn't exist or is corrupted } return null; } async function saveCurrentSession(session) { await fs_extra_1.default.writeJSON(CURRENT_SESSION_FILE, session, { spaces: 2 }); } async function getSessionHistory() { try { if (await fs_extra_1.default.pathExists(SESSIONS_FILE)) { return await fs_extra_1.default.readJSON(SESSIONS_FILE); } } catch (error) { // File doesn't exist or is corrupted } return []; } async function addToSessionHistory(session) { const sessions = await getSessionHistory(); sessions.push(session); await fs_extra_1.default.writeJSON(SESSIONS_FILE, sessions, { spaces: 2 }); } async function updateSessionInHistory(updatedSession) { const sessions = await getSessionHistory(); const index = sessions.findIndex(s => s.id === updatedSession.id); if (index >= 0) { sessions[index] = updatedSession; await fs_extra_1.default.writeJSON(SESSIONS_FILE, sessions, { spaces: 2 }); } } function generateSessionId() { const now = new Date(); return `${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}-${now.getHours().toString().padStart(2, '0')}${now.getMinutes().toString().padStart(2, '0')}`; } function formatDuration(ms) { const hours = Math.floor(ms / 3600000); const minutes = Math.floor((ms % 3600000) / 60000); const seconds = Math.floor((ms % 60000) / 1000); if (hours > 0) { return `${hours}h ${minutes}m`; } else if (minutes > 0) { return `${minutes}m ${seconds}s`; } else { return `${seconds}s`; } } function displaySessionProgress(session) { const progress = [ `Files: ${session.progress.filesModified}`, `Tests: ${session.progress.testsAdded}`, `Coverage: ${session.metrics.coverage}%`, `Quality Gates: ${session.progress.qualityGatesPassed}/${session.progress.qualityGatesPassed + session.progress.qualityGatesFailed}` ]; enhanced_logger_1.enhancedLogger.info(`Progress: ${progress.join(' | ')}`); } function displaySessionSummary(session) { const summary = [ chalk_compat_1.default.bold('Session Summary'), '', `Duration: ${session.duration ? formatDuration(session.duration) : 'Unknown'}`, `Files Modified: ${session.progress.filesModified}`, `Tests Added: ${session.progress.testsAdded}`, `Tests Fixed: ${session.progress.testsFixed}`, `Quality Gates Passed: ${session.progress.qualityGatesPassed}`, `Quality Gates Failed: ${session.progress.qualityGatesFailed}`, `Final Coverage: ${session.metrics.coverage}%`, `Status: ${session.status === 'completed' ? chalk_compat_1.default.green('✓ Completed') : chalk_compat_1.default.red('✗ Failed')}` ]; enhanced_logger_1.enhancedLogger.box(summary, { borderColor: session.status === 'completed' ? 'green' : 'red', borderStyle: 'round' }); } function generateSessionReport(sessions, format) { if (format === 'json') { return JSON.stringify(sessions, null, 2); } if (format === 'md') { let report = '# Development Session Report\n\n'; report += `Generated: ${new Date().toLocaleDateString()}\n\n`; report += '## Summary\n\n'; report += `- Total Sessions: ${sessions.length}\n`; report += `- Completed: ${sessions.filter(s => s.status === 'completed').length}\n`; report += `- Failed: ${sessions.filter(s => s.status === 'failed').length}\n`; report += `- Total Duration: ${formatDuration(sessions.reduce((acc, s) => acc + (s.duration || 0), 0))}\n\n`; report += '## Sessions\n\n'; for (const session of sessions) { report += `### ${session.id} (${session.type})\n\n`; report += `- **Epic**: ${session.epic || 'N/A'}\n`; report += `- **Story**: ${session.story || 'N/A'}\n`; report += `- **Duration**: ${session.duration ? formatDuration(session.duration) : 'In progress'}\n`; report += `- **Status**: ${session.status}\n`; report += `- **Files Modified**: ${session.progress.filesModified}\n`; report += `- **Tests Added**: ${session.progress.testsAdded}\n`; report += `- **Coverage**: ${session.metrics.coverage}%\n\n`; } return report; } return 'Unsupported format'; } //# sourceMappingURL=track.js.map