mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
- Improved error handling in AutoModeService to log errors and update feature status to "waiting_approval" when an error occurs during feature execution. - Added error message writing to the context file for better debugging. - Updated FeatureLoader to include an optional error message parameter when updating feature status. - Enhanced prompt generation to include detailed context about attached images for better user guidance during feature implementation. - Modified KanbanCard component to visually indicate features with errors, improving user awareness of issues. These changes significantly enhance the robustness of the auto mode feature and improve the user experience by providing clearer feedback on feature execution status.
163 lines
5.1 KiB
JavaScript
163 lines
5.1 KiB
JavaScript
const path = require("path");
|
|
const fs = require("fs/promises");
|
|
|
|
/**
|
|
* Feature Loader - Handles loading and selecting features from feature_list.json
|
|
*/
|
|
class FeatureLoader {
|
|
/**
|
|
* Load features from .automaker/feature_list.json
|
|
*/
|
|
async loadFeatures(projectPath) {
|
|
const featuresPath = path.join(
|
|
projectPath,
|
|
".automaker",
|
|
"feature_list.json"
|
|
);
|
|
|
|
try {
|
|
const content = await fs.readFile(featuresPath, "utf-8");
|
|
const features = JSON.parse(content);
|
|
|
|
// Ensure each feature has an ID
|
|
return features.map((f, index) => ({
|
|
...f,
|
|
id: f.id || `feature-${index}-${Date.now()}`,
|
|
}));
|
|
} catch (error) {
|
|
console.error("[FeatureLoader] Failed to load features:", error);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update feature status in .automaker/feature_list.json
|
|
* @param {string} featureId - The ID of the feature to update
|
|
* @param {string} status - The new status
|
|
* @param {string} projectPath - Path to the project
|
|
* @param {string} [summary] - Optional summary of what was done
|
|
* @param {string} [error] - Optional error message if feature errored
|
|
*/
|
|
async updateFeatureStatus(featureId, status, projectPath, summary, error) {
|
|
const featuresPath = path.join(
|
|
projectPath,
|
|
".automaker",
|
|
"feature_list.json"
|
|
);
|
|
|
|
// 🛡️ SAFETY: Create backup before any modification
|
|
const backupPath = path.join(
|
|
projectPath,
|
|
".automaker",
|
|
"feature_list.backup.json"
|
|
);
|
|
|
|
try {
|
|
const originalContent = await fs.readFile(featuresPath, "utf-8");
|
|
await fs.writeFile(backupPath, originalContent, "utf-8");
|
|
console.log(`[FeatureLoader] Created backup at ${backupPath}`);
|
|
} catch (error) {
|
|
console.warn(`[FeatureLoader] Could not create backup: ${error.message}`);
|
|
}
|
|
|
|
const features = await this.loadFeatures(projectPath);
|
|
|
|
// 🛡️ VALIDATION: Ensure we loaded features successfully
|
|
if (!Array.isArray(features)) {
|
|
throw new Error("CRITICAL: features is not an array - aborting to prevent data loss");
|
|
}
|
|
|
|
if (features.length === 0) {
|
|
console.warn(`[FeatureLoader] WARNING: Feature list is empty. This may indicate corruption.`);
|
|
// Try to restore from backup
|
|
try {
|
|
const backupContent = await fs.readFile(backupPath, "utf-8");
|
|
const backupFeatures = JSON.parse(backupContent);
|
|
if (Array.isArray(backupFeatures) && backupFeatures.length > 0) {
|
|
console.log(`[FeatureLoader] Restored ${backupFeatures.length} features from backup`);
|
|
// Use backup features instead
|
|
features.length = 0;
|
|
features.push(...backupFeatures);
|
|
}
|
|
} catch (backupError) {
|
|
console.error(`[FeatureLoader] Could not restore from backup: ${backupError.message}`);
|
|
}
|
|
}
|
|
|
|
const feature = features.find((f) => f.id === featureId);
|
|
|
|
if (!feature) {
|
|
console.error(`[FeatureLoader] Feature ${featureId} not found`);
|
|
return;
|
|
}
|
|
|
|
// Update the status field
|
|
feature.status = status;
|
|
|
|
// Update the summary field if provided
|
|
if (summary) {
|
|
feature.summary = summary;
|
|
}
|
|
|
|
// Update the error field (set or clear)
|
|
if (error) {
|
|
feature.error = error;
|
|
} else {
|
|
// Clear any previous error when status changes without error
|
|
delete feature.error;
|
|
}
|
|
|
|
// Save back to file
|
|
const toSave = features.map((f) => {
|
|
const featureData = {
|
|
id: f.id,
|
|
category: f.category,
|
|
description: f.description,
|
|
steps: f.steps,
|
|
status: f.status,
|
|
};
|
|
// Preserve optional fields if they exist
|
|
if (f.skipTests !== undefined) {
|
|
featureData.skipTests = f.skipTests;
|
|
}
|
|
if (f.images !== undefined) {
|
|
featureData.images = f.images;
|
|
}
|
|
if (f.imagePaths !== undefined) {
|
|
featureData.imagePaths = f.imagePaths;
|
|
}
|
|
if (f.startedAt !== undefined) {
|
|
featureData.startedAt = f.startedAt;
|
|
}
|
|
if (f.summary !== undefined) {
|
|
featureData.summary = f.summary;
|
|
}
|
|
if (f.error !== undefined) {
|
|
featureData.error = f.error;
|
|
}
|
|
return featureData;
|
|
});
|
|
|
|
// 🛡️ FINAL VALIDATION: Ensure we're not writing an empty array
|
|
if (!Array.isArray(toSave) || toSave.length === 0) {
|
|
throw new Error("CRITICAL: Attempted to save empty feature list - aborting to prevent data loss");
|
|
}
|
|
|
|
await fs.writeFile(featuresPath, JSON.stringify(toSave, null, 2), "utf-8");
|
|
console.log(`[FeatureLoader] Updated feature ${featureId}: status=${status}${summary ? `, summary="${summary}"` : ""}`);
|
|
console.log(`[FeatureLoader] Successfully saved ${toSave.length} features to feature_list.json`);
|
|
}
|
|
|
|
/**
|
|
* Select the next feature to implement
|
|
* Prioritizes: earlier features in the list that are not verified or waiting_approval
|
|
*/
|
|
selectNextFeature(features) {
|
|
// Find first feature that is in backlog or in_progress status
|
|
// Skip verified and waiting_approval (which needs user input)
|
|
return features.find((f) => f.status !== "verified" && f.status !== "waiting_approval");
|
|
}
|
|
}
|
|
|
|
module.exports = new FeatureLoader();
|