mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-05 09:33:07 +00:00
feat: enhance ideation routes with event handling and new suggestion feature
- Updated the ideation routes to include an EventEmitter for better event management. - Added a new endpoint to handle adding suggestions to the board, ensuring consistent category mapping. - Modified existing routes to emit events for idea creation, update, and deletion, improving frontend notifications. - Refactored the convert and create idea handlers to utilize the new event system. - Removed static guided prompts data in favor of dynamic fetching from the backend API.
This commit is contained in:
@@ -218,7 +218,7 @@ app.use('/api/context', createContextRoutes(settingsService));
|
|||||||
app.use('/api/backlog-plan', createBacklogPlanRoutes(events, settingsService));
|
app.use('/api/backlog-plan', createBacklogPlanRoutes(events, settingsService));
|
||||||
app.use('/api/mcp', createMCPRoutes(mcpTestService));
|
app.use('/api/mcp', createMCPRoutes(mcpTestService));
|
||||||
app.use('/api/pipeline', createPipelineRoutes(pipelineService));
|
app.use('/api/pipeline', createPipelineRoutes(pipelineService));
|
||||||
app.use('/api/ideation', createIdeationRoutes(ideationService, featureLoader));
|
app.use('/api/ideation', createIdeationRoutes(events, ideationService, featureLoader));
|
||||||
|
|
||||||
// Create HTTP server
|
// Create HTTP server
|
||||||
const server = createServer(app);
|
const server = createServer(app);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
|
import type { EventEmitter } from '../../lib/events.js';
|
||||||
import { validatePathParams } from '../../middleware/validate-paths.js';
|
import { validatePathParams } from '../../middleware/validate-paths.js';
|
||||||
import type { IdeationService } from '../../services/ideation-service.js';
|
import type { IdeationService } from '../../services/ideation-service.js';
|
||||||
import type { FeatureLoader } from '../../services/feature-loader.js';
|
import type { FeatureLoader } from '../../services/feature-loader.js';
|
||||||
@@ -19,10 +20,12 @@ import { createIdeasUpdateHandler } from './routes/ideas-update.js';
|
|||||||
import { createIdeasDeleteHandler } from './routes/ideas-delete.js';
|
import { createIdeasDeleteHandler } from './routes/ideas-delete.js';
|
||||||
import { createAnalyzeHandler, createGetAnalysisHandler } from './routes/analyze.js';
|
import { createAnalyzeHandler, createGetAnalysisHandler } from './routes/analyze.js';
|
||||||
import { createConvertHandler } from './routes/convert.js';
|
import { createConvertHandler } from './routes/convert.js';
|
||||||
|
import { createAddSuggestionHandler } from './routes/add-suggestion.js';
|
||||||
import { createPromptsHandler, createPromptsByCategoryHandler } from './routes/prompts.js';
|
import { createPromptsHandler, createPromptsByCategoryHandler } from './routes/prompts.js';
|
||||||
import { createSuggestionsGenerateHandler } from './routes/suggestions-generate.js';
|
import { createSuggestionsGenerateHandler } from './routes/suggestions-generate.js';
|
||||||
|
|
||||||
export function createIdeationRoutes(
|
export function createIdeationRoutes(
|
||||||
|
events: EventEmitter,
|
||||||
ideationService: IdeationService,
|
ideationService: IdeationService,
|
||||||
featureLoader: FeatureLoader
|
featureLoader: FeatureLoader
|
||||||
): Router {
|
): Router {
|
||||||
@@ -35,7 +38,7 @@ export function createIdeationRoutes(
|
|||||||
createSessionStartHandler(ideationService)
|
createSessionStartHandler(ideationService)
|
||||||
);
|
);
|
||||||
router.post('/session/message', createSessionMessageHandler(ideationService));
|
router.post('/session/message', createSessionMessageHandler(ideationService));
|
||||||
router.post('/session/stop', createSessionStopHandler(ideationService));
|
router.post('/session/stop', createSessionStopHandler(events, ideationService));
|
||||||
router.post(
|
router.post(
|
||||||
'/session/get',
|
'/session/get',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
@@ -51,7 +54,7 @@ export function createIdeationRoutes(
|
|||||||
router.post(
|
router.post(
|
||||||
'/ideas/create',
|
'/ideas/create',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createIdeasCreateHandler(ideationService)
|
createIdeasCreateHandler(events, ideationService)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/ideas/get',
|
'/ideas/get',
|
||||||
@@ -61,12 +64,12 @@ export function createIdeationRoutes(
|
|||||||
router.post(
|
router.post(
|
||||||
'/ideas/update',
|
'/ideas/update',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createIdeasUpdateHandler(ideationService)
|
createIdeasUpdateHandler(events, ideationService)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/ideas/delete',
|
'/ideas/delete',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createIdeasDeleteHandler(ideationService)
|
createIdeasDeleteHandler(events, ideationService)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Project analysis
|
// Project analysis
|
||||||
@@ -81,7 +84,14 @@ export function createIdeationRoutes(
|
|||||||
router.post(
|
router.post(
|
||||||
'/convert',
|
'/convert',
|
||||||
validatePathParams('projectPath'),
|
validatePathParams('projectPath'),
|
||||||
createConvertHandler(ideationService, featureLoader)
|
createConvertHandler(events, ideationService, featureLoader)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add suggestion to board as a feature
|
||||||
|
router.post(
|
||||||
|
'/add-suggestion',
|
||||||
|
validatePathParams('projectPath'),
|
||||||
|
createAddSuggestionHandler(ideationService, featureLoader)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Guided prompts (no validation needed - static data)
|
// Guided prompts (no validation needed - static data)
|
||||||
|
|||||||
70
apps/server/src/routes/ideation/routes/add-suggestion.ts
Normal file
70
apps/server/src/routes/ideation/routes/add-suggestion.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* POST /add-suggestion - Add an analysis suggestion to the board as a feature
|
||||||
|
*
|
||||||
|
* This endpoint converts an AnalysisSuggestion to a Feature using the
|
||||||
|
* IdeationService's mapIdeaCategoryToFeatureCategory for consistent category mapping.
|
||||||
|
* This ensures a single source of truth for the conversion logic.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Request, Response } from 'express';
|
||||||
|
import type { IdeationService } from '../../../services/ideation-service.js';
|
||||||
|
import type { FeatureLoader } from '../../../services/feature-loader.js';
|
||||||
|
import type { AnalysisSuggestion } from '@automaker/types';
|
||||||
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
|
export function createAddSuggestionHandler(
|
||||||
|
ideationService: IdeationService,
|
||||||
|
featureLoader: FeatureLoader
|
||||||
|
) {
|
||||||
|
return async (req: Request, res: Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const { projectPath, suggestion } = req.body as {
|
||||||
|
projectPath: string;
|
||||||
|
suggestion: AnalysisSuggestion;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!projectPath) {
|
||||||
|
res.status(400).json({ success: false, error: 'projectPath is required' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!suggestion) {
|
||||||
|
res.status(400).json({ success: false, error: 'suggestion is required' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!suggestion.title) {
|
||||||
|
res.status(400).json({ success: false, error: 'suggestion.title is required' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!suggestion.category) {
|
||||||
|
res.status(400).json({ success: false, error: 'suggestion.category is required' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build description with rationale if provided
|
||||||
|
const description = suggestion.rationale
|
||||||
|
? `${suggestion.description}\n\n**Rationale:** ${suggestion.rationale}`
|
||||||
|
: suggestion.description;
|
||||||
|
|
||||||
|
// Use the service's category mapping for consistency
|
||||||
|
const featureCategory = ideationService.mapSuggestionCategoryToFeatureCategory(
|
||||||
|
suggestion.category
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create the feature
|
||||||
|
const feature = await featureLoader.create(projectPath, {
|
||||||
|
title: suggestion.title,
|
||||||
|
description,
|
||||||
|
category: featureCategory,
|
||||||
|
status: 'backlog',
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true, featureId: feature.id });
|
||||||
|
} catch (error) {
|
||||||
|
logError(error, 'Add suggestion to board failed');
|
||||||
|
res.status(500).json({ success: false, error: getErrorMessage(error) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -3,12 +3,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
import type { EventEmitter } from '../../../lib/events.js';
|
||||||
import type { IdeationService } from '../../../services/ideation-service.js';
|
import type { IdeationService } from '../../../services/ideation-service.js';
|
||||||
import type { FeatureLoader } from '../../../services/feature-loader.js';
|
import type { FeatureLoader } from '../../../services/feature-loader.js';
|
||||||
import type { ConvertToFeatureOptions } from '@automaker/types';
|
import type { ConvertToFeatureOptions } from '@automaker/types';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
export function createConvertHandler(
|
export function createConvertHandler(
|
||||||
|
events: EventEmitter,
|
||||||
ideationService: IdeationService,
|
ideationService: IdeationService,
|
||||||
featureLoader: FeatureLoader
|
featureLoader: FeatureLoader
|
||||||
) {
|
) {
|
||||||
@@ -49,8 +51,22 @@ export function createConvertHandler(
|
|||||||
// Delete the idea unless keepIdea is explicitly true
|
// Delete the idea unless keepIdea is explicitly true
|
||||||
if (!keepIdea) {
|
if (!keepIdea) {
|
||||||
await ideationService.deleteIdea(projectPath, ideaId);
|
await ideationService.deleteIdea(projectPath, ideaId);
|
||||||
|
|
||||||
|
// Emit idea deleted event
|
||||||
|
events.emit('ideation:idea-deleted', {
|
||||||
|
projectPath,
|
||||||
|
ideaId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit idea converted event to notify frontend
|
||||||
|
events.emit('ideation:idea-converted', {
|
||||||
|
projectPath,
|
||||||
|
ideaId,
|
||||||
|
featureId: feature.id,
|
||||||
|
keepIdea: !!keepIdea,
|
||||||
|
});
|
||||||
|
|
||||||
// Return featureId as expected by the frontend API interface
|
// Return featureId as expected by the frontend API interface
|
||||||
res.json({ success: true, featureId: feature.id });
|
res.json({ success: true, featureId: feature.id });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
import type { EventEmitter } from '../../../lib/events.js';
|
||||||
import type { IdeationService } from '../../../services/ideation-service.js';
|
import type { IdeationService } from '../../../services/ideation-service.js';
|
||||||
import type { CreateIdeaInput } from '@automaker/types';
|
import type { CreateIdeaInput } from '@automaker/types';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
export function createIdeasCreateHandler(ideationService: IdeationService) {
|
export function createIdeasCreateHandler(events: EventEmitter, ideationService: IdeationService) {
|
||||||
return async (req: Request, res: Response): Promise<void> => {
|
return async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { projectPath, idea } = req.body as {
|
const { projectPath, idea } = req.body as {
|
||||||
@@ -34,6 +35,13 @@ export function createIdeasCreateHandler(ideationService: IdeationService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const created = await ideationService.createIdea(projectPath, idea);
|
const created = await ideationService.createIdea(projectPath, idea);
|
||||||
|
|
||||||
|
// Emit idea created event for frontend notification
|
||||||
|
events.emit('ideation:idea-created', {
|
||||||
|
projectPath,
|
||||||
|
idea: created,
|
||||||
|
});
|
||||||
|
|
||||||
res.json({ success: true, idea: created });
|
res.json({ success: true, idea: created });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, 'Create idea failed');
|
logError(error, 'Create idea failed');
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
import type { EventEmitter } from '../../../lib/events.js';
|
||||||
import type { IdeationService } from '../../../services/ideation-service.js';
|
import type { IdeationService } from '../../../services/ideation-service.js';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
export function createIdeasDeleteHandler(ideationService: IdeationService) {
|
export function createIdeasDeleteHandler(events: EventEmitter, ideationService: IdeationService) {
|
||||||
return async (req: Request, res: Response): Promise<void> => {
|
return async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { projectPath, ideaId } = req.body as {
|
const { projectPath, ideaId } = req.body as {
|
||||||
@@ -25,6 +26,13 @@ export function createIdeasDeleteHandler(ideationService: IdeationService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ideationService.deleteIdea(projectPath, ideaId);
|
await ideationService.deleteIdea(projectPath, ideaId);
|
||||||
|
|
||||||
|
// Emit idea deleted event for frontend notification
|
||||||
|
events.emit('ideation:idea-deleted', {
|
||||||
|
projectPath,
|
||||||
|
ideaId,
|
||||||
|
});
|
||||||
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, 'Delete idea failed');
|
logError(error, 'Delete idea failed');
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
import type { EventEmitter } from '../../../lib/events.js';
|
||||||
import type { IdeationService } from '../../../services/ideation-service.js';
|
import type { IdeationService } from '../../../services/ideation-service.js';
|
||||||
import type { UpdateIdeaInput } from '@automaker/types';
|
import type { UpdateIdeaInput } from '@automaker/types';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
export function createIdeasUpdateHandler(ideationService: IdeationService) {
|
export function createIdeasUpdateHandler(events: EventEmitter, ideationService: IdeationService) {
|
||||||
return async (req: Request, res: Response): Promise<void> => {
|
return async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { projectPath, ideaId, updates } = req.body as {
|
const { projectPath, ideaId, updates } = req.body as {
|
||||||
@@ -37,6 +38,13 @@ export function createIdeasUpdateHandler(ideationService: IdeationService) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit idea updated event for frontend notification
|
||||||
|
events.emit('ideation:idea-updated', {
|
||||||
|
projectPath,
|
||||||
|
ideaId,
|
||||||
|
idea,
|
||||||
|
});
|
||||||
|
|
||||||
res.json({ success: true, idea });
|
res.json({ success: true, idea });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, 'Update idea failed');
|
logError(error, 'Update idea failed');
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function createPromptsByCategoryHandler(ideationService: IdeationService)
|
|||||||
try {
|
try {
|
||||||
const { category } = req.params as { category: string };
|
const { category } = req.params as { category: string };
|
||||||
|
|
||||||
const validCategories: IdeaCategory[] = ['feature', 'ux-ui', 'dx', 'growth', 'technical'];
|
const validCategories = ideationService.getPromptCategories().map((c) => c.id);
|
||||||
if (!validCategories.includes(category as IdeaCategory)) {
|
if (!validCategories.includes(category as IdeaCategory)) {
|
||||||
res.status(400).json({ success: false, error: 'Invalid category' });
|
res.status(400).json({ success: false, error: 'Invalid category' });
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3,13 +3,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
import type { EventEmitter } from '../../../lib/events.js';
|
||||||
import type { IdeationService } from '../../../services/ideation-service.js';
|
import type { IdeationService } from '../../../services/ideation-service.js';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
export function createSessionStopHandler(ideationService: IdeationService) {
|
export function createSessionStopHandler(events: EventEmitter, ideationService: IdeationService) {
|
||||||
return async (req: Request, res: Response): Promise<void> => {
|
return async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { sessionId } = req.body as { sessionId: string };
|
const { sessionId, projectPath } = req.body as {
|
||||||
|
sessionId: string;
|
||||||
|
projectPath?: string;
|
||||||
|
};
|
||||||
|
|
||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
res.status(400).json({ success: false, error: 'sessionId is required' });
|
res.status(400).json({ success: false, error: 'sessionId is required' });
|
||||||
@@ -17,6 +21,15 @@ export function createSessionStopHandler(ideationService: IdeationService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ideationService.stopSession(sessionId);
|
await ideationService.stopSession(sessionId);
|
||||||
|
|
||||||
|
// Emit session stopped event for frontend notification
|
||||||
|
// Note: The service also emits 'ideation:session-ended' internally,
|
||||||
|
// but we emit here as well for route-level consistency with other routes
|
||||||
|
events.emit('ideation:session-ended', {
|
||||||
|
sessionId,
|
||||||
|
projectPath,
|
||||||
|
});
|
||||||
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(error, 'Stop session failed');
|
logError(error, 'Stop session failed');
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import type { IdeationService } from '../../../services/ideation-service.js';
|
import type { IdeationService } from '../../../services/ideation-service.js';
|
||||||
import { createLogger } from '@automaker/utils';
|
import { createLogger } from '@automaker/utils';
|
||||||
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
const logger = createLogger('ideation:suggestions-generate');
|
const logger = createLogger('ideation:suggestions-generate');
|
||||||
|
|
||||||
@@ -45,10 +46,10 @@ export function createSuggestionsGenerateHandler(ideationService: IdeationServic
|
|||||||
suggestions,
|
suggestions,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to generate suggestions:', error);
|
logError(error, 'Failed to generate suggestions');
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: (error as Error).message,
|
error: getErrorMessage(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import { ProviderFactory } from '../providers/provider-factory.js';
|
|||||||
import type { SettingsService } from './settings-service.js';
|
import type { SettingsService } from './settings-service.js';
|
||||||
import type { FeatureLoader } from './feature-loader.js';
|
import type { FeatureLoader } from './feature-loader.js';
|
||||||
import { createChatOptions, validateWorkingDirectory } from '../lib/sdk-options.js';
|
import { createChatOptions, validateWorkingDirectory } from '../lib/sdk-options.js';
|
||||||
|
import { resolveModelString } from '@automaker/model-resolver';
|
||||||
|
|
||||||
const logger = createLogger('IdeationService');
|
const logger = createLogger('IdeationService');
|
||||||
|
|
||||||
@@ -200,20 +201,22 @@ export class IdeationService {
|
|||||||
existingWorkContext
|
existingWorkContext
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Resolve model alias to canonical identifier
|
||||||
|
const modelId = resolveModelString(options?.model ?? 'sonnet');
|
||||||
|
|
||||||
// Create SDK options
|
// Create SDK options
|
||||||
const sdkOptions = createChatOptions({
|
const sdkOptions = createChatOptions({
|
||||||
cwd: projectPath,
|
cwd: projectPath,
|
||||||
model: options?.model || 'sonnet',
|
model: modelId,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
abortController: activeSession.abortController!,
|
abortController: activeSession.abortController!,
|
||||||
});
|
});
|
||||||
|
|
||||||
const effectiveModel = sdkOptions.model!;
|
const provider = ProviderFactory.getProviderForModel(modelId);
|
||||||
const provider = ProviderFactory.getProviderForModel(effectiveModel);
|
|
||||||
|
|
||||||
const executeOptions: ExecuteOptions = {
|
const executeOptions: ExecuteOptions = {
|
||||||
prompt: message,
|
prompt: message,
|
||||||
model: effectiveModel,
|
model: modelId,
|
||||||
cwd: projectPath,
|
cwd: projectPath,
|
||||||
systemPrompt: sdkOptions.systemPrompt,
|
systemPrompt: sdkOptions.systemPrompt,
|
||||||
maxTurns: 1, // Single turn for ideation
|
maxTurns: 1, // Single turn for ideation
|
||||||
@@ -645,20 +648,22 @@ export class IdeationService {
|
|||||||
existingWorkContext
|
existingWorkContext
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Resolve model alias to canonical identifier
|
||||||
|
const modelId = resolveModelString('sonnet');
|
||||||
|
|
||||||
// Create SDK options
|
// Create SDK options
|
||||||
const sdkOptions = createChatOptions({
|
const sdkOptions = createChatOptions({
|
||||||
cwd: projectPath,
|
cwd: projectPath,
|
||||||
model: 'sonnet',
|
model: modelId,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
abortController: new AbortController(),
|
abortController: new AbortController(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const effectiveModel = sdkOptions.model!;
|
const provider = ProviderFactory.getProviderForModel(modelId);
|
||||||
const provider = ProviderFactory.getProviderForModel(effectiveModel);
|
|
||||||
|
|
||||||
const executeOptions: ExecuteOptions = {
|
const executeOptions: ExecuteOptions = {
|
||||||
prompt: prompt.prompt,
|
prompt: prompt.prompt,
|
||||||
model: effectiveModel,
|
model: modelId,
|
||||||
cwd: projectPath,
|
cwd: projectPath,
|
||||||
systemPrompt: sdkOptions.systemPrompt,
|
systemPrompt: sdkOptions.systemPrompt,
|
||||||
maxTurns: 1,
|
maxTurns: 1,
|
||||||
@@ -892,6 +897,30 @@ ${contextSection}${existingWorkSection}`;
|
|||||||
icon: 'Cpu',
|
icon: 'Cpu',
|
||||||
description: 'Architecture and infrastructure',
|
description: 'Architecture and infrastructure',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'security',
|
||||||
|
name: 'Security',
|
||||||
|
icon: 'Shield',
|
||||||
|
description: 'Security improvements and vulnerability fixes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'performance',
|
||||||
|
name: 'Performance',
|
||||||
|
icon: 'Gauge',
|
||||||
|
description: 'Performance optimization and speed improvements',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'accessibility',
|
||||||
|
name: 'Accessibility',
|
||||||
|
icon: 'Accessibility',
|
||||||
|
description: 'Accessibility features and inclusive design',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'analytics',
|
||||||
|
name: 'Analytics',
|
||||||
|
icon: 'BarChart',
|
||||||
|
description: 'Analytics, monitoring, and insights features',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -905,7 +934,8 @@ ${contextSection}${existingWorkSection}`;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all guided prompts
|
* Get all guided prompts
|
||||||
* NOTE: Keep in sync with apps/ui/src/components/views/ideation-view/data/guided-prompts.ts
|
* This is the single source of truth for guided prompts data.
|
||||||
|
* Frontend fetches this data via /api/ideation/prompts endpoint.
|
||||||
*/
|
*/
|
||||||
getAllPrompts(): IdeationPrompt[] {
|
getAllPrompts(): IdeationPrompt[] {
|
||||||
return [
|
return [
|
||||||
@@ -1629,7 +1659,20 @@ Focus on practical, implementable suggestions that would genuinely improve the p
|
|||||||
return `${summary}. Found ${suggestions.length} improvement opportunities${highPriority > 0 ? ` (${highPriority} high priority)` : ''}.`;
|
return `${summary}. Found ${suggestions.length} improvement opportunities${highPriority > 0 ? ` (${highPriority} high priority)` : ''}.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map idea category to feature category
|
||||||
|
* Used internally for idea-to-feature conversion
|
||||||
|
*/
|
||||||
private mapIdeaCategoryToFeatureCategory(category: IdeaCategory): string {
|
private mapIdeaCategoryToFeatureCategory(category: IdeaCategory): string {
|
||||||
|
return this.mapSuggestionCategoryToFeatureCategory(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map suggestion/idea category to feature category
|
||||||
|
* This is the single source of truth for category mapping.
|
||||||
|
* Used by both idea-to-feature conversion and suggestion-to-feature conversion.
|
||||||
|
*/
|
||||||
|
mapSuggestionCategoryToFeatureCategory(category: IdeaCategory): string {
|
||||||
const mapping: Record<IdeaCategory, string> = {
|
const mapping: Record<IdeaCategory, string> = {
|
||||||
feature: 'ui',
|
feature: 'ui',
|
||||||
'ux-ui': 'enhancement',
|
'ux-ui': 'enhancement',
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import {
|
|||||||
Gauge,
|
Gauge,
|
||||||
Accessibility,
|
Accessibility,
|
||||||
BarChart3,
|
BarChart3,
|
||||||
|
Loader2,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { PROMPT_CATEGORIES } from '../data/guided-prompts';
|
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
||||||
import type { IdeaCategory } from '@automaker/types';
|
import type { IdeaCategory } from '@automaker/types';
|
||||||
|
|
||||||
interface PromptCategoryGridProps {
|
interface PromptCategoryGridProps {
|
||||||
@@ -36,6 +37,8 @@ const iconMap: Record<string, typeof Zap> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function PromptCategoryGrid({ onSelect, onBack }: PromptCategoryGridProps) {
|
export function PromptCategoryGrid({ onSelect, onBack }: PromptCategoryGridProps) {
|
||||||
|
const { categories, isLoading, error } = useGuidedPrompts();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col p-6 overflow-auto">
|
<div className="flex-1 flex flex-col p-6 overflow-auto">
|
||||||
<div className="max-w-4xl w-full mx-auto space-y-4">
|
<div className="max-w-4xl w-full mx-auto space-y-4">
|
||||||
@@ -48,8 +51,20 @@ export function PromptCategoryGrid({ onSelect, onBack }: PromptCategoryGridProps
|
|||||||
<span>Back</span>
|
<span>Back</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{isLoading && (
|
||||||
|
<div className="flex items-center justify-center py-12">
|
||||||
|
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
||||||
|
<span className="ml-2 text-muted-foreground">Loading categories...</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{error && (
|
||||||
|
<div className="text-center py-12 text-destructive">
|
||||||
|
<p>Failed to load categories: {error}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isLoading && !error && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{PROMPT_CATEGORIES.map((category) => {
|
{categories.map((category) => {
|
||||||
const Icon = iconMap[category.icon] || Zap;
|
const Icon = iconMap[category.icon] || Zap;
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -72,6 +87,7 @@ export function PromptCategoryGrid({ onSelect, onBack }: PromptCategoryGridProps
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ArrowLeft, Lightbulb, Loader2, CheckCircle2 } from 'lucide-react';
|
import { ArrowLeft, Lightbulb, Loader2, CheckCircle2 } from 'lucide-react';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { getPromptsByCategory } from '../data/guided-prompts';
|
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
||||||
import { useIdeationStore } from '@/store/ideation-store';
|
import { useIdeationStore } from '@/store/ideation-store';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
@@ -24,6 +24,11 @@ export function PromptList({ category, onBack }: PromptListProps) {
|
|||||||
const [loadingPromptId, setLoadingPromptId] = useState<string | null>(null);
|
const [loadingPromptId, setLoadingPromptId] = useState<string | null>(null);
|
||||||
const [startedPrompts, setStartedPrompts] = useState<Set<string>>(new Set());
|
const [startedPrompts, setStartedPrompts] = useState<Set<string>>(new Set());
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const {
|
||||||
|
getPromptsByCategory,
|
||||||
|
isLoading: isLoadingPrompts,
|
||||||
|
error: promptsError,
|
||||||
|
} = useGuidedPrompts();
|
||||||
|
|
||||||
const prompts = getPromptsByCategory(category);
|
const prompts = getPromptsByCategory(category);
|
||||||
|
|
||||||
@@ -101,7 +106,20 @@ export function PromptList({ category, onBack }: PromptListProps) {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{prompts.map((prompt) => {
|
{isLoadingPrompts && (
|
||||||
|
<div className="flex items-center justify-center py-8">
|
||||||
|
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
||||||
|
<span className="ml-2 text-muted-foreground">Loading prompts...</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{promptsError && (
|
||||||
|
<div className="text-center py-8 text-destructive">
|
||||||
|
<p>Failed to load prompts: {promptsError}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!isLoadingPrompts &&
|
||||||
|
!promptsError &&
|
||||||
|
prompts.map((prompt) => {
|
||||||
const isLoading = loadingPromptId === prompt.id;
|
const isLoading = loadingPromptId === prompt.id;
|
||||||
const isGenerating = generatingPromptIds.has(prompt.id);
|
const isGenerating = generatingPromptIds.has(prompt.id);
|
||||||
const isStarted = startedPrompts.has(prompt.id);
|
const isStarted = startedPrompts.has(prompt.id);
|
||||||
|
|||||||
@@ -1,391 +0,0 @@
|
|||||||
/**
|
|
||||||
* Guided prompts for ideation sessions
|
|
||||||
* Static data that provides pre-made prompts for different categories
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { IdeaCategory, IdeationPrompt, PromptCategory } from '@automaker/types';
|
|
||||||
|
|
||||||
export const PROMPT_CATEGORIES: PromptCategory[] = [
|
|
||||||
{
|
|
||||||
id: 'feature',
|
|
||||||
name: 'Features',
|
|
||||||
icon: 'Zap',
|
|
||||||
description: 'New capabilities and functionality',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ux-ui',
|
|
||||||
name: 'UX/UI',
|
|
||||||
icon: 'Palette',
|
|
||||||
description: 'Design and user experience improvements',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'dx',
|
|
||||||
name: 'Developer Experience',
|
|
||||||
icon: 'Code',
|
|
||||||
description: 'Developer tooling and workflows',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'growth',
|
|
||||||
name: 'Growth',
|
|
||||||
icon: 'TrendingUp',
|
|
||||||
description: 'User engagement and retention',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'technical',
|
|
||||||
name: 'Technical',
|
|
||||||
icon: 'Cpu',
|
|
||||||
description: 'Architecture and infrastructure',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'security',
|
|
||||||
name: 'Security',
|
|
||||||
icon: 'Shield',
|
|
||||||
description: 'Security and privacy improvements',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'performance',
|
|
||||||
name: 'Performance',
|
|
||||||
icon: 'Gauge',
|
|
||||||
description: 'Speed and optimization',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'accessibility',
|
|
||||||
name: 'Accessibility',
|
|
||||||
icon: 'Accessibility',
|
|
||||||
description: 'Inclusive design for all users',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'analytics',
|
|
||||||
name: 'Analytics',
|
|
||||||
icon: 'BarChart3',
|
|
||||||
description: 'Data insights and tracking',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const GUIDED_PROMPTS: IdeationPrompt[] = [
|
|
||||||
// Feature prompts
|
|
||||||
{
|
|
||||||
id: 'feature-missing',
|
|
||||||
category: 'feature',
|
|
||||||
title: 'Missing Features',
|
|
||||||
description: 'Discover features users might expect',
|
|
||||||
prompt:
|
|
||||||
"Analyze this codebase and identify features that users of similar applications typically expect but are missing here. Consider the app's domain, target users, and common patterns in similar products.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'feature-automation',
|
|
||||||
category: 'feature',
|
|
||||||
title: 'Automation Opportunities',
|
|
||||||
description: 'Find manual processes that could be automated',
|
|
||||||
prompt:
|
|
||||||
'Review this codebase and identify manual processes or repetitive tasks that could be automated. Look for patterns where users might be doing things repeatedly that software could handle.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'feature-integrations',
|
|
||||||
category: 'feature',
|
|
||||||
title: 'Integration Ideas',
|
|
||||||
description: 'Identify valuable third-party integrations',
|
|
||||||
prompt:
|
|
||||||
"Based on this codebase, what third-party services or APIs would provide value if integrated? Consider the app's domain and what complementary services users might need.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'feature-workflow',
|
|
||||||
category: 'feature',
|
|
||||||
title: 'Workflow Improvements',
|
|
||||||
description: 'Streamline user workflows',
|
|
||||||
prompt:
|
|
||||||
'Analyze the user workflows in this application. What steps could be combined, eliminated, or automated? Where are users likely spending too much time on repetitive tasks?',
|
|
||||||
},
|
|
||||||
|
|
||||||
// UX/UI prompts
|
|
||||||
{
|
|
||||||
id: 'ux-friction',
|
|
||||||
category: 'ux-ui',
|
|
||||||
title: 'Friction Points',
|
|
||||||
description: 'Identify where users might get stuck',
|
|
||||||
prompt:
|
|
||||||
'Analyze the user flows in this codebase and identify potential friction points. Where might users get confused, stuck, or frustrated? Look at form submissions, navigation, error states, and complex interactions.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ux-empty-states',
|
|
||||||
category: 'ux-ui',
|
|
||||||
title: 'Empty States',
|
|
||||||
description: 'Improve empty state experiences',
|
|
||||||
prompt:
|
|
||||||
"Review the components in this codebase and identify empty states that could be improved. How can we guide users when there's no content? Consider onboarding, helpful prompts, and sample data.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ux-accessibility',
|
|
||||||
category: 'ux-ui',
|
|
||||||
title: 'Accessibility Improvements',
|
|
||||||
description: 'Enhance accessibility and inclusivity',
|
|
||||||
prompt:
|
|
||||||
'Analyze this codebase for accessibility improvements. Consider keyboard navigation, screen reader support, color contrast, focus states, and ARIA labels. What specific improvements would make this more accessible?',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ux-mobile',
|
|
||||||
category: 'ux-ui',
|
|
||||||
title: 'Mobile Experience',
|
|
||||||
description: 'Optimize for mobile users',
|
|
||||||
prompt:
|
|
||||||
'Review this codebase from a mobile-first perspective. What improvements would enhance the mobile user experience? Consider touch targets, responsive layouts, and mobile-specific interactions.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'ux-feedback',
|
|
||||||
category: 'ux-ui',
|
|
||||||
title: 'User Feedback',
|
|
||||||
description: 'Improve feedback and status indicators',
|
|
||||||
prompt:
|
|
||||||
'Analyze how this application communicates with users. Where are loading states, success messages, or error handling missing or unclear? What feedback would help users understand what is happening?',
|
|
||||||
},
|
|
||||||
|
|
||||||
// DX prompts
|
|
||||||
{
|
|
||||||
id: 'dx-documentation',
|
|
||||||
category: 'dx',
|
|
||||||
title: 'Documentation Gaps',
|
|
||||||
description: 'Identify missing documentation',
|
|
||||||
prompt:
|
|
||||||
'Review this codebase and identify areas lacking documentation. What would help new developers understand the architecture, APIs, and conventions? Consider inline comments, READMEs, and API docs.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'dx-testing',
|
|
||||||
category: 'dx',
|
|
||||||
title: 'Testing Improvements',
|
|
||||||
description: 'Enhance test coverage and quality',
|
|
||||||
prompt:
|
|
||||||
'Analyze the testing patterns in this codebase. What areas need better test coverage? What types of tests are missing? Consider unit tests, integration tests, and end-to-end tests.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'dx-tooling',
|
|
||||||
category: 'dx',
|
|
||||||
title: 'Developer Tooling',
|
|
||||||
description: 'Improve development workflows',
|
|
||||||
prompt:
|
|
||||||
'Review the development setup and tooling in this codebase. What improvements would speed up development? Consider build times, hot reload, debugging tools, and developer scripts.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'dx-error-handling',
|
|
||||||
category: 'dx',
|
|
||||||
title: 'Error Handling',
|
|
||||||
description: 'Improve error messages and debugging',
|
|
||||||
prompt:
|
|
||||||
'Analyze error handling in this codebase. Where are error messages unclear or missing? What would help developers debug issues faster? Consider logging, error boundaries, and stack traces.',
|
|
||||||
},
|
|
||||||
|
|
||||||
// Growth prompts
|
|
||||||
{
|
|
||||||
id: 'growth-onboarding',
|
|
||||||
category: 'growth',
|
|
||||||
title: 'Onboarding Flow',
|
|
||||||
description: 'Improve new user experience',
|
|
||||||
prompt:
|
|
||||||
"Analyze this application's onboarding experience. How can we help new users understand the value and get started quickly? Consider tutorials, progressive disclosure, and quick wins.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'growth-engagement',
|
|
||||||
category: 'growth',
|
|
||||||
title: 'User Engagement',
|
|
||||||
description: 'Increase user retention and activity',
|
|
||||||
prompt:
|
|
||||||
'Review this application and suggest features that would increase user engagement and retention. What would bring users back daily? Consider notifications, streaks, social features, and personalization.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'growth-sharing',
|
|
||||||
category: 'growth',
|
|
||||||
title: 'Shareability',
|
|
||||||
description: 'Make the app more shareable',
|
|
||||||
prompt:
|
|
||||||
'How can this application be made more shareable? What features would encourage users to invite others or share their work? Consider collaboration, public profiles, and export features.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'growth-monetization',
|
|
||||||
category: 'growth',
|
|
||||||
title: 'Monetization Ideas',
|
|
||||||
description: 'Identify potential revenue streams',
|
|
||||||
prompt:
|
|
||||||
'Based on this codebase, what features or tiers could support monetization? Consider premium features, usage limits, team features, and integrations that users would pay for.',
|
|
||||||
},
|
|
||||||
|
|
||||||
// Technical prompts
|
|
||||||
{
|
|
||||||
id: 'tech-performance',
|
|
||||||
category: 'technical',
|
|
||||||
title: 'Performance Optimization',
|
|
||||||
description: 'Identify performance bottlenecks',
|
|
||||||
prompt:
|
|
||||||
'Analyze this codebase for performance optimization opportunities. Where are the likely bottlenecks? Consider database queries, API calls, bundle size, rendering, and caching strategies.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tech-architecture',
|
|
||||||
category: 'technical',
|
|
||||||
title: 'Architecture Review',
|
|
||||||
description: 'Evaluate and improve architecture',
|
|
||||||
prompt:
|
|
||||||
'Review the architecture of this codebase. What improvements would make it more maintainable, scalable, or testable? Consider separation of concerns, dependency management, and patterns.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tech-debt',
|
|
||||||
category: 'technical',
|
|
||||||
title: 'Technical Debt',
|
|
||||||
description: 'Identify areas needing refactoring',
|
|
||||||
prompt:
|
|
||||||
'Identify technical debt in this codebase. What areas are becoming hard to maintain or understand? What refactoring would have the highest impact? Consider duplicated code, complexity, and outdated patterns.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tech-security',
|
|
||||||
category: 'technical',
|
|
||||||
title: 'Security Review',
|
|
||||||
description: 'Identify security improvements',
|
|
||||||
prompt:
|
|
||||||
'Review this codebase for security improvements. What best practices are missing? Consider authentication, authorization, input validation, and data protection. Note: This is for improvement suggestions, not a security audit.',
|
|
||||||
},
|
|
||||||
|
|
||||||
// Security prompts
|
|
||||||
{
|
|
||||||
id: 'security-auth',
|
|
||||||
category: 'security',
|
|
||||||
title: 'Authentication Security',
|
|
||||||
description: 'Review authentication mechanisms',
|
|
||||||
prompt:
|
|
||||||
'Analyze the authentication system in this codebase. What security improvements would strengthen user authentication? Consider password policies, session management, MFA, and token handling.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'security-data',
|
|
||||||
category: 'security',
|
|
||||||
title: 'Data Protection',
|
|
||||||
description: 'Protect sensitive user data',
|
|
||||||
prompt:
|
|
||||||
'Review how this application handles sensitive data. What improvements would better protect user privacy? Consider encryption, data minimization, secure storage, and data retention policies.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'security-input',
|
|
||||||
category: 'security',
|
|
||||||
title: 'Input Validation',
|
|
||||||
description: 'Prevent injection attacks',
|
|
||||||
prompt:
|
|
||||||
'Analyze input handling in this codebase. Where could input validation be strengthened? Consider SQL injection, XSS, command injection, and file upload vulnerabilities.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'security-api',
|
|
||||||
category: 'security',
|
|
||||||
title: 'API Security',
|
|
||||||
description: 'Secure API endpoints',
|
|
||||||
prompt:
|
|
||||||
'Review the API security in this codebase. What improvements would make the API more secure? Consider rate limiting, authorization, CORS, and request validation.',
|
|
||||||
},
|
|
||||||
|
|
||||||
// Performance prompts
|
|
||||||
{
|
|
||||||
id: 'perf-frontend',
|
|
||||||
category: 'performance',
|
|
||||||
title: 'Frontend Performance',
|
|
||||||
description: 'Optimize UI rendering and loading',
|
|
||||||
prompt:
|
|
||||||
'Analyze the frontend performance of this application. What optimizations would improve load times and responsiveness? Consider bundle splitting, lazy loading, memoization, and render optimization.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'perf-backend',
|
|
||||||
category: 'performance',
|
|
||||||
title: 'Backend Performance',
|
|
||||||
description: 'Optimize server-side operations',
|
|
||||||
prompt:
|
|
||||||
'Review backend performance in this codebase. What optimizations would improve response times? Consider database queries, caching strategies, async operations, and resource pooling.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'perf-database',
|
|
||||||
category: 'performance',
|
|
||||||
title: 'Database Optimization',
|
|
||||||
description: 'Improve query performance',
|
|
||||||
prompt:
|
|
||||||
'Analyze database interactions in this codebase. What optimizations would improve data access performance? Consider indexing, query optimization, denormalization, and connection pooling.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'perf-caching',
|
|
||||||
category: 'performance',
|
|
||||||
title: 'Caching Strategies',
|
|
||||||
description: 'Implement effective caching',
|
|
||||||
prompt:
|
|
||||||
'Review caching opportunities in this application. Where would caching provide the most benefit? Consider API responses, computed values, static assets, and session data.',
|
|
||||||
},
|
|
||||||
|
|
||||||
// Accessibility prompts
|
|
||||||
{
|
|
||||||
id: 'a11y-keyboard',
|
|
||||||
category: 'accessibility',
|
|
||||||
title: 'Keyboard Navigation',
|
|
||||||
description: 'Enable full keyboard access',
|
|
||||||
prompt:
|
|
||||||
'Analyze keyboard accessibility in this codebase. What improvements would enable users to navigate entirely with keyboard? Consider focus management, tab order, and keyboard shortcuts.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'a11y-screen-reader',
|
|
||||||
category: 'accessibility',
|
|
||||||
title: 'Screen Reader Support',
|
|
||||||
description: 'Improve screen reader experience',
|
|
||||||
prompt:
|
|
||||||
'Review screen reader compatibility in this application. What improvements would help users with visual impairments? Consider ARIA labels, semantic HTML, live regions, and alt text.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'a11y-visual',
|
|
||||||
category: 'accessibility',
|
|
||||||
title: 'Visual Accessibility',
|
|
||||||
description: 'Improve visual design for all users',
|
|
||||||
prompt:
|
|
||||||
'Analyze visual accessibility in this codebase. What improvements would help users with visual impairments? Consider color contrast, text sizing, focus indicators, and reduced motion.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'a11y-forms',
|
|
||||||
category: 'accessibility',
|
|
||||||
title: 'Accessible Forms',
|
|
||||||
description: 'Make forms usable for everyone',
|
|
||||||
prompt:
|
|
||||||
'Review form accessibility in this application. What improvements would make forms more accessible? Consider labels, error messages, required field indicators, and input assistance.',
|
|
||||||
},
|
|
||||||
|
|
||||||
// Analytics prompts
|
|
||||||
{
|
|
||||||
id: 'analytics-tracking',
|
|
||||||
category: 'analytics',
|
|
||||||
title: 'User Tracking',
|
|
||||||
description: 'Track key user behaviors',
|
|
||||||
prompt:
|
|
||||||
'Analyze this application for analytics opportunities. What user behaviors should be tracked to understand engagement? Consider page views, feature usage, conversion funnels, and session duration.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'analytics-metrics',
|
|
||||||
category: 'analytics',
|
|
||||||
title: 'Key Metrics',
|
|
||||||
description: 'Define success metrics',
|
|
||||||
prompt:
|
|
||||||
'Based on this codebase, what key metrics should be tracked? Consider user acquisition, retention, engagement, and feature adoption. What dashboards would be most valuable?',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'analytics-errors',
|
|
||||||
category: 'analytics',
|
|
||||||
title: 'Error Monitoring',
|
|
||||||
description: 'Track and analyze errors',
|
|
||||||
prompt:
|
|
||||||
'Review error handling in this codebase for monitoring opportunities. What error tracking would help identify and fix issues faster? Consider error aggregation, alerting, and stack traces.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'analytics-performance',
|
|
||||||
category: 'analytics',
|
|
||||||
title: 'Performance Monitoring',
|
|
||||||
description: 'Track application performance',
|
|
||||||
prompt:
|
|
||||||
'Analyze this application for performance monitoring opportunities. What metrics would help identify bottlenecks? Consider load times, API response times, and resource usage.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export function getPromptsByCategory(category: IdeaCategory): IdeationPrompt[] {
|
|
||||||
return GUIDED_PROMPTS.filter((p) => p.category === category);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPromptById(id: string): IdeationPrompt | undefined {
|
|
||||||
return GUIDED_PROMPTS.find((p) => p.id === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCategoryById(id: IdeaCategory): PromptCategory | undefined {
|
|
||||||
return PROMPT_CATEGORIES.find((c) => c.id === id);
|
|
||||||
}
|
|
||||||
@@ -9,27 +9,12 @@ import { useAppStore } from '@/store/app-store';
|
|||||||
import { PromptCategoryGrid } from './components/prompt-category-grid';
|
import { PromptCategoryGrid } from './components/prompt-category-grid';
|
||||||
import { PromptList } from './components/prompt-list';
|
import { PromptList } from './components/prompt-list';
|
||||||
import { IdeationDashboard } from './components/ideation-dashboard';
|
import { IdeationDashboard } from './components/ideation-dashboard';
|
||||||
import { getCategoryById } from './data/guided-prompts';
|
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ArrowLeft, ChevronRight, Lightbulb } from 'lucide-react';
|
import { ArrowLeft, ChevronRight, Lightbulb } from 'lucide-react';
|
||||||
import type { IdeaCategory } from '@automaker/types';
|
import type { IdeaCategory } from '@automaker/types';
|
||||||
import type { IdeationMode } from '@/store/ideation-store';
|
import type { IdeationMode } from '@/store/ideation-store';
|
||||||
|
|
||||||
// Get subtitle text based on current mode
|
|
||||||
function getSubtitle(currentMode: IdeationMode, selectedCategory: IdeaCategory | null): string {
|
|
||||||
if (currentMode === 'dashboard') {
|
|
||||||
return 'Review and accept generated ideas';
|
|
||||||
}
|
|
||||||
if (currentMode === 'prompts') {
|
|
||||||
if (selectedCategory) {
|
|
||||||
const categoryInfo = getCategoryById(selectedCategory);
|
|
||||||
return `Select a prompt from ${categoryInfo?.name || 'category'}`;
|
|
||||||
}
|
|
||||||
return 'Select a category to generate ideas';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Breadcrumb component - compact inline breadcrumbs
|
// Breadcrumb component - compact inline breadcrumbs
|
||||||
function IdeationBreadcrumbs({
|
function IdeationBreadcrumbs({
|
||||||
currentMode,
|
currentMode,
|
||||||
@@ -40,6 +25,7 @@ function IdeationBreadcrumbs({
|
|||||||
selectedCategory: IdeaCategory | null;
|
selectedCategory: IdeaCategory | null;
|
||||||
onNavigate: (mode: IdeationMode, category?: IdeaCategory | null) => void;
|
onNavigate: (mode: IdeationMode, category?: IdeaCategory | null) => void;
|
||||||
}) {
|
}) {
|
||||||
|
const { getCategoryById } = useGuidedPrompts();
|
||||||
const categoryInfo = selectedCategory ? getCategoryById(selectedCategory) : null;
|
const categoryInfo = selectedCategory ? getCategoryById(selectedCategory) : null;
|
||||||
|
|
||||||
// On dashboard, no breadcrumbs needed (it's the root)
|
// On dashboard, no breadcrumbs needed (it's the root)
|
||||||
@@ -88,9 +74,26 @@ function IdeationHeader({
|
|||||||
onGenerateIdeas: () => void;
|
onGenerateIdeas: () => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
}) {
|
}) {
|
||||||
const subtitle = getSubtitle(currentMode, selectedCategory);
|
const { getCategoryById } = useGuidedPrompts();
|
||||||
const showBackButton = currentMode === 'prompts';
|
const showBackButton = currentMode === 'prompts';
|
||||||
|
|
||||||
|
// Get subtitle text based on current mode
|
||||||
|
const getSubtitle = (): string => {
|
||||||
|
if (currentMode === 'dashboard') {
|
||||||
|
return 'Review and accept generated ideas';
|
||||||
|
}
|
||||||
|
if (currentMode === 'prompts') {
|
||||||
|
if (selectedCategory) {
|
||||||
|
const categoryInfo = getCategoryById(selectedCategory);
|
||||||
|
return `Select a prompt from ${categoryInfo?.name || 'category'}`;
|
||||||
|
}
|
||||||
|
return 'Select a category to generate ideas';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const subtitle = getSubtitle();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between p-4 border-b border-border bg-glass backdrop-blur-md">
|
<div className="flex items-center justify-between p-4 border-b border-border bg-glass backdrop-blur-md">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export { useAutoMode } from './use-auto-mode';
|
export { useAutoMode } from './use-auto-mode';
|
||||||
export { useBoardBackgroundSettings } from './use-board-background-settings';
|
export { useBoardBackgroundSettings } from './use-board-background-settings';
|
||||||
export { useElectronAgent } from './use-electron-agent';
|
export { useElectronAgent } from './use-electron-agent';
|
||||||
|
export { useGuidedPrompts } from './use-guided-prompts';
|
||||||
export { useKeyboardShortcuts } from './use-keyboard-shortcuts';
|
export { useKeyboardShortcuts } from './use-keyboard-shortcuts';
|
||||||
export { useMessageQueue } from './use-message-queue';
|
export { useMessageQueue } from './use-message-queue';
|
||||||
export { useOSDetection, type OperatingSystem, type OSDetectionResult } from './use-os-detection';
|
export { useOSDetection, type OperatingSystem, type OSDetectionResult } from './use-os-detection';
|
||||||
|
|||||||
86
apps/ui/src/hooks/use-guided-prompts.ts
Normal file
86
apps/ui/src/hooks/use-guided-prompts.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* Hook for fetching guided prompts from the backend API
|
||||||
|
*
|
||||||
|
* This hook provides the single source of truth for guided prompts,
|
||||||
|
* fetched from the backend /api/ideation/prompts endpoint.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import type { IdeationPrompt, PromptCategory, IdeaCategory } from '@automaker/types';
|
||||||
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
|
interface UseGuidedPromptsReturn {
|
||||||
|
prompts: IdeationPrompt[];
|
||||||
|
categories: PromptCategory[];
|
||||||
|
isLoading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
refetch: () => Promise<void>;
|
||||||
|
getPromptsByCategory: (category: IdeaCategory) => IdeationPrompt[];
|
||||||
|
getPromptById: (id: string) => IdeationPrompt | undefined;
|
||||||
|
getCategoryById: (id: IdeaCategory) => PromptCategory | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGuidedPrompts(): UseGuidedPromptsReturn {
|
||||||
|
const [prompts, setPrompts] = useState<IdeationPrompt[]>([]);
|
||||||
|
const [categories, setCategories] = useState<PromptCategory[]>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const fetchPrompts = useCallback(async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const api = getElectronAPI();
|
||||||
|
const result = await api.ideation?.getPrompts();
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
setPrompts(result.prompts || []);
|
||||||
|
setCategories(result.categories || []);
|
||||||
|
} else {
|
||||||
|
setError(result?.error || 'Failed to fetch prompts');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to fetch guided prompts:', err);
|
||||||
|
setError(err instanceof Error ? err.message : 'Failed to fetch prompts');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchPrompts();
|
||||||
|
}, [fetchPrompts]);
|
||||||
|
|
||||||
|
const getPromptsByCategory = useCallback(
|
||||||
|
(category: IdeaCategory): IdeationPrompt[] => {
|
||||||
|
return prompts.filter((p) => p.category === category);
|
||||||
|
},
|
||||||
|
[prompts]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getPromptById = useCallback(
|
||||||
|
(id: string): IdeationPrompt | undefined => {
|
||||||
|
return prompts.find((p) => p.id === id);
|
||||||
|
},
|
||||||
|
[prompts]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getCategoryById = useCallback(
|
||||||
|
(id: IdeaCategory): PromptCategory | undefined => {
|
||||||
|
return categories.find((c) => c.id === id);
|
||||||
|
},
|
||||||
|
[categories]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
prompts,
|
||||||
|
categories,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
refetch: fetchPrompts,
|
||||||
|
getPromptsByCategory,
|
||||||
|
getPromptById,
|
||||||
|
getCategoryById,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -17,6 +17,8 @@ import type {
|
|||||||
IdeaCategory,
|
IdeaCategory,
|
||||||
IdeationSession,
|
IdeationSession,
|
||||||
IdeationMessage,
|
IdeationMessage,
|
||||||
|
IdeationPrompt,
|
||||||
|
PromptCategory,
|
||||||
ProjectAnalysisResult,
|
ProjectAnalysisResult,
|
||||||
AnalysisSuggestion,
|
AnalysisSuggestion,
|
||||||
StartSessionOptions,
|
StartSessionOptions,
|
||||||
@@ -46,6 +48,8 @@ export type {
|
|||||||
IdeaCategory,
|
IdeaCategory,
|
||||||
IdeationSession,
|
IdeationSession,
|
||||||
IdeationMessage,
|
IdeationMessage,
|
||||||
|
IdeationPrompt,
|
||||||
|
PromptCategory,
|
||||||
ProjectAnalysisResult,
|
ProjectAnalysisResult,
|
||||||
AnalysisSuggestion,
|
AnalysisSuggestion,
|
||||||
StartSessionOptions,
|
StartSessionOptions,
|
||||||
@@ -123,6 +127,14 @@ export interface IdeationAPI {
|
|||||||
suggestion: AnalysisSuggestion
|
suggestion: AnalysisSuggestion
|
||||||
) => Promise<{ success: boolean; featureId?: string; error?: string }>;
|
) => Promise<{ success: boolean; featureId?: string; error?: string }>;
|
||||||
|
|
||||||
|
// Get guided prompts (single source of truth from backend)
|
||||||
|
getPrompts: () => Promise<{
|
||||||
|
success: boolean;
|
||||||
|
prompts?: IdeationPrompt[];
|
||||||
|
categories?: PromptCategory[];
|
||||||
|
error?: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
// Event subscriptions
|
// Event subscriptions
|
||||||
onStream: (callback: (event: any) => void) => () => void;
|
onStream: (callback: (event: any) => void) => () => void;
|
||||||
onAnalysisEvent: (callback: (event: any) => void) => () => void;
|
onAnalysisEvent: (callback: (event: any) => void) => () => void;
|
||||||
|
|||||||
@@ -1690,35 +1690,13 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
convertToFeature: (projectPath: string, ideaId: string, options?: ConvertToFeatureOptions) =>
|
convertToFeature: (projectPath: string, ideaId: string, options?: ConvertToFeatureOptions) =>
|
||||||
this.post('/api/ideation/convert', { projectPath, ideaId, ...options }),
|
this.post('/api/ideation/convert', { projectPath, ideaId, ...options }),
|
||||||
|
|
||||||
addSuggestionToBoard: async (projectPath: string, suggestion: AnalysisSuggestion) => {
|
addSuggestionToBoard: (
|
||||||
// Create a feature directly from the suggestion
|
projectPath: string,
|
||||||
const result = await this.post<{ success: boolean; feature?: Feature; error?: string }>(
|
suggestion: AnalysisSuggestion
|
||||||
'/api/features/create',
|
): Promise<{ success: boolean; featureId?: string; error?: string }> =>
|
||||||
{
|
this.post('/api/ideation/add-suggestion', { projectPath, suggestion }),
|
||||||
projectPath,
|
|
||||||
feature: {
|
getPrompts: () => this.get('/api/ideation/prompts'),
|
||||||
title: suggestion.title,
|
|
||||||
description:
|
|
||||||
suggestion.description +
|
|
||||||
(suggestion.rationale ? `\n\n**Rationale:** ${suggestion.rationale}` : ''),
|
|
||||||
category:
|
|
||||||
suggestion.category === 'ux-ui'
|
|
||||||
? 'enhancement'
|
|
||||||
: suggestion.category === 'dx'
|
|
||||||
? 'chore'
|
|
||||||
: suggestion.category === 'technical'
|
|
||||||
? 'refactor'
|
|
||||||
: 'feature',
|
|
||||||
status: 'backlog',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
success: result.success,
|
|
||||||
featureId: result.feature?.id,
|
|
||||||
error: result.error,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onStream: (callback: (event: any) => void): (() => void) => {
|
onStream: (callback: (event: any) => void): (() => void) => {
|
||||||
return this.subscribeToEvent('ideation:stream', callback as EventCallback);
|
return this.subscribeToEvent('ideation:stream', callback as EventCallback);
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ export type EventType =
|
|||||||
| 'ideation:analysis-progress'
|
| 'ideation:analysis-progress'
|
||||||
| 'ideation:analysis-complete'
|
| 'ideation:analysis-complete'
|
||||||
| 'ideation:analysis-error'
|
| 'ideation:analysis-error'
|
||||||
| 'ideation:suggestions';
|
| 'ideation:suggestions'
|
||||||
|
| 'ideation:idea-created'
|
||||||
|
| 'ideation:idea-updated'
|
||||||
|
| 'ideation:idea-deleted'
|
||||||
|
| 'ideation:idea-converted';
|
||||||
|
|
||||||
export type EventCallback = (type: EventType, payload: unknown) => void;
|
export type EventCallback = (type: EventType, payload: unknown) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user