Files
automaker/app/electron/services/feature-verifier.js
Cody Seibert 08014f3a4a Refactor Auto Mode Service and add feature verification and project analysis capabilities
- Refactored AutoModeService to delegate tasks to specialized services: featureLoader, featureExecutor, featureVerifier, contextManager, and projectAnalyzer.
- Implemented feature verification logic in a new FeatureVerifier service, which runs tests and updates feature status.
- Added ProjectAnalyzer service to scan project structure and update app_spec.txt.
- Removed obsolete methods related to feature loading and context management from AutoModeService.
- Updated feature status handling to ensure context files are deleted when features are verified.

This refactor enhances modularity and maintainability of the codebase, allowing for better separation of concerns in feature management.

🤖 Generated with Claude Code
2025-12-09 13:03:03 -05:00

149 lines
4.8 KiB
JavaScript

const { query, AbortError } = require("@anthropic-ai/claude-agent-sdk");
const promptBuilder = require("./prompt-builder");
const contextManager = require("./context-manager");
const featureLoader = require("./feature-loader");
const mcpServerFactory = require("./mcp-server-factory");
/**
* Feature Verifier - Handles feature verification by running tests
*/
class FeatureVerifier {
/**
* Verify feature tests (runs tests and checks if they pass)
*/
async verifyFeatureTests(feature, projectPath, sendToRenderer, execution) {
console.log(`[FeatureVerifier] Verifying tests for: ${feature.description}`);
try {
const verifyMsg = `\n✅ Verifying tests for: ${feature.description}\n`;
await contextManager.writeToContextFile(projectPath, feature.id, verifyMsg);
sendToRenderer({
type: "auto_mode_phase",
featureId: feature.id,
phase: "verification",
message: `Verifying tests for: ${feature.description}`,
});
const abortController = new AbortController();
execution.abortController = abortController;
// Create custom MCP server with UpdateFeatureStatus tool
const featureToolsServer = mcpServerFactory.createFeatureToolsServer(
featureLoader.updateFeatureStatus.bind(featureLoader),
projectPath
);
const options = {
model: "claude-opus-4-5-20251101",
systemPrompt: promptBuilder.getVerificationPrompt(),
maxTurns: 1000,
cwd: projectPath,
mcpServers: {
"automaker-tools": featureToolsServer
},
allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash", "mcp__automaker-tools__UpdateFeatureStatus"],
permissionMode: "acceptEdits",
sandbox: {
enabled: true,
autoAllowBashIfSandboxed: true,
},
abortController: abortController,
};
const prompt = promptBuilder.buildVerificationPrompt(feature);
const runningTestsMsg =
"Running Playwright tests to verify feature implementation...\n";
await contextManager.writeToContextFile(projectPath, feature.id, runningTestsMsg);
sendToRenderer({
type: "auto_mode_progress",
featureId: feature.id,
content: runningTestsMsg,
});
const currentQuery = query({ prompt, options });
execution.query = currentQuery;
let responseText = "";
for await (const msg of currentQuery) {
// Check if this specific feature was aborted
if (!execution.isActive()) break;
if (msg.type === "assistant" && msg.message?.content) {
for (const block of msg.message.content) {
if (block.type === "text") {
responseText += block.text;
await contextManager.writeToContextFile(projectPath, feature.id, block.text);
sendToRenderer({
type: "auto_mode_progress",
featureId: feature.id,
content: block.text,
});
} else if (block.type === "tool_use") {
const toolMsg = `\n🔧 Tool: ${block.name}\n`;
await contextManager.writeToContextFile(projectPath, feature.id, toolMsg);
sendToRenderer({
type: "auto_mode_tool",
featureId: feature.id,
tool: block.name,
input: block.input,
});
}
}
}
}
execution.query = null;
execution.abortController = null;
// Re-load features to check if it was marked as verified
const updatedFeatures = await featureLoader.loadFeatures(projectPath);
const updatedFeature = updatedFeatures.find((f) => f.id === feature.id);
const passes = updatedFeature?.status === "verified";
const finalMsg = passes
? "✓ Verification successful: All tests passed\n"
: "✗ Tests failed or not all passing - feature remains in progress\n";
await contextManager.writeToContextFile(projectPath, feature.id, finalMsg);
sendToRenderer({
type: "auto_mode_progress",
featureId: feature.id,
content: finalMsg,
});
return {
passes,
message: responseText.substring(0, 500),
};
} catch (error) {
if (error instanceof AbortError || error?.name === "AbortError") {
console.log("[FeatureVerifier] Verification aborted");
if (execution) {
execution.abortController = null;
execution.query = null;
}
return {
passes: false,
message: "Verification aborted",
};
}
console.error("[FeatureVerifier] Error verifying feature:", error);
if (execution) {
execution.abortController = null;
execution.query = null;
}
throw error;
}
}
}
module.exports = new FeatureVerifier();