mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
further gemini reviews and fixes
This commit is contained in:
@@ -391,8 +391,7 @@ class AutoModeService {
|
||||
featureId,
|
||||
"waiting_approval",
|
||||
projectPath,
|
||||
null, // no summary
|
||||
error.message // pass error message
|
||||
{ error: error.message }
|
||||
);
|
||||
} catch (statusError) {
|
||||
console.error("[AutoMode] Failed to update feature status after error:", statusError);
|
||||
@@ -495,8 +494,7 @@ class AutoModeService {
|
||||
featureId,
|
||||
"waiting_approval",
|
||||
projectPath,
|
||||
null, // no summary
|
||||
error.message // pass error message
|
||||
{ error: error.message }
|
||||
);
|
||||
} catch (statusError) {
|
||||
console.error("[AutoMode] Failed to update feature status after error:", statusError);
|
||||
@@ -662,8 +660,7 @@ class AutoModeService {
|
||||
featureId,
|
||||
"waiting_approval",
|
||||
projectPath,
|
||||
null, // no summary
|
||||
error.message // pass error message
|
||||
{ error: error.message }
|
||||
);
|
||||
} catch (statusError) {
|
||||
console.error("[AutoMode] Failed to update feature status after error:", statusError);
|
||||
@@ -859,8 +856,7 @@ class AutoModeService {
|
||||
featureId,
|
||||
"waiting_approval",
|
||||
projectPath,
|
||||
null, // no summary
|
||||
error.message // pass error message
|
||||
{ error: error.message }
|
||||
);
|
||||
} catch (statusError) {
|
||||
console.error("[AutoMode] Failed to update feature status after error:", statusError);
|
||||
@@ -1102,8 +1098,7 @@ class AutoModeService {
|
||||
featureId,
|
||||
"waiting_approval",
|
||||
projectPath,
|
||||
null, // no summary
|
||||
error.message // pass error message
|
||||
{ error: error.message }
|
||||
);
|
||||
} catch (statusError) {
|
||||
console.error("[AutoMode] Failed to update feature status after error:", statusError);
|
||||
|
||||
@@ -898,7 +898,7 @@ ipcMain.handle(
|
||||
featureId,
|
||||
status,
|
||||
projectPath,
|
||||
summary
|
||||
{ summary }
|
||||
);
|
||||
|
||||
// Notify renderer if window is available
|
||||
@@ -1170,6 +1170,7 @@ ipcMain.handle("spec-regeneration:status", () => {
|
||||
isRunning:
|
||||
specRegenerationExecution !== null &&
|
||||
specRegenerationExecution.isActive(),
|
||||
currentPhase: specRegenerationService.getCurrentPhase(),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -382,13 +382,15 @@ class FeatureLoader {
|
||||
* @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
|
||||
* @param {string} [description] - Optional detailed description
|
||||
* @param {string} [category] - Optional category/phase
|
||||
* @param {string[]} [steps] - Optional array of implementation steps
|
||||
* @param {Object} options - Options object for optional parameters
|
||||
* @param {string} [options.summary] - Optional summary of what was done
|
||||
* @param {string} [options.error] - Optional error message if feature errored
|
||||
* @param {string} [options.description] - Optional detailed description
|
||||
* @param {string} [options.category] - Optional category/phase
|
||||
* @param {string[]} [options.steps] - Optional array of implementation steps
|
||||
*/
|
||||
async updateFeatureStatus(featureId, status, projectPath, summary, error, description, category, steps) {
|
||||
async updateFeatureStatus(featureId, status, projectPath, options = {}) {
|
||||
const { summary, error, description, category, steps } = options;
|
||||
// Check if feature exists
|
||||
const existingFeature = await this.get(projectPath, featureId);
|
||||
|
||||
|
||||
@@ -53,16 +53,17 @@ class McpServerFactory {
|
||||
finalStatus = "waiting_approval";
|
||||
}
|
||||
|
||||
// Call the provided callback to update feature status with all parameters
|
||||
// Call the provided callback to update feature status
|
||||
await updateFeatureStatusCallback(
|
||||
args.featureId,
|
||||
finalStatus,
|
||||
projectPath,
|
||||
args.summary,
|
||||
undefined, // error
|
||||
args.description,
|
||||
args.category,
|
||||
args.steps
|
||||
{
|
||||
summary: args.summary,
|
||||
description: args.description,
|
||||
category: args.category,
|
||||
steps: args.steps,
|
||||
}
|
||||
);
|
||||
|
||||
const statusMessage = finalStatus !== args.status
|
||||
|
||||
@@ -215,7 +215,7 @@ async function handleToolsCall(params, id) {
|
||||
// Call the update callback via IPC or direct call
|
||||
// Since we're in a separate process, we need to use IPC to communicate back
|
||||
// For now, we'll call the feature loader directly since it has the update method
|
||||
await featureLoader.updateFeatureStatus(featureId, finalStatus, projectPath, summary);
|
||||
await featureLoader.updateFeatureStatus(featureId, finalStatus, projectPath, { summary });
|
||||
|
||||
const statusMessage = finalStatus !== status
|
||||
? `Successfully updated feature ${featureId} to status "${finalStatus}" (converted from "${status}" because skipTests=true)${summary ? ` with summary: "${summary}"` : ''}`
|
||||
|
||||
@@ -86,6 +86,15 @@ const APP_SPEC_XML_TEMPLATE = `<project_specification>
|
||||
class SpecRegenerationService {
|
||||
constructor() {
|
||||
this.runningRegeneration = null;
|
||||
this.currentPhase = ""; // Tracks current phase for status queries
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current phase of the regeneration process
|
||||
* @returns {string} Current phase or empty string if not running
|
||||
*/
|
||||
getCurrentPhase() {
|
||||
return this.currentPhase;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,14 +116,14 @@ class SpecRegenerationService {
|
||||
const abortController = new AbortController();
|
||||
execution.abortController = abortController;
|
||||
|
||||
// Phase tracking
|
||||
let currentPhase = "initialization";
|
||||
// Phase tracking - use instance property for status queries
|
||||
this.currentPhase = "initialization";
|
||||
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Phase: ${currentPhase}] Initializing spec generation process...\n`,
|
||||
content: `[Phase: ${this.currentPhase}] Initializing spec generation process...\n`,
|
||||
});
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase}`);
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase}`);
|
||||
|
||||
// Create custom MCP server with UpdateFeatureStatus tool if generating features
|
||||
if (generateFeatures) {
|
||||
@@ -135,12 +144,12 @@ class SpecRegenerationService {
|
||||
}
|
||||
}
|
||||
|
||||
currentPhase = "setup";
|
||||
this.currentPhase = "setup";
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Phase: ${currentPhase}] Configuring AI agent and tools...\n`,
|
||||
content: `[Phase: ${this.currentPhase}] Configuring AI agent and tools...\n`,
|
||||
});
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase}`);
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase}`);
|
||||
|
||||
// Phase 1: Generate spec WITHOUT UpdateFeatureStatus tool
|
||||
// This prevents features from being created before the spec is complete
|
||||
@@ -160,17 +169,17 @@ class SpecRegenerationService {
|
||||
|
||||
const prompt = this.buildInitialCreationPrompt(projectOverview); // No feature generation during spec creation
|
||||
|
||||
currentPhase = "analysis";
|
||||
this.currentPhase = "analysis";
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Phase: ${currentPhase}] Starting project analysis and spec creation...\n`,
|
||||
content: `[Phase: ${this.currentPhase}] Starting project analysis and spec creation...\n`,
|
||||
});
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase} - Starting AI agent query`);
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase} - Starting AI agent query`);
|
||||
|
||||
if (generateFeatures) {
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Phase: ${currentPhase}] Feature generation is enabled - features will be created after spec is complete.\n`,
|
||||
content: `[Phase: ${this.currentPhase}] Feature generation is enabled - features will be created after spec is complete.\n`,
|
||||
});
|
||||
console.log("[SpecRegeneration] Feature generation enabled - will create features after spec");
|
||||
}
|
||||
@@ -253,11 +262,11 @@ class SpecRegenerationService {
|
||||
execution.abortController = null;
|
||||
|
||||
const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||
currentPhase = "spec_complete";
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase} - Spec creation completed in ${elapsedTime}s`);
|
||||
this.currentPhase = "spec_complete";
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase} - Spec creation completed in ${elapsedTime}s`);
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `\n[Phase: ${currentPhase}] ✓ App specification created successfully! (${elapsedTime}s)\n`,
|
||||
content: `\n[Phase: ${this.currentPhase}] ✓ App specification created successfully! (${elapsedTime}s)\n`,
|
||||
});
|
||||
|
||||
if (generateFeatures) {
|
||||
@@ -282,10 +291,10 @@ class SpecRegenerationService {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
currentPhase = "complete";
|
||||
this.currentPhase = "complete";
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Phase: ${currentPhase}] All tasks completed!\n`,
|
||||
content: `[Phase: ${this.currentPhase}] All tasks completed!\n`,
|
||||
});
|
||||
|
||||
// Send final completion event
|
||||
@@ -344,16 +353,16 @@ class SpecRegenerationService {
|
||||
*/
|
||||
async generateFeaturesFromSpec(projectPath, sendToRenderer, execution, startTime) {
|
||||
const featureStartTime = Date.now();
|
||||
let currentPhase = "feature_generation";
|
||||
this.currentPhase = "feature_generation";
|
||||
|
||||
console.log(`[SpecRegeneration] ===== Starting Phase 2: Feature Generation =====`);
|
||||
console.log(`[SpecRegeneration] Project path: ${projectPath}`);
|
||||
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `\n[Phase: ${currentPhase}] Starting feature creation from implementation roadmap...\n`,
|
||||
content: `\n[Phase: ${this.currentPhase}] Starting feature creation from implementation roadmap...\n`,
|
||||
});
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase} - Starting feature generation query`);
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase} - Starting feature generation query`);
|
||||
|
||||
try {
|
||||
// Create feature tools server
|
||||
@@ -492,12 +501,12 @@ Use the UpdateFeatureStatus tool to create features with ALL the fields above.`,
|
||||
execution.query = null;
|
||||
execution.abortController = null;
|
||||
|
||||
currentPhase = "complete";
|
||||
this.currentPhase = "complete";
|
||||
const featureElapsedTime = ((Date.now() - featureStartTime) / 1000).toFixed(1);
|
||||
const totalElapsedTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `\n[Phase: ${currentPhase}] ✓ All tasks completed! (${totalElapsedTime}s total, ${featureElapsedTime}s for features)\n`,
|
||||
content: `\n[Phase: ${this.currentPhase}] ✓ All tasks completed! (${totalElapsedTime}s total, ${featureElapsedTime}s for features)\n`,
|
||||
});
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_complete",
|
||||
@@ -653,22 +662,22 @@ Begin by exploring the project structure.`;
|
||||
console.log(`[SpecRegeneration] Project definition length: ${projectDefinition.length} characters`);
|
||||
|
||||
try {
|
||||
let currentPhase = "initialization";
|
||||
this.currentPhase = "initialization";
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Phase: ${currentPhase}] Initializing spec regeneration process...\n`,
|
||||
content: `[Phase: ${this.currentPhase}] Initializing spec regeneration process...\n`,
|
||||
});
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase}`);
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase}`);
|
||||
|
||||
const abortController = new AbortController();
|
||||
execution.abortController = abortController;
|
||||
|
||||
currentPhase = "setup";
|
||||
this.currentPhase = "setup";
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Phase: ${currentPhase}] Configuring AI agent and tools...\n`,
|
||||
content: `[Phase: ${this.currentPhase}] Configuring AI agent and tools...\n`,
|
||||
});
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase}`);
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase}`);
|
||||
|
||||
const options = {
|
||||
model: "claude-sonnet-4-20250514",
|
||||
@@ -686,12 +695,12 @@ Begin by exploring the project structure.`;
|
||||
|
||||
const prompt = this.buildRegenerationPrompt(projectDefinition);
|
||||
|
||||
currentPhase = "regeneration";
|
||||
this.currentPhase = "regeneration";
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Phase: ${currentPhase}] Starting spec regeneration...\n`,
|
||||
content: `[Phase: ${this.currentPhase}] Starting spec regeneration...\n`,
|
||||
});
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase} - Starting AI agent query`);
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase} - Starting AI agent query`);
|
||||
|
||||
const currentQuery = query({ prompt, options });
|
||||
execution.query = currentQuery;
|
||||
@@ -725,21 +734,10 @@ Begin by exploring the project structure.`;
|
||||
console.log(`[SpecRegeneration] Tool call #${toolCallCount}: ${toolName}`);
|
||||
console.log(`[SpecRegeneration] Tool input: ${JSON.stringify(toolInput).substring(0, 200)}...`);
|
||||
|
||||
// Special handling for UpdateFeatureStatus to show feature creation
|
||||
if (toolName === "mcp__automaker-tools__UpdateFeatureStatus" || toolName === "UpdateFeatureStatus") {
|
||||
const featureId = toolInput?.featureId || "unknown";
|
||||
const status = toolInput?.status || "unknown";
|
||||
const summary = toolInput?.summary || "";
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `\n[Feature Creation] Creating feature "${featureId}" with status "${status}"${summary ? `\n Summary: ${summary}` : ""}\n`,
|
||||
});
|
||||
} else {
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `\n[Tool] Using ${toolName}...\n`,
|
||||
});
|
||||
}
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `\n[Tool] Using ${toolName}...\n`,
|
||||
});
|
||||
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_tool",
|
||||
@@ -755,18 +753,10 @@ Begin by exploring the project structure.`;
|
||||
const resultPreview = result.substring(0, 200).replace(/\n/g, " ");
|
||||
console.log(`[SpecRegeneration] Tool result (${toolName}): ${resultPreview}...`);
|
||||
|
||||
// Special handling for UpdateFeatureStatus results
|
||||
if (toolName === "mcp__automaker-tools__UpdateFeatureStatus" || toolName === "UpdateFeatureStatus") {
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Feature Creation] ${result}\n`,
|
||||
});
|
||||
} else {
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Tool Result] ${toolName} completed successfully\n`,
|
||||
});
|
||||
}
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `[Tool Result] ${toolName} completed successfully\n`,
|
||||
});
|
||||
} else if (msg.type === "error") {
|
||||
const errorMsg = msg.error?.message || JSON.stringify(msg.error);
|
||||
console.error(`[SpecRegeneration] ERROR in query stream: ${errorMsg}`);
|
||||
@@ -791,11 +781,11 @@ Begin by exploring the project structure.`;
|
||||
execution.abortController = null;
|
||||
|
||||
const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||
currentPhase = "complete";
|
||||
console.log(`[SpecRegeneration] Phase: ${currentPhase} - Spec regeneration completed in ${elapsedTime}s`);
|
||||
this.currentPhase = "complete";
|
||||
console.log(`[SpecRegeneration] Phase: ${this.currentPhase} - Spec regeneration completed in ${elapsedTime}s`);
|
||||
sendToRenderer({
|
||||
type: "spec_regeneration_progress",
|
||||
content: `\n[Phase: ${currentPhase}] ✓ Spec regeneration complete! (${elapsedTime}s)\n`,
|
||||
content: `\n[Phase: ${this.currentPhase}] ✓ Spec regeneration complete! (${elapsedTime}s)\n`,
|
||||
});
|
||||
|
||||
sendToRenderer({
|
||||
@@ -867,9 +857,8 @@ When analyzing, look at:
|
||||
- Database configurations and schemas
|
||||
- API structures and patterns
|
||||
|
||||
**Feature Storage:**
|
||||
Features are stored in .automaker/features/{id}/feature.json - each feature has its own folder.
|
||||
Do NOT manually create feature files. Use the UpdateFeatureStatus tool to manage features.
|
||||
**Note:** Feature files are stored separately in .automaker/features/{id}/feature.json.
|
||||
Your task is ONLY to update the app_spec.txt file - feature files will be managed separately.
|
||||
|
||||
You CAN and SHOULD modify:
|
||||
- .automaker/app_spec.txt (this is your primary target)
|
||||
@@ -1042,6 +1031,7 @@ Begin by exploring the project structure.`;
|
||||
this.runningRegeneration.abortController.abort();
|
||||
}
|
||||
this.runningRegeneration = null;
|
||||
this.currentPhase = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,93 +105,38 @@ export function SpecView() {
|
||||
console.log("[SpecView] Status check on mount:", status);
|
||||
|
||||
if (status.success && status.isRunning) {
|
||||
// Something is running - restore state
|
||||
console.log("[SpecView] Spec generation is running - restoring state");
|
||||
// Something is running - restore state using backend's authoritative phase
|
||||
console.log("[SpecView] Spec generation is running - restoring state", { phase: status.currentPhase });
|
||||
|
||||
// Only restore state if we haven't already done so for this project
|
||||
// This prevents resetting state when switching tabs
|
||||
if (!stateRestoredRef.current) {
|
||||
setIsCreating(true);
|
||||
setIsRegenerating(true);
|
||||
stateRestoredRef.current = true;
|
||||
}
|
||||
|
||||
// Try to extract the current phase from existing logs
|
||||
let detectedPhase = "";
|
||||
if (logsRef.current) {
|
||||
// Look for the most recent phase in the logs
|
||||
const phaseMatches = logsRef.current.matchAll(/\[Phase:\s*([^\]]+)\]/g);
|
||||
const phases = Array.from(phaseMatches);
|
||||
if (phases.length > 0) {
|
||||
// Get the last phase mentioned in the logs
|
||||
detectedPhase = phases[phases.length - 1][1];
|
||||
console.log(`[SpecView] Detected phase from logs: ${detectedPhase}`);
|
||||
}
|
||||
|
||||
// Also check for feature generation indicators in logs
|
||||
const hasFeatureGeneration = logsRef.current.includes("Feature Generation") ||
|
||||
logsRef.current.includes("Feature Creation") ||
|
||||
logsRef.current.includes("Creating feature") ||
|
||||
logsRef.current.includes("feature_generation");
|
||||
|
||||
if (hasFeatureGeneration && !detectedPhase) {
|
||||
detectedPhase = "feature_generation";
|
||||
console.log("[SpecView] Detected feature generation from logs");
|
||||
}
|
||||
}
|
||||
|
||||
// Update phase from logs if we found one and don't have a specific phase set
|
||||
// This allows the phase to update as new events come in
|
||||
if (detectedPhase) {
|
||||
setCurrentPhase((prevPhase) => {
|
||||
// Only update if we don't have a phase or if the detected phase is more recent
|
||||
if (!prevPhase || prevPhase === "unknown" || prevPhase === "in progress") {
|
||||
return detectedPhase;
|
||||
}
|
||||
return prevPhase;
|
||||
});
|
||||
} else if (!currentPhase) {
|
||||
// Use a more descriptive default instead of "unknown"
|
||||
// Use the backend's currentPhase directly - single source of truth
|
||||
if (status.currentPhase) {
|
||||
setCurrentPhase(status.currentPhase);
|
||||
} else {
|
||||
setCurrentPhase("in progress");
|
||||
}
|
||||
|
||||
// Don't clear logs - they may have been persisted
|
||||
// Add resume message to logs if needed
|
||||
if (!logsRef.current) {
|
||||
const resumeMessage = "[Status] Resumed monitoring existing spec generation process...\n";
|
||||
logsRef.current = resumeMessage;
|
||||
setLogs(resumeMessage);
|
||||
} else if (!logsRef.current.includes("Resumed monitoring")) {
|
||||
// Add a resume message to existing logs only if not already present
|
||||
const resumeMessage = "\n[Status] Resumed monitoring existing spec generation process...\n";
|
||||
logsRef.current = logsRef.current + resumeMessage;
|
||||
setLogs(logsRef.current);
|
||||
}
|
||||
} else if (status.success && !status.isRunning) {
|
||||
// Check if we might still be in feature generation phase based on logs
|
||||
const mightBeGeneratingFeatures = logsRef.current && (
|
||||
logsRef.current.includes("Feature Generation") ||
|
||||
logsRef.current.includes("Feature Creation") ||
|
||||
logsRef.current.includes("Creating feature") ||
|
||||
logsRef.current.includes("feature_generation") ||
|
||||
currentPhase === "feature_generation"
|
||||
);
|
||||
|
||||
if (mightBeGeneratingFeatures && specExists) {
|
||||
// Spec exists and we might still be generating features - keep state active
|
||||
console.log("[SpecView] Detected potential feature generation - keeping state active");
|
||||
if (!isCreating && !isRegenerating) {
|
||||
setIsCreating(true);
|
||||
}
|
||||
if (currentPhase !== "feature_generation") {
|
||||
setCurrentPhase("feature_generation");
|
||||
}
|
||||
} else {
|
||||
// Not running - clear running state
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase("");
|
||||
stateRestoredRef.current = false;
|
||||
}
|
||||
// Not running - clear all state
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setCurrentPhase("");
|
||||
stateRestoredRef.current = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[SpecView] Failed to check status:", error);
|
||||
@@ -209,7 +154,7 @@ export function SpecView() {
|
||||
useEffect(() => {
|
||||
const handleVisibilityChange = async () => {
|
||||
if (!document.hidden && currentProject && (isCreating || isRegenerating || isGeneratingFeatures)) {
|
||||
// Tab became visible and we think we're still generating - verify status
|
||||
// Tab became visible and we think we're still generating - verify status from backend
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (!api.specRegeneration) return;
|
||||
@@ -218,45 +163,17 @@ export function SpecView() {
|
||||
console.log("[SpecView] Visibility change - status check:", status);
|
||||
|
||||
if (!status.isRunning) {
|
||||
// Not running but we think we are - check if we're truly done
|
||||
// Look for recent activity in logs (within last 30 seconds worth of content)
|
||||
const recentLogs = logsRef.current.slice(-5000); // Last ~5000 chars
|
||||
const hasRecentFeatureActivity = recentLogs.includes("Feature Creation") ||
|
||||
recentLogs.includes("Creating feature") ||
|
||||
recentLogs.match(/\[Feature Creation\].*$/m);
|
||||
|
||||
// Check if we have a completion message or complete phase
|
||||
const hasCompletion = logsRef.current.includes("All tasks completed") ||
|
||||
logsRef.current.includes("[Complete] All tasks completed") ||
|
||||
logsRef.current.includes("[Phase: complete]");
|
||||
|
||||
if (hasCompletion || (!hasRecentFeatureActivity && currentPhase !== "feature_generation")) {
|
||||
// No recent activity and not running - we're done
|
||||
console.log("[SpecView] Visibility change: Generation appears complete - clearing state");
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
stateRestoredRef.current = false;
|
||||
loadSpec();
|
||||
} else if (currentPhase === "feature_generation" && !hasRecentFeatureActivity) {
|
||||
// We were in feature generation but no recent activity - might be done
|
||||
// Wait a moment and check again
|
||||
setTimeout(async () => {
|
||||
if (api.specRegeneration) {
|
||||
const recheckStatus = await api.specRegeneration.status();
|
||||
if (!recheckStatus.isRunning) {
|
||||
console.log("[SpecView] Re-check after visibility: Still not running - clearing state");
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
stateRestoredRef.current = false;
|
||||
loadSpec();
|
||||
}
|
||||
}
|
||||
}, STATUS_CHECK_INTERVAL_MS);
|
||||
}
|
||||
// Backend says not running - clear state
|
||||
console.log("[SpecView] Visibility change: Backend indicates generation complete - clearing state");
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
stateRestoredRef.current = false;
|
||||
loadSpec();
|
||||
} else if (status.currentPhase) {
|
||||
// Still running - update phase from backend
|
||||
setCurrentPhase(status.currentPhase);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[SpecView] Failed to check status on visibility change:", error);
|
||||
@@ -268,7 +185,7 @@ export function SpecView() {
|
||||
return () => {
|
||||
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||
};
|
||||
}, [currentProject, isCreating, isRegenerating, isGeneratingFeatures, currentPhase, loadSpec]);
|
||||
}, [currentProject, isCreating, isRegenerating, isGeneratingFeatures, loadSpec]);
|
||||
|
||||
// Periodic status check to ensure state stays in sync (only when we think we're running)
|
||||
useEffect(() => {
|
||||
@@ -281,40 +198,22 @@ export function SpecView() {
|
||||
|
||||
const status = await api.specRegeneration.status();
|
||||
|
||||
// If not running but we think we are, verify we're truly done
|
||||
if (!status.isRunning && (isCreating || isRegenerating || isGeneratingFeatures)) {
|
||||
// Check logs for completion indicators
|
||||
const hasCompletion = logsRef.current.includes("All tasks completed") ||
|
||||
logsRef.current.includes("[Complete] All tasks completed") ||
|
||||
logsRef.current.includes("[Phase: complete]") ||
|
||||
currentPhase === "complete";
|
||||
|
||||
// Also check if we haven't seen feature activity recently
|
||||
const recentLogs = logsRef.current.slice(-3000); // Last 3000 chars (more context)
|
||||
const hasRecentFeatureActivity = recentLogs.includes("Feature Creation") ||
|
||||
recentLogs.includes("Creating feature") ||
|
||||
recentLogs.includes("UpdateFeatureStatus") ||
|
||||
recentLogs.includes("[Tool]") && recentLogs.includes("UpdateFeatureStatus");
|
||||
|
||||
// If we're in feature_generation phase and not running, we're likely done
|
||||
// (features are created via tool calls, so when stream ends, they're done)
|
||||
const isFeatureGenComplete = currentPhase === "feature_generation" &&
|
||||
!hasRecentFeatureActivity;
|
||||
|
||||
if (hasCompletion || isFeatureGenComplete) {
|
||||
console.log("[SpecView] Periodic check: Generation complete - clearing state", {
|
||||
hasCompletion,
|
||||
hasRecentFeatureActivity,
|
||||
currentPhase,
|
||||
isFeatureGenComplete
|
||||
});
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
stateRestoredRef.current = false;
|
||||
loadSpec();
|
||||
}
|
||||
if (!status.isRunning) {
|
||||
// Backend says not running - clear state
|
||||
console.log("[SpecView] Periodic check: Backend indicates generation complete - clearing state");
|
||||
setIsCreating(false);
|
||||
setIsRegenerating(false);
|
||||
setIsGeneratingFeatures(false);
|
||||
setCurrentPhase("");
|
||||
stateRestoredRef.current = false;
|
||||
loadSpec();
|
||||
} else if (status.currentPhase && status.currentPhase !== currentPhase) {
|
||||
// Still running but phase changed - update from backend
|
||||
console.log("[SpecView] Periodic check: Phase updated from backend", {
|
||||
old: currentPhase,
|
||||
new: status.currentPhase
|
||||
});
|
||||
setCurrentPhase(status.currentPhase);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[SpecView] Periodic status check error:", error);
|
||||
|
||||
@@ -1800,6 +1800,7 @@ async function simulateSuggestionsGeneration(
|
||||
|
||||
// Mock Spec Regeneration state and implementation
|
||||
let mockSpecRegenerationRunning = false;
|
||||
let mockSpecRegenerationPhase = "";
|
||||
let mockSpecRegenerationCallbacks: ((event: SpecRegenerationEvent) => void)[] =
|
||||
[];
|
||||
let mockSpecRegenerationTimeout: NodeJS.Timeout | null = null;
|
||||
@@ -1862,6 +1863,7 @@ function createMockSpecRegenerationAPI(): SpecRegenerationAPI {
|
||||
|
||||
stop: async () => {
|
||||
mockSpecRegenerationRunning = false;
|
||||
mockSpecRegenerationPhase = "";
|
||||
if (mockSpecRegenerationTimeout) {
|
||||
clearTimeout(mockSpecRegenerationTimeout);
|
||||
mockSpecRegenerationTimeout = null;
|
||||
@@ -1873,6 +1875,7 @@ function createMockSpecRegenerationAPI(): SpecRegenerationAPI {
|
||||
return {
|
||||
success: true,
|
||||
isRunning: mockSpecRegenerationRunning,
|
||||
currentPhase: mockSpecRegenerationPhase,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1896,9 +1899,10 @@ async function simulateSpecCreation(
|
||||
projectOverview: string,
|
||||
generateFeatures = true
|
||||
) {
|
||||
mockSpecRegenerationPhase = "initialization";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_progress",
|
||||
content: "Starting project analysis...\n",
|
||||
content: "[Phase: initialization] Starting project analysis...\n",
|
||||
});
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@@ -1906,6 +1910,7 @@ async function simulateSpecCreation(
|
||||
});
|
||||
if (!mockSpecRegenerationRunning) return;
|
||||
|
||||
mockSpecRegenerationPhase = "setup";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_tool",
|
||||
tool: "Glob",
|
||||
@@ -1917,9 +1922,10 @@ async function simulateSpecCreation(
|
||||
});
|
||||
if (!mockSpecRegenerationRunning) return;
|
||||
|
||||
mockSpecRegenerationPhase = "analysis";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_progress",
|
||||
content: "Detecting tech stack...\n",
|
||||
content: "[Phase: analysis] Detecting tech stack...\n",
|
||||
});
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@@ -1959,12 +1965,14 @@ async function simulateSpecCreation(
|
||||
// The generateFeatures parameter is kept for API compatibility but features
|
||||
// should be created through the features API
|
||||
|
||||
mockSpecRegenerationPhase = "complete";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_complete",
|
||||
message: "Initial spec creation complete!",
|
||||
message: "All tasks completed!",
|
||||
});
|
||||
|
||||
mockSpecRegenerationRunning = false;
|
||||
mockSpecRegenerationPhase = "";
|
||||
mockSpecRegenerationTimeout = null;
|
||||
}
|
||||
|
||||
@@ -1972,9 +1980,10 @@ async function simulateSpecRegeneration(
|
||||
projectPath: string,
|
||||
projectDefinition: string
|
||||
) {
|
||||
mockSpecRegenerationPhase = "initialization";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_progress",
|
||||
content: "Starting spec regeneration...\n",
|
||||
content: "[Phase: initialization] Starting spec regeneration...\n",
|
||||
});
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@@ -1982,9 +1991,10 @@ async function simulateSpecRegeneration(
|
||||
});
|
||||
if (!mockSpecRegenerationRunning) return;
|
||||
|
||||
mockSpecRegenerationPhase = "analysis";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_progress",
|
||||
content: "Analyzing codebase...\n",
|
||||
content: "[Phase: analysis] Analyzing codebase...\n",
|
||||
});
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@@ -2015,16 +2025,19 @@ async function simulateSpecRegeneration(
|
||||
</core_capabilities>
|
||||
</project_specification>`;
|
||||
|
||||
mockSpecRegenerationPhase = "complete";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_complete",
|
||||
message: "Spec regeneration complete!",
|
||||
message: "All tasks completed!",
|
||||
});
|
||||
|
||||
mockSpecRegenerationRunning = false;
|
||||
mockSpecRegenerationPhase = "";
|
||||
mockSpecRegenerationTimeout = null;
|
||||
}
|
||||
|
||||
async function simulateFeatureGeneration(projectPath: string) {
|
||||
mockSpecRegenerationPhase = "initialization";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_progress",
|
||||
content: "[Phase: initialization] Starting feature generation from existing app_spec.txt...\n",
|
||||
@@ -2045,9 +2058,10 @@ async function simulateFeatureGeneration(projectPath: string) {
|
||||
});
|
||||
if (!mockSpecRegenerationRunning) return;
|
||||
|
||||
mockSpecRegenerationPhase = "feature_generation";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_progress",
|
||||
content: "[Feature Creation] Creating features from roadmap...\n",
|
||||
content: "[Phase: feature_generation] Creating features from roadmap...\n",
|
||||
});
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@@ -2055,6 +2069,7 @@ async function simulateFeatureGeneration(projectPath: string) {
|
||||
});
|
||||
if (!mockSpecRegenerationRunning) return;
|
||||
|
||||
mockSpecRegenerationPhase = "complete";
|
||||
emitSpecRegenerationEvent({
|
||||
type: "spec_regeneration_progress",
|
||||
content: "[Phase: complete] All tasks completed!\n",
|
||||
@@ -2066,6 +2081,7 @@ async function simulateFeatureGeneration(projectPath: string) {
|
||||
});
|
||||
|
||||
mockSpecRegenerationRunning = false;
|
||||
mockSpecRegenerationPhase = "";
|
||||
mockSpecRegenerationTimeout = null;
|
||||
}
|
||||
|
||||
|
||||
1
app/src/types/electron.d.ts
vendored
1
app/src/types/electron.d.ts
vendored
@@ -270,6 +270,7 @@ export interface SpecRegenerationAPI {
|
||||
status: () => Promise<{
|
||||
success: boolean;
|
||||
isRunning?: boolean;
|
||||
currentPhase?: string;
|
||||
error?: string;
|
||||
}>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user