mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
feat(feature-list-protection): implement multi-layered protection strategy for feature list
- Removed the `FEATURE_LIST_PROTECTION.md` documentation file as the protection strategy has been fully implemented. - Updated the `.automaker/feature_list.json` to an empty array to prevent accidental data loss. - Enhanced the context management and prompt generation to include critical warnings and memory content for agents. - Added validation, auto-backup, and access control mechanisms to safeguard the feature list from direct modifications. These changes ensure robust protection against data loss and improve the overall safety of feature management within the application.
This commit is contained in:
@@ -1,24 +1 @@
|
||||
[
|
||||
{
|
||||
"id": "feature-1765376066671-lmchhkgez",
|
||||
"category": "Agent Runner",
|
||||
"description": "On the Agent Runner, the markdown is not being formatted properly. Can you please bring in a React library to format markdown so that on a session window of the agent, it'll properly format the markdown?",
|
||||
"steps": [],
|
||||
"status": "waiting_approval",
|
||||
"startedAt": "2025-12-10T14:15:24.929Z",
|
||||
"imagePaths": [],
|
||||
"skipTests": true,
|
||||
"summary": "Added markdown formatting to Agent Runner session messages. Modified: agent-view.tsx. Used existing Markdown component with react-markdown library to properly render headings, code blocks, lists, links, and other markdown elements in assistant messages."
|
||||
},
|
||||
{
|
||||
"id": "feature-1765376191866-n4r0s1l6q",
|
||||
"category": "Agent Runner",
|
||||
"description": "On the Agent Runner page, change the hotkey for making a session from W to New. I want all new feature buttons to be the hotkey New.",
|
||||
"steps": [],
|
||||
"status": "waiting_approval",
|
||||
"startedAt": "2025-12-10T14:16:34.207Z",
|
||||
"imagePaths": [],
|
||||
"skipTests": true,
|
||||
"summary": "Changed the new session hotkey from \"W\" to \"N\" on the Agent Runner page. Modified: use-keyboard-shortcuts.ts (line 125). This aligns with the user's request to have all \"new\" feature buttons use the \"N\" hotkey consistently."
|
||||
}
|
||||
]
|
||||
[]
|
||||
@@ -1,120 +0,0 @@
|
||||
## YOUR ROLE - INITIALIZER AGENT (Session 1 of Many)
|
||||
|
||||
You are the FIRST agent in a long-running autonomous development process.
|
||||
Your job is to set up the foundation for all future coding agents.
|
||||
|
||||
### FIRST: Read the Project Specification
|
||||
|
||||
Start by reading `app_spec.txt` in your working directory. This file contains
|
||||
the complete specification for what you need to build. Read it carefully
|
||||
before proceeding.
|
||||
|
||||
### CRITICAL FIRST TASK: Create .automaker/feature_list.json
|
||||
|
||||
Based on `app_spec.txt`, create a file called `feature_list.json` with 200 detailed
|
||||
end-to-end test cases. This file is the single source of truth for what
|
||||
needs to be built.
|
||||
|
||||
**Format:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"category": "functional",
|
||||
"description": "Brief description of the feature and what this test verifies",
|
||||
"steps": [
|
||||
"Step 1: Navigate to relevant page",
|
||||
"Step 2: Perform action",
|
||||
"Step 3: Verify expected result"
|
||||
],
|
||||
"passes": false
|
||||
},
|
||||
{
|
||||
"category": "style",
|
||||
"description": "Brief description of UI/UX requirement",
|
||||
"steps": [
|
||||
"Step 1: Navigate to page",
|
||||
"Step 2: Take screenshot",
|
||||
"Step 3: Verify visual requirements"
|
||||
],
|
||||
"passes": false
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Requirements for .automaker/feature_list.json:**
|
||||
|
||||
- Minimum 200 features total with testing steps for each
|
||||
- Both "functional" and "style" categories
|
||||
- Mix of narrow tests (2-5 steps) and comprehensive tests (10+ steps)
|
||||
- At least 25 tests MUST have 10+ steps each
|
||||
- Order features by priority: fundamental features first
|
||||
- ALL tests start with "passes": false
|
||||
- Cover every feature in the spec exhaustively
|
||||
|
||||
**CRITICAL INSTRUCTION:**
|
||||
IT IS CATASTROPHIC TO REMOVE OR EDIT FEATURES IN FUTURE SESSIONS.
|
||||
Features can ONLY be marked as passing (change "passes": false to "passes": true).
|
||||
Never remove features, never edit descriptions, never modify testing steps.
|
||||
This ensures no functionality is missed.
|
||||
|
||||
**🚨 CRITICAL: AFTER CREATING .automaker/feature_list.json 🚨**
|
||||
Once you create this file in this session, you MUST NEVER directly modify it again.
|
||||
In all future sessions, feature_list.json is COMPLETELY OFF-LIMITS for:
|
||||
- Write tool
|
||||
- Edit tool
|
||||
- Any bash commands (echo, sed, awk, etc.)
|
||||
- Any form of direct file modification
|
||||
|
||||
The ONLY way to update features is through the UpdateFeatureStatus MCP tool.
|
||||
|
||||
### SECOND TASK: Create init.sh
|
||||
|
||||
Create a script called `init.sh` that future agents can use to quickly
|
||||
set up and run the development environment. The script should:
|
||||
|
||||
1. Install any required dependencies
|
||||
2. Start any necessary servers or services
|
||||
3. Print helpful information about how to access the running application
|
||||
|
||||
Base the script on the technology stack specified in `app_spec.txt`.
|
||||
|
||||
### THIRD TASK: Initialize Git
|
||||
|
||||
Create a git repository and make your first commit with:
|
||||
|
||||
- .automaker/feature_list.json (complete with all 200+ features)
|
||||
- init.sh (environment setup script)
|
||||
- README.md (project overview and setup instructions)
|
||||
|
||||
Commit message: "Initial setup: .automaker/feature_list.json, init.sh, and project structure"
|
||||
|
||||
### FOURTH TASK: Create Project Structure
|
||||
|
||||
Set up the basic project structure based on what's specified in `app_spec.txt`.
|
||||
This typically includes directories for frontend, backend, and any other
|
||||
components mentioned in the spec.
|
||||
|
||||
### OPTIONAL: Start Implementation
|
||||
|
||||
If you have time remaining in this session, you may begin implementing
|
||||
the highest-priority features from .automaker/feature_list.json. Remember:
|
||||
|
||||
- Work on ONE feature at a time
|
||||
- Test thoroughly before marking "passes": true
|
||||
- Commit your progress before session ends
|
||||
|
||||
### ENDING THIS SESSION
|
||||
|
||||
Before your context fills up:
|
||||
|
||||
1. Commit all work with descriptive messages
|
||||
2. Ensure .automaker/feature_list.json is complete and saved
|
||||
3. Leave the environment in a clean, working state
|
||||
|
||||
The next agent will continue from here with a fresh context window.
|
||||
|
||||
---
|
||||
|
||||
**Remember:** You have unlimited time across many sessions. Focus on
|
||||
quality over speed. Production-ready is the goal.
|
||||
@@ -1,252 +0,0 @@
|
||||
# Feature List Protection Strategy
|
||||
|
||||
## Problem
|
||||
|
||||
The `.automaker/feature_list.json` file is the single source of truth for all project features and their status. If an AI agent accidentally clears or corrupts this file, it results in catastrophic data loss - potentially erasing hours or days of planning work.
|
||||
|
||||
**Incident:** An agent attempted to update the feature list and completely cleared it out, leaving only `[]`.
|
||||
|
||||
## Solution: Multi-Layered Protection
|
||||
|
||||
We've implemented a defense-in-depth strategy with multiple layers of protection to prevent this from ever happening again.
|
||||
|
||||
---
|
||||
|
||||
## Layer 1: Explicit Prompt-Level Warnings
|
||||
|
||||
### Location
|
||||
All agent system prompts now include prominent warnings at the top:
|
||||
|
||||
- `app/electron/services/prompt-builder.js`:
|
||||
- `getCodingPrompt()` - Used by feature implementation agents
|
||||
- `getVerificationPrompt()` - Used by verification agents
|
||||
- `app/electron/agent-service.js`:
|
||||
- `getSystemPrompt()` - Used by the general chat agent
|
||||
- `.automaker/initializer_prompt.md` - Used by the initialization agent
|
||||
|
||||
### Content
|
||||
Each prompt now starts with:
|
||||
|
||||
```
|
||||
🚨 CRITICAL FILE PROTECTION - READ THIS FIRST 🚨
|
||||
|
||||
THE FOLLOWING FILE IS ABSOLUTELY FORBIDDEN FROM DIRECT MODIFICATION:
|
||||
- .automaker/feature_list.json
|
||||
|
||||
YOU MUST NEVER:
|
||||
- Use the Write tool on feature_list.json
|
||||
- Use the Edit tool on feature_list.json
|
||||
- Use any Bash command that writes to feature_list.json (echo, sed, awk, etc.)
|
||||
- Attempt to read and rewrite feature_list.json
|
||||
- UNDER ANY CIRCUMSTANCES touch this file directly
|
||||
|
||||
CATASTROPHIC CONSEQUENCES:
|
||||
Directly modifying feature_list.json can:
|
||||
- Erase all project features permanently
|
||||
- Corrupt the project state beyond recovery
|
||||
- Destroy hours/days of planning work
|
||||
- This is a FIREABLE OFFENSE - you will be terminated if you do this
|
||||
|
||||
THE ONLY WAY to update features:
|
||||
Use the mcp__automaker-tools__UpdateFeatureStatus tool with featureId, status, and summary parameters.
|
||||
```
|
||||
|
||||
### Why This Works
|
||||
- Uses attention-grabbing emoji and formatting
|
||||
- Places warnings at the very top of prompts (high visibility)
|
||||
- Uses strong language ("CATASTROPHIC", "FIREABLE OFFENSE")
|
||||
- Explicitly lists all forbidden actions
|
||||
- Provides the correct alternative (UpdateFeatureStatus tool)
|
||||
|
||||
---
|
||||
|
||||
## Layer 2: Dedicated MCP Tool
|
||||
|
||||
### Location
|
||||
`app/electron/services/mcp-server-factory.js`
|
||||
|
||||
### How It Works
|
||||
The `UpdateFeatureStatus` tool provides a safe, controlled interface for updating features:
|
||||
|
||||
```javascript
|
||||
tool(
|
||||
"UpdateFeatureStatus",
|
||||
"Update the status of a feature in the feature list. Use this tool instead of directly modifying feature_list.json...",
|
||||
{
|
||||
featureId: z.string(),
|
||||
status: z.enum(["backlog", "in_progress", "verified"]),
|
||||
summary: z.string().optional()
|
||||
},
|
||||
async (args) => {
|
||||
// Calls featureLoader.updateFeatureStatus with validation
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Why This Works
|
||||
- Provides a single, well-defined API for status updates
|
||||
- Only accepts specific, validated parameters
|
||||
- Cannot be misused to clear the entire file
|
||||
- Tool description explicitly states it should be used instead of direct edits
|
||||
|
||||
---
|
||||
|
||||
## Layer 3: File-Level Validation & Auto-Backup
|
||||
|
||||
### Location
|
||||
`app/electron/services/feature-loader.js` - `updateFeatureStatus()` method
|
||||
|
||||
### Protection Mechanisms
|
||||
|
||||
#### 3.1 Automatic Backup Before Every Write
|
||||
```javascript
|
||||
// Create .automaker/feature_list.backup.json before any modification
|
||||
const backupPath = path.join(projectPath, ".automaker", "feature_list.backup.json");
|
||||
await fs.writeFile(backupPath, originalContent, "utf-8");
|
||||
```
|
||||
|
||||
**Benefit:** If corruption occurs, we can manually restore from the backup.
|
||||
|
||||
#### 3.2 Array Validation
|
||||
```javascript
|
||||
if (!Array.isArray(features)) {
|
||||
throw new Error("CRITICAL: features is not an array - aborting to prevent data loss");
|
||||
}
|
||||
```
|
||||
|
||||
**Benefit:** Prevents writing if the loaded data is corrupted.
|
||||
|
||||
#### 3.3 Empty Array Detection & Auto-Restore
|
||||
```javascript
|
||||
if (features.length === 0) {
|
||||
console.warn("WARNING: Feature list is empty. This may indicate corruption.");
|
||||
// Try to restore from backup
|
||||
const backupFeatures = JSON.parse(await fs.readFile(backupPath, "utf-8"));
|
||||
if (Array.isArray(backupFeatures) && backupFeatures.length > 0) {
|
||||
features.push(...backupFeatures);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Benefit:** If the file is somehow cleared, the tool automatically attempts to restore from backup.
|
||||
|
||||
#### 3.4 Pre-Write Validation
|
||||
```javascript
|
||||
if (!Array.isArray(toSave) || toSave.length === 0) {
|
||||
throw new Error("CRITICAL: Attempted to save empty feature list - aborting to prevent data loss");
|
||||
}
|
||||
```
|
||||
|
||||
**Benefit:** Final safety check - will never write an empty array to the file.
|
||||
|
||||
#### 3.5 Backup File Ignored by Git
|
||||
Created `.automaker/.gitignore`:
|
||||
```
|
||||
feature_list.backup.json
|
||||
```
|
||||
|
||||
**Benefit:** Backup files don't clutter the git repository.
|
||||
|
||||
---
|
||||
|
||||
## Layer 4: Tool Access Control
|
||||
|
||||
### Location
|
||||
`app/electron/services/feature-executor.js` and `feature-verifier.js`
|
||||
|
||||
### Allowed Tools
|
||||
The agents only have access to these tools:
|
||||
```javascript
|
||||
allowedTools: [
|
||||
"Read",
|
||||
"Write",
|
||||
"Edit",
|
||||
"Glob",
|
||||
"Grep",
|
||||
"Bash",
|
||||
"WebSearch",
|
||||
"WebFetch",
|
||||
"mcp__automaker-tools__UpdateFeatureStatus",
|
||||
]
|
||||
```
|
||||
|
||||
### Future Enhancement Opportunity
|
||||
We could create a custom wrapper around Write/Edit that blocks access to specific files:
|
||||
```javascript
|
||||
// Potential future enhancement
|
||||
if (filePath.includes('feature_list.json')) {
|
||||
throw new Error('BLOCKED: feature_list.json can only be updated via UpdateFeatureStatus tool');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing the Protection
|
||||
|
||||
To verify the protection works:
|
||||
|
||||
1. **Prompt-Level Protection Test:**
|
||||
- Ask an agent to update feature_list.json directly
|
||||
- Agent should refuse and explain it must use UpdateFeatureStatus tool
|
||||
|
||||
2. **Tool Protection Test:**
|
||||
- Use UpdateFeatureStatus with valid data
|
||||
- Verify backup is created in `.automaker/feature_list.backup.json`
|
||||
- Verify feature is updated correctly
|
||||
|
||||
3. **Corruption Recovery Test:**
|
||||
- Manually corrupt feature_list.json (e.g., set to `[]`)
|
||||
- Call UpdateFeatureStatus
|
||||
- Verify it auto-restores from backup
|
||||
|
||||
4. **Empty Array Prevention Test:**
|
||||
- Attempt to save empty array programmatically
|
||||
- Verify the error is thrown and file is not written
|
||||
|
||||
---
|
||||
|
||||
## Recovery Procedures
|
||||
|
||||
### If feature_list.json Gets Cleared
|
||||
|
||||
1. **Immediate Recovery:**
|
||||
```bash
|
||||
cd .automaker
|
||||
cp feature_list.backup.json feature_list.json
|
||||
```
|
||||
|
||||
2. **Check Git History:**
|
||||
```bash
|
||||
git log --all --full-history -- .automaker/feature_list.json
|
||||
git show <commit>:.automaker/feature_list.json > .automaker/feature_list.json
|
||||
```
|
||||
|
||||
3. **Verify Recovery:**
|
||||
```bash
|
||||
cat .automaker/feature_list.json | jq length
|
||||
# Should show number of features, not 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
We now have **four layers of protection**:
|
||||
|
||||
1. ✅ **Explicit prompt warnings** - Agents are told in strong language never to touch the file
|
||||
2. ✅ **Dedicated MCP tool** - UpdateFeatureStatus provides the only safe way to update
|
||||
3. ✅ **File validation & auto-backup** - Automatic backups and validation prevent corruption
|
||||
4. ✅ **Tool access control** - Agents have limited tool access (could be enhanced further)
|
||||
|
||||
This defense-in-depth approach ensures that even if one layer fails, others will prevent data loss.
|
||||
|
||||
---
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. `app/electron/services/prompt-builder.js` - Added protection warnings to getCodingPrompt() and getVerificationPrompt()
|
||||
2. `app/electron/agent-service.js` - Added protection warnings to getSystemPrompt()
|
||||
3. `.automaker/initializer_prompt.md` - Added warning for initializer agent
|
||||
4. `app/electron/services/feature-loader.js` - Added backup, validation, and auto-restore logic
|
||||
5. `.automaker/.gitignore` - Added backup file ignore rule
|
||||
6. `FEATURE_LIST_PROTECTION.md` - This documentation file
|
||||
@@ -40,7 +40,12 @@ class ContextManager {
|
||||
*/
|
||||
async readContextFile(projectPath, featureId) {
|
||||
try {
|
||||
const contextPath = path.join(projectPath, ".automaker", "agents-context", `${featureId}.md`);
|
||||
const contextPath = path.join(
|
||||
projectPath,
|
||||
".automaker",
|
||||
"agents-context",
|
||||
`${featureId}.md`
|
||||
);
|
||||
const content = await fs.readFile(contextPath, "utf-8");
|
||||
return content;
|
||||
} catch (error) {
|
||||
@@ -56,16 +61,145 @@ class ContextManager {
|
||||
if (!projectPath) return;
|
||||
|
||||
try {
|
||||
const contextPath = path.join(projectPath, ".automaker", "agents-context", `${featureId}.md`);
|
||||
const contextPath = path.join(
|
||||
projectPath,
|
||||
".automaker",
|
||||
"agents-context",
|
||||
`${featureId}.md`
|
||||
);
|
||||
await fs.unlink(contextPath);
|
||||
console.log(`[ContextManager] Deleted agent context for feature ${featureId}`);
|
||||
console.log(
|
||||
`[ContextManager] Deleted agent context for feature ${featureId}`
|
||||
);
|
||||
} catch (error) {
|
||||
// File might not exist, which is fine
|
||||
if (error.code !== 'ENOENT') {
|
||||
if (error.code !== "ENOENT") {
|
||||
console.error("[ContextManager] Failed to delete context file:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the memory.md file containing lessons learned and common issues
|
||||
* Returns formatted string to inject into prompts
|
||||
*/
|
||||
async getMemoryContent(projectPath) {
|
||||
if (!projectPath) return "";
|
||||
|
||||
try {
|
||||
const memoryPath = path.join(projectPath, ".automaker", "memory.md");
|
||||
|
||||
// Check if file exists
|
||||
try {
|
||||
await fs.access(memoryPath);
|
||||
} catch {
|
||||
// File doesn't exist, return empty string
|
||||
return "";
|
||||
}
|
||||
|
||||
const content = await fs.readFile(memoryPath, "utf-8");
|
||||
|
||||
if (!content.trim()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `
|
||||
**🧠 Agent Memory - Previous Lessons Learned:**
|
||||
|
||||
The following memory file contains lessons learned from previous agent runs, including common issues and their solutions. Review this carefully to avoid repeating past mistakes.
|
||||
|
||||
<agent-memory>
|
||||
${content}
|
||||
</agent-memory>
|
||||
|
||||
**IMPORTANT:** If you encounter a new issue that took significant debugging effort to resolve, add it to the memory file at \`.automaker/memory.md\` in a concise format:
|
||||
- Issue title
|
||||
- Problem description (1-2 sentences)
|
||||
- Solution/fix (with code example if helpful)
|
||||
|
||||
This helps future agent runs avoid the same pitfalls.
|
||||
`;
|
||||
} catch (error) {
|
||||
console.error("[ContextManager] Failed to read memory file:", error);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List context files from .automaker/context/ directory and get previews
|
||||
* Returns a formatted string with file names and first 50 lines of each file
|
||||
*/
|
||||
async getContextFilesPreview(projectPath) {
|
||||
if (!projectPath) return "";
|
||||
|
||||
try {
|
||||
const contextDir = path.join(projectPath, ".automaker", "context");
|
||||
|
||||
// Check if directory exists
|
||||
try {
|
||||
await fs.access(contextDir);
|
||||
} catch {
|
||||
// Directory doesn't exist, return empty string
|
||||
return "";
|
||||
}
|
||||
|
||||
// Read directory contents
|
||||
const entries = await fs.readdir(contextDir, { withFileTypes: true });
|
||||
const files = entries
|
||||
.filter((entry) => entry.isFile())
|
||||
.map((entry) => entry.name)
|
||||
.sort();
|
||||
|
||||
if (files.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Build preview string
|
||||
const previews = [];
|
||||
previews.push(`\n**📁 Context Files Available:**\n`);
|
||||
previews.push(
|
||||
`The following context files are available in \`.automaker/context/\` directory.`
|
||||
);
|
||||
previews.push(
|
||||
`These files contain additional context that may be relevant to your work.`
|
||||
);
|
||||
previews.push(
|
||||
`You can read them in full using the Read tool if needed.\n`
|
||||
);
|
||||
|
||||
for (const fileName of files) {
|
||||
try {
|
||||
const filePath = path.join(contextDir, fileName);
|
||||
const content = await fs.readFile(filePath, "utf-8");
|
||||
const lines = content.split("\n");
|
||||
const previewLines = lines.slice(0, 50);
|
||||
const preview = previewLines.join("\n");
|
||||
const hasMore = lines.length > 50;
|
||||
|
||||
previews.push(`\n**File: ${fileName}**`);
|
||||
if (hasMore) {
|
||||
previews.push(
|
||||
`(Showing first 50 of ${lines.length} lines - use Read tool to see full content)`
|
||||
);
|
||||
}
|
||||
previews.push(`\`\`\``);
|
||||
previews.push(preview);
|
||||
previews.push(`\`\`\`\n`);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[ContextManager] Failed to read context file ${fileName}:`,
|
||||
error
|
||||
);
|
||||
previews.push(`\n**File: ${fileName}** (Error reading file)\n`);
|
||||
}
|
||||
}
|
||||
|
||||
return previews.join("\n");
|
||||
} catch (error) {
|
||||
console.error("[ContextManager] Failed to list context files:", error);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ContextManager();
|
||||
|
||||
@@ -27,7 +27,11 @@ class FeatureExecutor {
|
||||
// PHASE 1: PLANNING
|
||||
// ========================================
|
||||
const planningMessage = `📋 Planning implementation for: ${feature.description}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, planningMessage);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
planningMessage
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_phase",
|
||||
@@ -35,7 +39,9 @@ class FeatureExecutor {
|
||||
phase: "planning",
|
||||
message: `Planning implementation for: ${feature.description}`,
|
||||
});
|
||||
console.log(`[FeatureExecutor] Phase: PLANNING for ${feature.description}`);
|
||||
console.log(
|
||||
`[FeatureExecutor] Phase: PLANNING for ${feature.description}`
|
||||
);
|
||||
|
||||
const abortController = new AbortController();
|
||||
execution.abortController = abortController;
|
||||
@@ -46,14 +52,17 @@ class FeatureExecutor {
|
||||
projectPath
|
||||
);
|
||||
|
||||
// Determine if we're in TDD mode (skipTests=false means TDD mode)
|
||||
const isTDD = !feature.skipTests;
|
||||
|
||||
// Configure options for the SDK query
|
||||
const options = {
|
||||
model: "claude-opus-4-5-20251101",
|
||||
systemPrompt: promptBuilder.getCodingPrompt(),
|
||||
systemPrompt: await promptBuilder.getCodingPrompt(projectPath, isTDD),
|
||||
maxTurns: 1000,
|
||||
cwd: projectPath,
|
||||
mcpServers: {
|
||||
"automaker-tools": featureToolsServer
|
||||
"automaker-tools": featureToolsServer,
|
||||
},
|
||||
allowedTools: [
|
||||
"Read",
|
||||
@@ -75,7 +84,7 @@ class FeatureExecutor {
|
||||
};
|
||||
|
||||
// Build the prompt for this specific feature
|
||||
let prompt = promptBuilder.buildFeaturePrompt(feature);
|
||||
let prompt = await promptBuilder.buildFeaturePrompt(feature, projectPath);
|
||||
|
||||
// Add images to prompt if feature has imagePaths
|
||||
if (feature.imagePaths && feature.imagePaths.length > 0) {
|
||||
@@ -103,7 +112,8 @@ class FeatureExecutor {
|
||||
".gif": "image/gif",
|
||||
".webp": "image/webp",
|
||||
};
|
||||
const mediaType = mimeTypeMap[ext] || imagePathObj.mimeType || "image/png";
|
||||
const mediaType =
|
||||
mimeTypeMap[ext] || imagePathObj.mimeType || "image/png";
|
||||
|
||||
contentBlocks.push({
|
||||
type: "image",
|
||||
@@ -114,7 +124,9 @@ class FeatureExecutor {
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`[FeatureExecutor] Added image to prompt: ${imagePath}`);
|
||||
console.log(
|
||||
`[FeatureExecutor] Added image to prompt: ${imagePath}`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[FeatureExecutor] Failed to load image ${imagePathObj.path}:`,
|
||||
@@ -142,7 +154,11 @@ class FeatureExecutor {
|
||||
// PHASE 2: ACTION
|
||||
// ========================================
|
||||
const actionMessage = `⚡ Executing implementation for: ${feature.description}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, actionMessage);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
actionMessage
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_phase",
|
||||
@@ -169,7 +185,11 @@ class FeatureExecutor {
|
||||
responseText += block.text;
|
||||
|
||||
// Write to context file
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, block.text);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
block.text
|
||||
);
|
||||
|
||||
// Stream progress to renderer
|
||||
sendToRenderer({
|
||||
@@ -182,7 +202,11 @@ class FeatureExecutor {
|
||||
if (!hasStartedToolUse) {
|
||||
hasStartedToolUse = true;
|
||||
const startMsg = "Starting code implementation...\n";
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, startMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
startMsg
|
||||
);
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
featureId: feature.id,
|
||||
@@ -192,7 +216,11 @@ class FeatureExecutor {
|
||||
|
||||
// Write tool use to context file
|
||||
const toolMsg = `\n🔧 Tool: ${block.name}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, toolMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
toolMsg
|
||||
);
|
||||
|
||||
// Notify about tool use
|
||||
sendToRenderer({
|
||||
@@ -213,7 +241,11 @@ class FeatureExecutor {
|
||||
// PHASE 3: VERIFICATION
|
||||
// ========================================
|
||||
const verificationMessage = `✅ Verifying implementation for: ${feature.description}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, verificationMessage);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
verificationMessage
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_phase",
|
||||
@@ -221,11 +253,17 @@ class FeatureExecutor {
|
||||
phase: "verification",
|
||||
message: `Verifying implementation for: ${feature.description}`,
|
||||
});
|
||||
console.log(`[FeatureExecutor] Phase: VERIFICATION for ${feature.description}`);
|
||||
console.log(
|
||||
`[FeatureExecutor] Phase: VERIFICATION for ${feature.description}`
|
||||
);
|
||||
|
||||
const checkingMsg =
|
||||
"Verifying implementation and checking test results...\n";
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, checkingMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
checkingMsg
|
||||
);
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
featureId: feature.id,
|
||||
@@ -236,15 +274,21 @@ class FeatureExecutor {
|
||||
const updatedFeatures = await featureLoader.loadFeatures(projectPath);
|
||||
const updatedFeature = updatedFeatures.find((f) => f.id === feature.id);
|
||||
// For skipTests features, waiting_approval is also considered a success
|
||||
const passes = updatedFeature?.status === "verified" ||
|
||||
(updatedFeature?.skipTests && updatedFeature?.status === "waiting_approval");
|
||||
const passes =
|
||||
updatedFeature?.status === "verified" ||
|
||||
(updatedFeature?.skipTests &&
|
||||
updatedFeature?.status === "waiting_approval");
|
||||
|
||||
// Send verification result
|
||||
const resultMsg = passes
|
||||
? "✓ Verification successful: All tests passed\n"
|
||||
: "✗ Verification: Tests need attention\n";
|
||||
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, resultMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
resultMsg
|
||||
);
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
featureId: feature.id,
|
||||
@@ -283,12 +327,24 @@ class FeatureExecutor {
|
||||
/**
|
||||
* Resume feature implementation with previous context
|
||||
*/
|
||||
async resumeFeatureWithContext(feature, projectPath, sendToRenderer, previousContext, execution) {
|
||||
console.log(`[FeatureExecutor] Resuming with context for: ${feature.description}`);
|
||||
async resumeFeatureWithContext(
|
||||
feature,
|
||||
projectPath,
|
||||
sendToRenderer,
|
||||
previousContext,
|
||||
execution
|
||||
) {
|
||||
console.log(
|
||||
`[FeatureExecutor] Resuming with context for: ${feature.description}`
|
||||
);
|
||||
|
||||
try {
|
||||
const resumeMessage = `\n🔄 Resuming implementation for: ${feature.description}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, resumeMessage);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
resumeMessage
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_phase",
|
||||
@@ -300,6 +356,9 @@ class FeatureExecutor {
|
||||
const abortController = new AbortController();
|
||||
execution.abortController = abortController;
|
||||
|
||||
// Determine if we're in TDD mode (skipTests=false means TDD mode)
|
||||
const isTDD = !feature.skipTests;
|
||||
|
||||
// Create custom MCP server with UpdateFeatureStatus tool
|
||||
const featureToolsServer = mcpServerFactory.createFeatureToolsServer(
|
||||
featureLoader.updateFeatureStatus.bind(featureLoader),
|
||||
@@ -308,13 +367,23 @@ class FeatureExecutor {
|
||||
|
||||
const options = {
|
||||
model: "claude-opus-4-5-20251101",
|
||||
systemPrompt: promptBuilder.getVerificationPrompt(),
|
||||
systemPrompt: await promptBuilder.getVerificationPrompt(projectPath, isTDD),
|
||||
maxTurns: 1000,
|
||||
cwd: projectPath,
|
||||
mcpServers: {
|
||||
"automaker-tools": featureToolsServer
|
||||
"automaker-tools": featureToolsServer,
|
||||
},
|
||||
allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash", "WebSearch", "WebFetch", "mcp__automaker-tools__UpdateFeatureStatus"],
|
||||
allowedTools: [
|
||||
"Read",
|
||||
"Write",
|
||||
"Edit",
|
||||
"Glob",
|
||||
"Grep",
|
||||
"Bash",
|
||||
"WebSearch",
|
||||
"WebFetch",
|
||||
"mcp__automaker-tools__UpdateFeatureStatus",
|
||||
],
|
||||
permissionMode: "acceptEdits",
|
||||
sandbox: {
|
||||
enabled: true,
|
||||
@@ -324,7 +393,11 @@ class FeatureExecutor {
|
||||
};
|
||||
|
||||
// Build prompt with previous context
|
||||
let prompt = promptBuilder.buildResumePrompt(feature, previousContext);
|
||||
let prompt = await promptBuilder.buildResumePrompt(
|
||||
feature,
|
||||
previousContext,
|
||||
projectPath
|
||||
);
|
||||
|
||||
// Add images to prompt if feature has imagePaths or followUpImages
|
||||
const imagePaths = feature.followUpImages || feature.imagePaths;
|
||||
@@ -343,7 +416,10 @@ class FeatureExecutor {
|
||||
for (const imagePathObj of imagePaths) {
|
||||
try {
|
||||
// Handle both string paths and FeatureImagePath objects
|
||||
const imagePath = typeof imagePathObj === 'string' ? imagePathObj : imagePathObj.path;
|
||||
const imagePath =
|
||||
typeof imagePathObj === "string"
|
||||
? imagePathObj
|
||||
: imagePathObj.path;
|
||||
const imageBuffer = fs.readFileSync(imagePath);
|
||||
const base64Data = imageBuffer.toString("base64");
|
||||
const ext = path.extname(imagePath).toLowerCase();
|
||||
@@ -354,9 +430,10 @@ class FeatureExecutor {
|
||||
".gif": "image/gif",
|
||||
".webp": "image/webp",
|
||||
};
|
||||
const mediaType = typeof imagePathObj === 'string'
|
||||
? (mimeTypeMap[ext] || "image/png")
|
||||
: (mimeTypeMap[ext] || imagePathObj.mimeType || "image/png");
|
||||
const mediaType =
|
||||
typeof imagePathObj === "string"
|
||||
? mimeTypeMap[ext] || "image/png"
|
||||
: mimeTypeMap[ext] || imagePathObj.mimeType || "image/png";
|
||||
|
||||
contentBlocks.push({
|
||||
type: "image",
|
||||
@@ -367,9 +444,14 @@ class FeatureExecutor {
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`[FeatureExecutor] Added image to resume prompt: ${imagePath}`);
|
||||
console.log(
|
||||
`[FeatureExecutor] Added image to resume prompt: ${imagePath}`
|
||||
);
|
||||
} catch (error) {
|
||||
const errorPath = typeof imagePathObj === 'string' ? imagePathObj : imagePathObj.path;
|
||||
const errorPath =
|
||||
typeof imagePathObj === "string"
|
||||
? imagePathObj
|
||||
: imagePathObj.path;
|
||||
console.error(
|
||||
`[FeatureExecutor] Failed to load image ${errorPath}:`,
|
||||
error
|
||||
@@ -394,7 +476,11 @@ class FeatureExecutor {
|
||||
if (block.type === "text") {
|
||||
responseText += block.text;
|
||||
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, block.text);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
block.text
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
@@ -403,7 +489,11 @@ class FeatureExecutor {
|
||||
});
|
||||
} else if (block.type === "tool_use") {
|
||||
const toolMsg = `\n🔧 Tool: ${block.name}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, toolMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
toolMsg
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_tool",
|
||||
@@ -423,14 +513,20 @@ class FeatureExecutor {
|
||||
const updatedFeatures = await featureLoader.loadFeatures(projectPath);
|
||||
const updatedFeature = updatedFeatures.find((f) => f.id === feature.id);
|
||||
// For skipTests features, waiting_approval is also considered a success
|
||||
const passes = updatedFeature?.status === "verified" ||
|
||||
(updatedFeature?.skipTests && updatedFeature?.status === "waiting_approval");
|
||||
const passes =
|
||||
updatedFeature?.status === "verified" ||
|
||||
(updatedFeature?.skipTests &&
|
||||
updatedFeature?.status === "waiting_approval");
|
||||
|
||||
const finalMsg = passes
|
||||
? "✓ Feature successfully verified and completed\n"
|
||||
: "⚠ Feature still in progress - may need additional work\n";
|
||||
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, finalMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
finalMsg
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
@@ -469,11 +565,17 @@ class FeatureExecutor {
|
||||
* Analyzes changes and creates a proper conventional commit message
|
||||
*/
|
||||
async commitChangesOnly(feature, projectPath, sendToRenderer, execution) {
|
||||
console.log(`[FeatureExecutor] Committing changes for: ${feature.description}`);
|
||||
console.log(
|
||||
`[FeatureExecutor] Committing changes for: ${feature.description}`
|
||||
);
|
||||
|
||||
try {
|
||||
const commitMessage = `\n📝 Committing changes for: ${feature.description}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, commitMessage);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
commitMessage
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
@@ -503,7 +605,7 @@ IMPORTANT RULES:
|
||||
maxTurns: 15, // Allow some turns to analyze and commit
|
||||
cwd: projectPath,
|
||||
mcpServers: {
|
||||
"automaker-tools": featureToolsServer
|
||||
"automaker-tools": featureToolsServer,
|
||||
},
|
||||
allowedTools: ["Bash", "mcp__automaker-tools__UpdateFeatureStatus"],
|
||||
permissionMode: "acceptEdits",
|
||||
@@ -569,7 +671,11 @@ EOF
|
||||
if (block.type === "text") {
|
||||
responseText += block.text;
|
||||
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, block.text);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
block.text
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
@@ -578,7 +684,11 @@ EOF
|
||||
});
|
||||
} else if (block.type === "tool_use") {
|
||||
const toolMsg = `\n🔧 Tool: ${block.name}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, toolMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
toolMsg
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_tool",
|
||||
@@ -595,7 +705,11 @@ EOF
|
||||
execution.abortController = null;
|
||||
|
||||
const finalMsg = "✓ Changes committed successfully\n";
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, finalMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
finalMsg
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
|
||||
@@ -12,11 +12,17 @@ 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}`);
|
||||
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);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
verifyMsg
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_phase",
|
||||
@@ -36,13 +42,21 @@ class FeatureVerifier {
|
||||
|
||||
const options = {
|
||||
model: "claude-opus-4-5-20251101",
|
||||
systemPrompt: promptBuilder.getVerificationPrompt(),
|
||||
systemPrompt: await promptBuilder.getVerificationPrompt(projectPath),
|
||||
maxTurns: 1000,
|
||||
cwd: projectPath,
|
||||
mcpServers: {
|
||||
"automaker-tools": featureToolsServer
|
||||
"automaker-tools": featureToolsServer,
|
||||
},
|
||||
allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash", "mcp__automaker-tools__UpdateFeatureStatus"],
|
||||
allowedTools: [
|
||||
"Read",
|
||||
"Write",
|
||||
"Edit",
|
||||
"Glob",
|
||||
"Grep",
|
||||
"Bash",
|
||||
"mcp__automaker-tools__UpdateFeatureStatus",
|
||||
],
|
||||
permissionMode: "acceptEdits",
|
||||
sandbox: {
|
||||
enabled: true,
|
||||
@@ -51,11 +65,18 @@ class FeatureVerifier {
|
||||
abortController: abortController,
|
||||
};
|
||||
|
||||
const prompt = promptBuilder.buildVerificationPrompt(feature);
|
||||
const prompt = await promptBuilder.buildVerificationPrompt(
|
||||
feature,
|
||||
projectPath
|
||||
);
|
||||
|
||||
const runningTestsMsg =
|
||||
"Running Playwright tests to verify feature implementation...\n";
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, runningTestsMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
runningTestsMsg
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
@@ -76,7 +97,11 @@ class FeatureVerifier {
|
||||
if (block.type === "text") {
|
||||
responseText += block.text;
|
||||
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, block.text);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
block.text
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
@@ -85,7 +110,11 @@ class FeatureVerifier {
|
||||
});
|
||||
} else if (block.type === "tool_use") {
|
||||
const toolMsg = `\n🔧 Tool: ${block.name}\n`;
|
||||
await contextManager.writeToContextFile(projectPath, feature.id, toolMsg);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
toolMsg
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_tool",
|
||||
@@ -105,14 +134,20 @@ class FeatureVerifier {
|
||||
const updatedFeatures = await featureLoader.loadFeatures(projectPath);
|
||||
const updatedFeature = updatedFeatures.find((f) => f.id === feature.id);
|
||||
// For skipTests features, waiting_approval is also considered a success
|
||||
const passes = updatedFeature?.status === "verified" ||
|
||||
(updatedFeature?.skipTests && updatedFeature?.status === "waiting_approval");
|
||||
const passes =
|
||||
updatedFeature?.status === "verified" ||
|
||||
(updatedFeature?.skipTests &&
|
||||
updatedFeature?.status === "waiting_approval");
|
||||
|
||||
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);
|
||||
await contextManager.writeToContextFile(
|
||||
projectPath,
|
||||
feature.id,
|
||||
finalMsg
|
||||
);
|
||||
|
||||
sendToRenderer({
|
||||
type: "auto_mode_progress",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const contextManager = require("./context-manager");
|
||||
|
||||
/**
|
||||
* Prompt Builder - Generates prompts for different agent tasks
|
||||
*/
|
||||
@@ -5,16 +7,21 @@ class PromptBuilder {
|
||||
/**
|
||||
* Build the prompt for implementing a specific feature
|
||||
*/
|
||||
buildFeaturePrompt(feature) {
|
||||
async buildFeaturePrompt(feature, projectPath) {
|
||||
const skipTestsNote = feature.skipTests
|
||||
? `\n**⚠️ IMPORTANT - Manual Testing Mode:**\nThis feature has skipTests=true, which means:\n- DO NOT commit changes automatically\n- DO NOT mark as verified - it will automatically go to "waiting_approval" status\n- The user will manually review and commit the changes\n- Just implement the feature and mark it as verified (it will be converted to waiting_approval)\n`
|
||||
: "";
|
||||
|
||||
let imagesNote = "";
|
||||
if (feature.imagePaths && feature.imagePaths.length > 0) {
|
||||
const imagesList = feature.imagePaths.map((img, idx) =>
|
||||
` ${idx + 1}. ${img.filename} (${img.mimeType})\n Path: ${img.path}`
|
||||
).join("\n");
|
||||
const imagesList = feature.imagePaths
|
||||
.map(
|
||||
(img, idx) =>
|
||||
` ${idx + 1}. ${img.filename} (${img.mimeType})\n Path: ${
|
||||
img.path
|
||||
}`
|
||||
)
|
||||
.join("\n");
|
||||
|
||||
imagesNote = `\n**📎 Context Images Attached:**\nThe user has attached ${feature.imagePaths.length} image(s) for context. These images are provided both visually (in the initial message) and as files you can read:
|
||||
|
||||
@@ -23,14 +30,31 @@ ${imagesList}
|
||||
You can use the Read tool to view these images at any time during implementation. Review them carefully before implementing.\n`;
|
||||
}
|
||||
|
||||
// Get context files preview
|
||||
const contextFilesPreview = await contextManager.getContextFilesPreview(
|
||||
projectPath
|
||||
);
|
||||
|
||||
// Get memory content (lessons learned from previous runs)
|
||||
const memoryContent = await contextManager.getMemoryContent(projectPath);
|
||||
|
||||
// Build mode header for this feature
|
||||
const modeHeader = feature.skipTests
|
||||
? `**🔨 MODE: Manual Review (No Automated Tests)**
|
||||
This feature is set for manual review - focus on clean implementation without automated tests.`
|
||||
: `**🧪 MODE: Test-Driven Development (TDD)**
|
||||
This feature requires automated Playwright tests to verify the implementation.`;
|
||||
|
||||
return `You are working on a feature implementation task.
|
||||
|
||||
${modeHeader}
|
||||
${memoryContent}
|
||||
**Current Feature to Implement:**
|
||||
|
||||
ID: ${feature.id}
|
||||
Category: ${feature.category}
|
||||
Description: ${feature.description}
|
||||
${skipTestsNote}${imagesNote}
|
||||
${skipTestsNote}${imagesNote}${contextFilesPreview}
|
||||
**Steps to Complete:**
|
||||
${feature.steps.map((step, i) => `${i + 1}. ${step}`).join("\n")}
|
||||
|
||||
@@ -125,16 +149,21 @@ Begin by reading the project structure and then implementing the feature.`;
|
||||
/**
|
||||
* Build the prompt for verifying a specific feature
|
||||
*/
|
||||
buildVerificationPrompt(feature) {
|
||||
async buildVerificationPrompt(feature, projectPath) {
|
||||
const skipTestsNote = feature.skipTests
|
||||
? `\n**⚠️ IMPORTANT - Manual Testing Mode:**\nThis feature has skipTests=true, which means:\n- DO NOT commit changes automatically\n- DO NOT mark as verified - it will automatically go to "waiting_approval" status\n- The user will manually review and commit the changes\n- Just implement the feature and mark it as verified (it will be converted to waiting_approval)\n`
|
||||
: "";
|
||||
|
||||
let imagesNote = "";
|
||||
if (feature.imagePaths && feature.imagePaths.length > 0) {
|
||||
const imagesList = feature.imagePaths.map((img, idx) =>
|
||||
` ${idx + 1}. ${img.filename} (${img.mimeType})\n Path: ${img.path}`
|
||||
).join("\n");
|
||||
const imagesList = feature.imagePaths
|
||||
.map(
|
||||
(img, idx) =>
|
||||
` ${idx + 1}. ${img.filename} (${img.mimeType})\n Path: ${
|
||||
img.path
|
||||
}`
|
||||
)
|
||||
.join("\n");
|
||||
|
||||
imagesNote = `\n**📎 Context Images Attached:**\nThe user has attached ${feature.imagePaths.length} image(s) for context. These images are provided both visually (in the initial message) and as files you can read:
|
||||
|
||||
@@ -143,15 +172,33 @@ ${imagesList}
|
||||
You can use the Read tool to view these images at any time during implementation. Review them carefully before implementing.\n`;
|
||||
}
|
||||
|
||||
// Get context files preview
|
||||
const contextFilesPreview = await contextManager.getContextFilesPreview(
|
||||
projectPath
|
||||
);
|
||||
|
||||
// Get memory content (lessons learned from previous runs)
|
||||
const memoryContent = await contextManager.getMemoryContent(projectPath);
|
||||
|
||||
// Build mode header for this feature
|
||||
const modeHeader = feature.skipTests
|
||||
? `**🔨 MODE: Manual Review (No Automated Tests)**
|
||||
This feature is set for manual review - focus on completing implementation without automated tests.`
|
||||
: `**🧪 MODE: Test-Driven Development (TDD)**
|
||||
This feature requires automated Playwright tests to verify the implementation.`;
|
||||
|
||||
return `You are implementing and verifying a feature until it is complete and working correctly.
|
||||
|
||||
${modeHeader}
|
||||
${memoryContent}
|
||||
|
||||
**Feature to Implement/Verify:**
|
||||
|
||||
ID: ${feature.id}
|
||||
Category: ${feature.category}
|
||||
Description: ${feature.description}
|
||||
Current Status: ${feature.status}
|
||||
${skipTestsNote}${imagesNote}
|
||||
${skipTestsNote}${imagesNote}${contextFilesPreview}
|
||||
**Steps that should be implemented:**
|
||||
${feature.steps.map((step, i) => `${i + 1}. ${step}`).join("\n")}
|
||||
|
||||
@@ -237,7 +284,7 @@ Begin by reading the project structure and understanding what needs to be implem
|
||||
/**
|
||||
* Build prompt for resuming feature with previous context
|
||||
*/
|
||||
buildResumePrompt(feature, previousContext) {
|
||||
async buildResumePrompt(feature, previousContext, projectPath) {
|
||||
const skipTestsNote = feature.skipTests
|
||||
? `\n**⚠️ IMPORTANT - Manual Testing Mode:**\nThis feature has skipTests=true, which means:\n- DO NOT commit changes automatically\n- DO NOT mark as verified - it will automatically go to "waiting_approval" status\n- The user will manually review and commit the changes\n- Just implement the feature and mark it as verified (it will be converted to waiting_approval)\n`
|
||||
: "";
|
||||
@@ -246,13 +293,18 @@ Begin by reading the project structure and understanding what needs to be implem
|
||||
const imagePaths = feature.followUpImages || feature.imagePaths;
|
||||
let imagesNote = "";
|
||||
if (imagePaths && imagePaths.length > 0) {
|
||||
const imagesList = imagePaths.map((img, idx) => {
|
||||
// Handle both FeatureImagePath objects and simple path strings
|
||||
const path = typeof img === 'string' ? img : img.path;
|
||||
const filename = typeof img === 'string' ? path.split('/').pop() : img.filename;
|
||||
const mimeType = typeof img === 'string' ? 'image/*' : img.mimeType;
|
||||
return ` ${idx + 1}. ${filename} (${mimeType})\n Path: ${path}`;
|
||||
}).join("\n");
|
||||
const imagesList = imagePaths
|
||||
.map((img, idx) => {
|
||||
// Handle both FeatureImagePath objects and simple path strings
|
||||
const path = typeof img === "string" ? img : img.path;
|
||||
const filename =
|
||||
typeof img === "string" ? path.split("/").pop() : img.filename;
|
||||
const mimeType = typeof img === "string" ? "image/*" : img.mimeType;
|
||||
return ` ${
|
||||
idx + 1
|
||||
}. ${filename} (${mimeType})\n Path: ${path}`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
imagesNote = `\n**📎 Context Images Attached:**\nThe user has attached ${imagePaths.length} image(s) for context. These images are provided both visually (in the initial message) and as files you can read:
|
||||
|
||||
@@ -261,14 +313,31 @@ ${imagesList}
|
||||
You can use the Read tool to view these images at any time. Review them carefully.\n`;
|
||||
}
|
||||
|
||||
// Get context files preview
|
||||
const contextFilesPreview = await contextManager.getContextFilesPreview(
|
||||
projectPath
|
||||
);
|
||||
|
||||
// Get memory content (lessons learned from previous runs)
|
||||
const memoryContent = await contextManager.getMemoryContent(projectPath);
|
||||
|
||||
// Build mode header for this feature
|
||||
const modeHeader = feature.skipTests
|
||||
? `**🔨 MODE: Manual Review (No Automated Tests)**
|
||||
This feature is set for manual review - focus on clean implementation without automated tests.`
|
||||
: `**🧪 MODE: Test-Driven Development (TDD)**
|
||||
This feature requires automated Playwright tests to verify the implementation.`;
|
||||
|
||||
return `You are resuming work on a feature implementation that was previously started.
|
||||
|
||||
${modeHeader}
|
||||
${memoryContent}
|
||||
**Current Feature:**
|
||||
|
||||
ID: ${feature.id}
|
||||
Category: ${feature.category}
|
||||
Description: ${feature.description}
|
||||
${skipTestsNote}${imagesNote}
|
||||
${skipTestsNote}${imagesNote}${contextFilesPreview}
|
||||
**Steps to Complete:**
|
||||
${feature.steps.map((step, i) => `${i + 1}. ${step}`).join("\n")}
|
||||
|
||||
@@ -460,10 +529,40 @@ Begin by exploring the project structure.`;
|
||||
|
||||
/**
|
||||
* Get the system prompt for coding agent
|
||||
* @param {string} projectPath - Path to the project
|
||||
* @param {boolean} isTDD - Whether this is Test-Driven Development mode (skipTests=false)
|
||||
*/
|
||||
getCodingPrompt() {
|
||||
async getCodingPrompt(projectPath, isTDD = true) {
|
||||
// Get context files preview
|
||||
const contextFilesPreview = projectPath
|
||||
? await contextManager.getContextFilesPreview(projectPath)
|
||||
: "";
|
||||
|
||||
// Get memory content (lessons learned from previous runs)
|
||||
const memoryContent = projectPath
|
||||
? await contextManager.getMemoryContent(projectPath)
|
||||
: "";
|
||||
|
||||
// Build mode-specific instructions
|
||||
const modeHeader = isTDD
|
||||
? `**🧪 MODE: Test-Driven Development (TDD)**
|
||||
You are implementing features using TDD methodology. This means:
|
||||
- Write Playwright tests BEFORE or alongside implementation
|
||||
- Run tests frequently to verify your work
|
||||
- Tests are your validation mechanism
|
||||
- Delete tests after they pass (they're for immediate verification only)`
|
||||
: `**🔨 MODE: Manual Review (No Automated Tests)**
|
||||
You are implementing features for manual user review. This means:
|
||||
- Focus on clean, working implementation
|
||||
- NO automated test writing required
|
||||
- User will manually verify the implementation
|
||||
- DO NOT commit changes - user will review and commit`;
|
||||
|
||||
return `You are an AI coding agent working autonomously to implement features.
|
||||
|
||||
${modeHeader}
|
||||
${memoryContent}
|
||||
|
||||
**🚨 CRITICAL FILE PROTECTION - READ THIS FIRST 🚨**
|
||||
|
||||
THE FOLLOWING FILE IS ABSOLUTELY FORBIDDEN FROM DIRECT MODIFICATION:
|
||||
@@ -486,6 +585,8 @@ Directly modifying feature_list.json can:
|
||||
**THE ONLY WAY to update features:**
|
||||
Use the mcp__automaker-tools__UpdateFeatureStatus tool with featureId, status, and summary parameters.
|
||||
|
||||
${contextFilesPreview}
|
||||
|
||||
Your role is to:
|
||||
- Implement features exactly as specified
|
||||
- Write production-quality code
|
||||
@@ -547,15 +648,62 @@ You have full access to:
|
||||
- Search and analyze the codebase
|
||||
- **UpdateFeatureStatus tool** (mcp__automaker-tools__UpdateFeatureStatus) - Use this to update feature status
|
||||
|
||||
**🧠 Learning from Errors - Memory System:**
|
||||
|
||||
If you encounter an error or issue that:
|
||||
- Took multiple attempts to debug
|
||||
- Was caused by a non-obvious codebase quirk
|
||||
- Required understanding something specific about this project
|
||||
- Could trip up future agent runs
|
||||
|
||||
**ADD IT TO MEMORY** by appending to \`.automaker/memory.md\`:
|
||||
|
||||
\`\`\`markdown
|
||||
### Issue: [Brief Title]
|
||||
**Problem:** [1-2 sentence description of the issue]
|
||||
**Fix:** [Concise explanation of the solution]
|
||||
\`\`\`
|
||||
|
||||
Keep entries concise - focus on the essential information needed to avoid the issue in the future. This helps both you and other agents learn from mistakes.
|
||||
|
||||
Focus on one feature at a time and complete it fully before finishing. Always delete tests after they pass and use the UpdateFeatureStatus tool.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system prompt for verification agent
|
||||
* @param {string} projectPath - Path to the project
|
||||
* @param {boolean} isTDD - Whether this is Test-Driven Development mode (skipTests=false)
|
||||
*/
|
||||
getVerificationPrompt() {
|
||||
async getVerificationPrompt(projectPath, isTDD = true) {
|
||||
// Get context files preview
|
||||
const contextFilesPreview = projectPath
|
||||
? await contextManager.getContextFilesPreview(projectPath)
|
||||
: "";
|
||||
|
||||
// Get memory content (lessons learned from previous runs)
|
||||
const memoryContent = projectPath
|
||||
? await contextManager.getMemoryContent(projectPath)
|
||||
: "";
|
||||
|
||||
// Build mode-specific instructions
|
||||
const modeHeader = isTDD
|
||||
? `**🧪 MODE: Test-Driven Development (TDD)**
|
||||
You are verifying/completing features using TDD methodology. This means:
|
||||
- Run Playwright tests to verify implementation
|
||||
- Fix failing tests by updating code
|
||||
- Tests are your validation mechanism
|
||||
- Delete tests after they pass (they're for immediate verification only)`
|
||||
: `**🔨 MODE: Manual Review (No Automated Tests)**
|
||||
You are completing features for manual user review. This means:
|
||||
- Focus on clean, working implementation
|
||||
- NO automated test writing required
|
||||
- User will manually verify the implementation
|
||||
- DO NOT commit changes - user will review and commit`;
|
||||
|
||||
return `You are an AI implementation and verification agent focused on completing features and ensuring they work.
|
||||
|
||||
${modeHeader}
|
||||
${memoryContent}
|
||||
**🚨 CRITICAL FILE PROTECTION - READ THIS FIRST 🚨**
|
||||
|
||||
THE FOLLOWING FILE IS ABSOLUTELY FORBIDDEN FROM DIRECT MODIFICATION:
|
||||
@@ -578,6 +726,8 @@ Directly modifying feature_list.json can:
|
||||
**THE ONLY WAY to update features:**
|
||||
Use the mcp__automaker-tools__UpdateFeatureStatus tool with featureId, status, and summary parameters.
|
||||
|
||||
${contextFilesPreview}
|
||||
|
||||
Your role is to:
|
||||
- **Continue implementing features until they are complete** - don't stop at the first failure
|
||||
- Check if feature.skipTests is true - if so, skip automated testing and don't commit
|
||||
@@ -638,6 +788,24 @@ You have access to:
|
||||
- Make git commits
|
||||
- **UpdateFeatureStatus tool** (mcp__automaker-tools__UpdateFeatureStatus) - Use this to update feature status
|
||||
|
||||
**🧠 Learning from Errors - Memory System:**
|
||||
|
||||
If you encounter an error or issue that:
|
||||
- Took multiple attempts to debug
|
||||
- Was caused by a non-obvious codebase quirk
|
||||
- Required understanding something specific about this project
|
||||
- Could trip up future agent runs
|
||||
|
||||
**ADD IT TO MEMORY** by appending to \`.automaker/memory.md\`:
|
||||
|
||||
\`\`\`markdown
|
||||
### Issue: [Brief Title]
|
||||
**Problem:** [1-2 sentence description of the issue]
|
||||
**Fix:** [Concise explanation of the solution]
|
||||
\`\`\`
|
||||
|
||||
Keep entries concise - focus on the essential information needed to avoid the issue in the future. This helps both you and other agents learn from mistakes.
|
||||
|
||||
**CRITICAL:** Be persistent and thorough - keep iterating on the implementation until all tests pass. Don't give up after the first failure. Always delete tests after they pass, use the UpdateFeatureStatus tool with a summary, and commit your work.`;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function RootLayout({
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
<Toaster richColors position="top-right" />
|
||||
<Toaster richColors position="bottom-right" />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -699,7 +699,7 @@ export function Sidebar() {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowTrashDialog(true)}
|
||||
className="group flex items-center justify-center px-3 py-2.5 rounded-lg relative overflow-hidden transition-all text-muted-foreground hover:text-primary hover:bg-destructive/10 border border-sidebar-border"
|
||||
className="group flex items-center justify-center px-3 h-[42px] rounded-lg relative overflow-hidden transition-all text-muted-foreground hover:text-primary hover:bg-destructive/10 border border-sidebar-border"
|
||||
title="Trash"
|
||||
data-testid="trash-button"
|
||||
>
|
||||
@@ -778,7 +778,7 @@ export function Sidebar() {
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
className="hidden lg:flex items-center justify-center w-8 h-8 rounded-lg text-muted-foreground hover:text-foreground hover:bg-sidebar-accent/50 border border-sidebar-border transition-all titlebar-no-drag"
|
||||
className="hidden lg:flex items-center justify-center w-8 h-[42px] rounded-lg text-muted-foreground hover:text-foreground hover:bg-sidebar-accent/50 border border-sidebar-border transition-all titlebar-no-drag"
|
||||
title="Project history"
|
||||
data-testid="project-history-menu"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user