diff --git a/apps/server/src/routes/suggestions/generate-suggestions.ts b/apps/server/src/routes/suggestions/generate-suggestions.ts index c8000ce5..8362ab2f 100644 --- a/apps/server/src/routes/suggestions/generate-suggestions.ts +++ b/apps/server/src/routes/suggestions/generate-suggestions.ts @@ -6,9 +6,87 @@ import { query } from '@anthropic-ai/claude-agent-sdk'; import type { EventEmitter } from '../../lib/events.js'; import { createLogger } from '@automaker/utils'; import { createSuggestionsOptions } from '../../lib/sdk-options.js'; +import { FeatureLoader } from '../../services/feature-loader.js'; +import { getAppSpecPath } from '@automaker/platform'; +import * as secureFs from '../../lib/secure-fs.js'; const logger = createLogger('Suggestions'); +/** + * Extract implemented features from app_spec.txt XML content + */ +function extractImplementedFeatures(specContent: string): string[] { + const features: string[] = []; + + // Match ... section + const implementedMatch = specContent.match( + /([\s\S]*?)<\/implemented_features>/ + ); + + if (implementedMatch) { + const implementedSection = implementedMatch[1]; + + // Extract feature names from ... tags + const nameRegex = /(.*?)<\/name>/g; + let match; + + while ((match = nameRegex.exec(implementedSection)) !== null) { + features.push(match[1].trim()); + } + } + + return features; +} + +/** + * Load existing context (app spec and backlog features) to avoid duplicates + */ +async function loadExistingContext(projectPath: string): Promise { + let context = ''; + + // 1. Read app_spec.txt for implemented features + try { + const appSpecPath = getAppSpecPath(projectPath); + const specContent = (await secureFs.readFile(appSpecPath, 'utf-8')) as string; + + if (specContent && specContent.trim().length > 0) { + const implementedFeatures = extractImplementedFeatures(specContent); + + if (implementedFeatures.length > 0) { + context += '\n\n=== ALREADY IMPLEMENTED FEATURES ===\n'; + context += 'These features are already implemented in the codebase:\n'; + implementedFeatures.forEach((feature) => { + context += `- ${feature}\n`; + }); + } + } + } catch (error) { + // app_spec.txt doesn't exist or can't be read - that's okay + logger.debug('No app_spec.txt found or error reading it:', error); + } + + // 2. Load existing features from backlog + try { + const featureLoader = new FeatureLoader(); + const features = await featureLoader.getAll(projectPath); + + if (features.length > 0) { + context += '\n\n=== EXISTING FEATURES IN BACKLOG ===\n'; + context += 'These features are already planned or in progress:\n'; + features.forEach((feature) => { + const status = feature.status || 'pending'; + const title = feature.title || feature.description.substring(0, 50); + context += `- ${title} (${status})\n`; + }); + } + } catch (error) { + // Features directory doesn't exist or can't be read - that's okay + logger.debug('No features found or error loading them:', error); + } + + return context; +} + /** * JSON Schema for suggestions output */ @@ -51,8 +129,13 @@ export async function generateSuggestions( performance: 'Analyze this project for performance issues and suggest optimizations.', }; - const prompt = `${typePrompts[suggestionType] || typePrompts.features} + // Load existing context to avoid duplicates + const existingContext = await loadExistingContext(projectPath); + const prompt = `${typePrompts[suggestionType] || typePrompts.features} +${existingContext} + +${existingContext ? '\nIMPORTANT: Do NOT suggest features that are already implemented or already in the backlog above. Focus on NEW ideas that complement what already exists.\n' : ''} Look at the codebase and provide 3-5 concrete suggestions. For each suggestion, provide: