Overhaul updates for appspec & feature generation. Added detail back to kanban feature creation

This commit is contained in:
trueheads
2025-12-11 03:40:16 -06:00
parent c198c10244
commit 8d6f825c5c
4 changed files with 115 additions and 42 deletions

View File

@@ -384,20 +384,30 @@ class FeatureLoader {
* @param {string} projectPath - Path to the project * @param {string} projectPath - Path to the project
* @param {string} [summary] - Optional summary of what was done * @param {string} [summary] - Optional summary of what was done
* @param {string} [error] - Optional error message if feature errored * @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
*/ */
async updateFeatureStatus(featureId, status, projectPath, summary, error) { async updateFeatureStatus(featureId, status, projectPath, summary, error, description, category, steps) {
// Check if feature exists // Check if feature exists
const existingFeature = await this.get(projectPath, featureId); const existingFeature = await this.get(projectPath, featureId);
if (!existingFeature) { if (!existingFeature) {
// Feature doesn't exist - create it // Feature doesn't exist - create it with all required fields
console.log(`[FeatureLoader] Feature ${featureId} not found - creating new feature`); console.log(`[FeatureLoader] Feature ${featureId} not found - creating new feature`);
const newFeature = { const newFeature = {
id: featureId, id: featureId,
title: featureId.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' '), title: featureId.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' '),
description: summary || '', // Use summary as description for display description: description || summary || '', // Use provided description, fall back to summary
category: category || "Uncategorized",
steps: steps || [],
status: status, status: status,
summary: summary || '', images: [],
imagePaths: [],
skipTests: true,
model: "sonnet",
thinkingLevel: "none",
summary: summary || description || '',
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}; };
if (error !== undefined) { if (error !== undefined) {
@@ -405,7 +415,7 @@ class FeatureLoader {
} }
await this.create(projectPath, newFeature); await this.create(projectPath, newFeature);
console.log( console.log(
`[FeatureLoader] Created feature ${featureId}: status=${status}${ `[FeatureLoader] Created feature ${featureId}: status=${status}, category=${category || "Uncategorized"}, steps=${steps?.length || 0}${
summary ? `, summary="${summary}"` : "" summary ? `, summary="${summary}"` : ""
}` }`
); );
@@ -421,6 +431,15 @@ class FeatureLoader {
updates.description = summary; updates.description = summary;
} }
} }
if (description !== undefined) {
updates.description = description;
}
if (category !== undefined) {
updates.category = category;
}
if (steps !== undefined && Array.isArray(steps)) {
updates.steps = steps;
}
if (error !== undefined) { if (error !== undefined) {
updates.error = error; updates.error = error;
} else { } else {
@@ -429,10 +448,21 @@ class FeatureLoader {
updates.error = undefined; updates.error = undefined;
} }
} }
// Ensure required fields exist (for features created before this fix)
if (!existingFeature.category && !updates.category) updates.category = "Uncategorized";
if (!existingFeature.steps && !updates.steps) updates.steps = [];
if (!existingFeature.images) updates.images = [];
if (!existingFeature.imagePaths) updates.imagePaths = [];
if (existingFeature.skipTests === undefined) updates.skipTests = true;
if (!existingFeature.model) updates.model = "sonnet";
if (!existingFeature.thinkingLevel) updates.thinkingLevel = "none";
await this.update(projectPath, featureId, updates); await this.update(projectPath, featureId, updates);
console.log( console.log(
`[FeatureLoader] Updated feature ${featureId}: status=${status}${ `[FeatureLoader] Updated feature ${featureId}: status=${status}${
category ? `, category="${category}"` : ""
}${steps ? `, steps=${steps.length}` : ""}${
summary ? `, summary="${summary}"` : "" summary ? `, summary="${summary}"` : ""
}` }`
); );

View File

@@ -19,15 +19,18 @@ class McpServerFactory {
tools: [ tools: [
tool( tool(
"UpdateFeatureStatus", "UpdateFeatureStatus",
"Update the status of a feature. Use this tool instead of directly modifying feature files to safely update feature status. IMPORTANT: If the feature has skipTests=true, you should NOT mark it as verified - instead it will automatically go to waiting_approval status for manual review. Always include a summary of what was done.", "Create or update a feature. Use this tool to create new features with detailed information or update existing feature status. When creating features, provide comprehensive description, category, and implementation steps.",
{ {
featureId: z.string().describe("The ID of the feature to update"), featureId: z.string().describe("The ID of the feature (lowercase, hyphens for spaces). Example: 'user-authentication', 'budget-tracking'"),
status: z.enum(["backlog", "in_progress", "verified"]).describe("The new status for the feature. Note: If skipTests=true, verified will be converted to waiting_approval automatically."), status: z.enum(["backlog", "todo", "in_progress", "verified"]).describe("The status for the feature. Use 'backlog' or 'todo' for new features."),
summary: z.string().optional().describe("A brief summary of what was implemented/changed. This will be displayed on the Kanban card. Example: 'Added dark mode toggle. Modified: settings.tsx, theme-provider.tsx'") summary: z.string().optional().describe("A brief summary of what was implemented/changed or what the feature does."),
description: z.string().optional().describe("A detailed description of the feature. Be comprehensive - explain what the feature does, its purpose, and key functionality."),
category: z.string().optional().describe("The category/phase for this feature. Example: 'Phase 1: Foundation', 'Phase 2: Core Logic', 'Phase 3: Polish', 'Authentication', 'UI/UX'"),
steps: z.array(z.string()).optional().describe("Array of implementation steps. Each step should be a clear, actionable task. Example: ['Set up database schema', 'Create API endpoints', 'Build UI components', 'Add validation']")
}, },
async (args) => { async (args) => {
try { try {
console.log(`[McpServerFactory] UpdateFeatureStatus tool called: featureId=${args.featureId}, status=${args.status}, summary=${args.summary || "(none)"}`); console.log(`[McpServerFactory] UpdateFeatureStatus tool called: featureId=${args.featureId}, status=${args.status}, summary=${args.summary || "(none)"}, category=${args.category || "(none)"}, steps=${args.steps?.length || 0}`);
console.log(`[Feature Creation] Creating/updating feature "${args.featureId}" with status "${args.status}"`); console.log(`[Feature Creation] Creating/updating feature "${args.featureId}" with status "${args.status}"`);
// Load the feature to check skipTests flag // Load the feature to check skipTests flag
@@ -41,17 +44,30 @@ class McpServerFactory {
// If agent tries to mark as verified but feature has skipTests=true, convert to waiting_approval // If agent tries to mark as verified but feature has skipTests=true, convert to waiting_approval
let finalStatus = args.status; let finalStatus = args.status;
// Convert 'todo' to 'backlog' for consistency
if (finalStatus === "todo") {
finalStatus = "backlog";
}
if (feature && args.status === "verified" && feature.skipTests === true) { if (feature && args.status === "verified" && feature.skipTests === true) {
console.log(`[McpServerFactory] Feature ${args.featureId} has skipTests=true, converting verified -> waiting_approval`); console.log(`[McpServerFactory] Feature ${args.featureId} has skipTests=true, converting verified -> waiting_approval`);
finalStatus = "waiting_approval"; finalStatus = "waiting_approval";
} }
// Call the provided callback to update feature status with summary // Call the provided callback to update feature status with all parameters
await updateFeatureStatusCallback(args.featureId, finalStatus, projectPath, args.summary); await updateFeatureStatusCallback(
args.featureId,
finalStatus,
projectPath,
args.summary,
undefined, // error
args.description,
args.category,
args.steps
);
const statusMessage = finalStatus !== args.status const statusMessage = finalStatus !== args.status
? `Successfully updated feature ${args.featureId} to status "${finalStatus}" (converted from "${args.status}" because skipTests=true)${args.summary ? ` with summary: "${args.summary}"` : ""}` ? `Successfully created/updated feature ${args.featureId} to status "${finalStatus}" (converted from "${args.status}")${args.summary ? ` - ${args.summary}` : ""}`
: `Successfully updated feature ${args.featureId} to status "${finalStatus}"${args.summary ? ` with summary: "${args.summary}"` : ""}`; : `Successfully created/updated feature ${args.featureId} to status "${finalStatus}"${args.summary ? ` - ${args.summary}` : ""}`;
console.log(`[Feature Creation] ✓ ${statusMessage}`); console.log(`[Feature Creation] ✓ ${statusMessage}`);

View File

@@ -52,11 +52,11 @@ ${memoryContent}
**Current Feature to Implement:** **Current Feature to Implement:**
ID: ${feature.id} ID: ${feature.id}
Category: ${feature.category} Category: ${feature.category || "Uncategorized"}
Description: ${feature.description} Description: ${feature.description || feature.summary || feature.title || "No description provided"}
${skipTestsNote}${imagesNote}${contextFilesPreview} ${skipTestsNote}${imagesNote}${contextFilesPreview}
**Steps to Complete:** **Steps to Complete:**
${feature.steps.map((step, i) => `${i + 1}. ${step}`).join("\n")} ${(feature.steps || []).map((step, i) => `${i + 1}. ${step}`).join("\n") || "No specific steps provided - implement based on description"}
**Your Task:** **Your Task:**
@@ -195,12 +195,12 @@ ${memoryContent}
**Feature to Implement/Verify:** **Feature to Implement/Verify:**
ID: ${feature.id} ID: ${feature.id}
Category: ${feature.category} Category: ${feature.category || "Uncategorized"}
Description: ${feature.description} Description: ${feature.description || feature.summary || feature.title || "No description provided"}
Current Status: ${feature.status} Current Status: ${feature.status}
${skipTestsNote}${imagesNote}${contextFilesPreview} ${skipTestsNote}${imagesNote}${contextFilesPreview}
**Steps that should be implemented:** **Steps that should be implemented:**
${feature.steps.map((step, i) => `${i + 1}. ${step}`).join("\n")} ${(feature.steps || []).map((step, i) => `${i + 1}. ${step}`).join("\n") || "No specific steps provided - implement based on description"}
**Your Task:** **Your Task:**
@@ -335,11 +335,11 @@ ${memoryContent}
**Current Feature:** **Current Feature:**
ID: ${feature.id} ID: ${feature.id}
Category: ${feature.category} Category: ${feature.category || "Uncategorized"}
Description: ${feature.description} Description: ${feature.description || feature.summary || feature.title || "No description provided"}
${skipTestsNote}${imagesNote}${contextFilesPreview} ${skipTestsNote}${imagesNote}${contextFilesPreview}
**Steps to Complete:** **Steps to Complete:**
${feature.steps.map((step, i) => `${i + 1}. ${step}`).join("\n")} ${(feature.steps || []).map((step, i) => `${i + 1}. ${step}`).join("\n") || "No specific steps provided - implement based on description"}
**Previous Work Context:** **Previous Work Context:**

View File

@@ -368,24 +368,42 @@ class SpecRegenerationService {
const options = { const options = {
model: "claude-sonnet-4-20250514", model: "claude-sonnet-4-20250514",
systemPrompt: `You are a feature management assistant. Your job is to read the app_spec.txt file and create feature entries based on the implementation_roadmap section. systemPrompt: `You are a feature management assistant. Your job is to read the app_spec.txt file and create DETAILED, COMPREHENSIVE feature entries based on the implementation_roadmap section.
**Your Task:** **Your Task:**
1. Read the .automaker/app_spec.txt file 1. Read the .automaker/app_spec.txt file thoroughly
2. Parse the implementation_roadmap section (it contains phases with features listed) 2. Parse the implementation_roadmap section (it contains phases with features listed)
3. For each feature listed in the roadmap, use the UpdateFeatureStatus tool to create a feature entry 3. For EACH feature in the roadmap, use the UpdateFeatureStatus tool to create a detailed feature entry
4. Set the initial status to "todo" for all features 4. Set the initial status to "backlog" for all features
5. Extract a meaningful summary/description for each feature from the roadmap
**IMPORTANT - For each feature you MUST provide:**
- **featureId**: A descriptive ID (lowercase, hyphens for spaces). Example: "user-authentication", "budget-tracking"
- **status**: "backlog" for all new features
- **description**: A DETAILED description (2-4 sentences) explaining what the feature does, its purpose, and key functionality
- **category**: The phase from the roadmap (e.g., "Phase 1: Foundation", "Phase 2: Core Logic", "Phase 3: Polish")
- **steps**: An array of 4-8 clear, actionable implementation steps. Each step should be specific and completable.
- **summary**: A brief one-line summary of the feature
**Example of a well-defined feature:**
{
"featureId": "user-authentication",
"status": "backlog",
"description": "Implement secure user authentication system with email/password login, OAuth integration for Google and Facebook, password reset functionality, and session management. This forms the foundation for all user-specific features.",
"category": "Phase 1: Foundation",
"steps": [
"Set up authentication provider (NextAuth.js or similar)",
"Configure email/password authentication",
"Implement social login (Google, Facebook OAuth)",
"Create login and registration UI components",
"Add password reset flow with email verification",
"Implement session management and token refresh"
],
"summary": "Secure authentication with email/password and social login"
}
**Feature Storage:** **Feature Storage:**
Features are stored in .automaker/features/{id}/feature.json - each feature has its own folder. Features are stored in .automaker/features/{id}/feature.json - each feature has its own folder.
Use the UpdateFeatureStatus tool to create features. The tool will handle creating the directory structure and feature.json file. Use the UpdateFeatureStatus tool to create features with ALL the fields above.`,
**Important:**
- Create features ONLY from the implementation_roadmap section
- Use the UpdateFeatureStatus tool for each feature
- Set status to "todo" initially
- Use a descriptive featureId based on the feature name (lowercase, hyphens for spaces)`,
maxTurns: 50, maxTurns: 50,
cwd: projectPath, cwd: projectPath,
mcpServers: { mcpServers: {
@@ -400,15 +418,24 @@ Use the UpdateFeatureStatus tool to create features. The tool will handle creati
abortController: abortController, abortController: abortController,
}; };
const prompt = `Please read the .automaker/app_spec.txt file and create feature entries for all features listed in the implementation_roadmap section. const prompt = `Please read the .automaker/app_spec.txt file and create DETAILED feature entries for ALL features listed in the implementation_roadmap section.
For each feature in the roadmap: **Your workflow:**
1. Use the UpdateFeatureStatus tool to create the feature 1. Read the app_spec.txt file completely
2. Set status to "todo" 2. Identify ALL features from the implementation_roadmap section
3. Provide a clear summary/description based on what's in the roadmap 3. For EACH feature, call UpdateFeatureStatus with ALL required fields:
4. Use a featureId that's descriptive and follows the pattern: lowercase with hyphens (e.g., "user-authentication", "payment-processing")
Start by reading the app_spec.txt file to see the implementation roadmap.`; **Required for each UpdateFeatureStatus call:**
- featureId: Descriptive ID (lowercase, hyphens). Example: "user-authentication"
- status: "backlog"
- description: 2-4 sentences explaining the feature in detail
- category: The phase name (e.g., "Phase 1: Foundation", "Phase 2: Core Logic")
- steps: Array of 4-8 specific implementation steps
- summary: One-line summary
**Do NOT create features with just a summary - each feature needs description, category, AND steps.**
Start by reading the app_spec.txt file, then create each feature with full detail.`;
const currentQuery = query({ prompt, options }); const currentQuery = query({ prompt, options });
execution.query = currentQuery; execution.query = currentQuery;