context-forge
Version:
AI orchestration platform with autonomous teams, enhancement planning, migration tools, 25+ slash commands, checkpoints & hooks. Multi-IDE: Claude, Cursor, Windsurf, Cline, Copilot
239 lines • 8.84 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProgressTracker = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
class ProgressTracker {
constructor(projectPath = process.cwd()) {
this.currentEntry = null;
this.progressFile = path_1.default.join(projectPath, '.context-forge', 'progress.json');
}
async startOperation(command, operation, metadata) {
const id = this.generateId();
this.currentEntry = {
id,
command,
operation,
status: 'in_progress',
startTime: new Date(),
projectPath: process.cwd(),
metadata: metadata || {},
steps: [],
};
await this.saveProgress();
return id;
}
async updateOperation(id, updates) {
if (this.currentEntry && this.currentEntry.id === id) {
Object.assign(this.currentEntry, updates);
await this.saveProgress();
}
else {
// Update stored entry
const progress = await this.loadProgress();
const entry = progress.find((p) => p.id === id);
if (entry) {
Object.assign(entry, updates);
await this.saveToFile(progress);
}
}
}
async completeOperation(id, status = 'completed', metadata) {
if (this.currentEntry && this.currentEntry.id === id) {
this.currentEntry.status = status;
this.currentEntry.endTime = new Date();
this.currentEntry.duration =
this.currentEntry.endTime.getTime() - this.currentEntry.startTime.getTime();
if (metadata) {
this.currentEntry.metadata = { ...this.currentEntry.metadata, ...metadata };
}
await this.saveProgress();
this.currentEntry = null;
}
}
async addStep(operationId, stepName, description) {
const stepId = this.generateId();
const step = {
id: stepId,
name: stepName,
status: 'in_progress',
startTime: new Date(),
description,
};
if (this.currentEntry && this.currentEntry.id === operationId) {
this.currentEntry.steps.push(step);
await this.saveProgress();
}
return stepId;
}
async completeStep(operationId, stepId, status = 'completed', metadata) {
if (this.currentEntry && this.currentEntry.id === operationId) {
const step = this.currentEntry.steps.find((s) => s.id === stepId);
if (step) {
step.status = status;
step.endTime = new Date();
step.metadata = metadata;
await this.saveProgress();
}
}
}
async getProgress() {
return await this.loadProgress();
}
async getProgressSummary() {
const progress = await this.loadProgress();
const now = new Date();
const recentActivity = progress
.filter((p) => {
const daysSinceStart = (now.getTime() - p.startTime.getTime()) / (1000 * 60 * 60 * 24);
return daysSinceStart <= 7; // Last 7 days
})
.sort((a, b) => b.startTime.getTime() - a.startTime.getTime())
.slice(0, 10);
const totalOperations = progress.length;
const completedOperations = progress.filter((p) => p.status === 'completed').length;
const failedOperations = progress.filter((p) => p.status === 'failed').length;
const inProgressOperations = progress.filter((p) => p.status === 'in_progress').length;
const completedWithDuration = progress.filter((p) => p.status === 'completed' && p.duration);
const averageDuration = completedWithDuration.length > 0
? completedWithDuration.reduce((sum, p) => sum + (p.duration || 0), 0) /
completedWithDuration.length
: 0;
const successRate = totalOperations > 0 ? (completedOperations / totalOperations) * 100 : 0;
return {
totalOperations,
completedOperations,
failedOperations,
inProgressOperations,
recentActivity,
averageDuration,
successRate,
};
}
async getCurrentOperation() {
if (this.currentEntry) {
return this.currentEntry;
}
const progress = await this.loadProgress();
return progress.find((p) => p.status === 'in_progress') || null;
}
async clearHistory(olderThanDays = 30) {
const progress = await this.loadProgress();
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - olderThanDays);
const filtered = progress.filter((p) => {
return p.startTime >= cutoffDate || p.status === 'in_progress';
});
const removedCount = progress.length - filtered.length;
if (removedCount > 0) {
await this.saveToFile(filtered);
}
return removedCount;
}
async loadProgress() {
try {
if (await fs_extra_1.default.pathExists(this.progressFile)) {
const data = await fs_extra_1.default.readJson(this.progressFile);
return Array.isArray(data) ? data.map(this.deserializeEntry) : [];
}
}
catch (error) {
console.warn('Failed to load progress file:', error);
}
return [];
}
async saveProgress() {
if (!this.currentEntry)
return;
const progress = await this.loadProgress();
const existingIndex = progress.findIndex((p) => p.id === this.currentEntry.id);
if (existingIndex >= 0) {
progress[existingIndex] = this.currentEntry;
}
else {
progress.push(this.currentEntry);
}
await this.saveToFile(progress);
}
async saveToFile(progress) {
try {
await fs_extra_1.default.ensureDir(path_1.default.dirname(this.progressFile));
await fs_extra_1.default.writeJson(this.progressFile, progress, { spaces: 2 });
}
catch (error) {
console.warn('Failed to save progress file:', error);
}
}
deserializeEntry(data) {
return {
id: data.id,
command: data.command,
operation: data.operation,
status: data.status,
projectPath: data.projectPath,
startTime: new Date(data.startTime),
endTime: data.endTime ? new Date(data.endTime) : undefined,
duration: data.duration,
metadata: data.metadata,
steps: data.steps?.map((step) => ({
id: step.id,
name: step.name,
status: step.status,
startTime: step.startTime ? new Date(step.startTime) : undefined,
endTime: step.endTime ? new Date(step.endTime) : undefined,
description: step.description,
metadata: step.metadata,
})) || [],
};
}
generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2, 9);
}
// Utility methods for displaying progress
formatDuration(milliseconds) {
if (milliseconds < 1000)
return `${milliseconds}ms`;
if (milliseconds < 60000)
return `${(milliseconds / 1000).toFixed(1)}s`;
if (milliseconds < 3600000)
return `${(milliseconds / 60000).toFixed(1)}m`;
return `${(milliseconds / 3600000).toFixed(1)}h`;
}
getStatusIcon(status) {
switch (status) {
case 'completed':
return '✅';
case 'failed':
return '❌';
case 'in_progress':
return '🔄';
case 'cancelled':
return '⚠️';
case 'pending':
return '⏳';
default:
return '❓';
}
}
getStatusColor(status) {
switch (status) {
case 'completed':
return 'green';
case 'failed':
return 'red';
case 'in_progress':
return 'yellow';
case 'cancelled':
return 'orange';
case 'pending':
return 'gray';
default:
return 'white';
}
}
}
exports.ProgressTracker = ProgressTracker;
//# sourceMappingURL=progressTracker.js.map