mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
Merge pull request #123 from AutoMaker-Org/enhance-feature-with-ai
feat: add AI enhancement feature to settings and board views
This commit is contained in:
@@ -22,6 +22,7 @@ import { createAgentRoutes } from "./routes/agent/index.js";
|
||||
import { createSessionsRoutes } from "./routes/sessions/index.js";
|
||||
import { createFeaturesRoutes } from "./routes/features/index.js";
|
||||
import { createAutoModeRoutes } from "./routes/auto-mode/index.js";
|
||||
import { createEnhancePromptRoutes } from "./routes/enhance-prompt/index.js";
|
||||
import { createWorktreeRoutes } from "./routes/worktree/index.js";
|
||||
import { createGitRoutes } from "./routes/git/index.js";
|
||||
import { createSetupRoutes } from "./routes/setup/index.js";
|
||||
@@ -125,6 +126,7 @@ app.use("/api/agent", createAgentRoutes(agentService, events));
|
||||
app.use("/api/sessions", createSessionsRoutes(agentService));
|
||||
app.use("/api/features", createFeaturesRoutes(featureLoader));
|
||||
app.use("/api/auto-mode", createAutoModeRoutes(autoModeService));
|
||||
app.use("/api/enhance-prompt", createEnhancePromptRoutes());
|
||||
app.use("/api/worktree", createWorktreeRoutes());
|
||||
app.use("/api/git", createGitRoutes());
|
||||
app.use("/api/setup", createSetupRoutes());
|
||||
|
||||
456
apps/server/src/lib/enhancement-prompts.ts
Normal file
456
apps/server/src/lib/enhancement-prompts.ts
Normal file
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* Enhancement Prompts Library - AI-powered text enhancement for task descriptions
|
||||
*
|
||||
* Provides prompt templates and utilities for enhancing user-written task descriptions:
|
||||
* - Improve: Transform vague requests into clear, actionable tasks
|
||||
* - Technical: Add implementation details and technical specifications
|
||||
* - Simplify: Make verbose descriptions concise and focused
|
||||
* - Acceptance: Add testable acceptance criteria
|
||||
*
|
||||
* Uses chain-of-thought prompting with few-shot examples for consistent results.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Available enhancement modes for transforming task descriptions
|
||||
*/
|
||||
export type EnhancementMode = "improve" | "technical" | "simplify" | "acceptance";
|
||||
|
||||
/**
|
||||
* Example input/output pair for few-shot learning
|
||||
*/
|
||||
export interface EnhancementExample {
|
||||
input: string;
|
||||
output: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* System prompt for the "improve" enhancement mode.
|
||||
* Transforms vague or unclear requests into clear, actionable task descriptions.
|
||||
*/
|
||||
export const IMPROVE_SYSTEM_PROMPT = `You are an expert at transforming vague, unclear, or incomplete task descriptions into clear, actionable specifications.
|
||||
|
||||
Your task is to take a user's rough description and improve it by:
|
||||
|
||||
1. ANALYZE the input:
|
||||
- Identify the core intent behind the request
|
||||
- Note any ambiguities or missing details
|
||||
- Determine what success would look like
|
||||
|
||||
2. CLARIFY the scope:
|
||||
- Define clear boundaries for the task
|
||||
- Identify implicit requirements
|
||||
- Add relevant context that may be assumed
|
||||
|
||||
3. STRUCTURE the output:
|
||||
- Write a clear, actionable title
|
||||
- Provide a concise description of what needs to be done
|
||||
- Break down into specific sub-tasks if appropriate
|
||||
|
||||
4. ENHANCE with details:
|
||||
- Add specific, measurable outcomes where possible
|
||||
- Include edge cases to consider
|
||||
- Note any dependencies or prerequisites
|
||||
|
||||
Output ONLY the improved task description. Do not include explanations, markdown formatting, or meta-commentary about your changes.`;
|
||||
|
||||
/**
|
||||
* System prompt for the "technical" enhancement mode.
|
||||
* Adds implementation details and technical specifications.
|
||||
*/
|
||||
export const TECHNICAL_SYSTEM_PROMPT = `You are a senior software engineer skilled at adding technical depth to feature descriptions.
|
||||
|
||||
Your task is to enhance a task description with technical implementation details:
|
||||
|
||||
1. ANALYZE the requirement:
|
||||
- Understand the functional goal
|
||||
- Identify the technical domain (frontend, backend, database, etc.)
|
||||
- Consider the likely tech stack based on context
|
||||
|
||||
2. ADD technical specifications:
|
||||
- Suggest specific technologies, libraries, or patterns
|
||||
- Define API contracts or data structures if relevant
|
||||
- Note performance considerations
|
||||
- Identify security implications
|
||||
|
||||
3. OUTLINE implementation approach:
|
||||
- Break down into technical sub-tasks
|
||||
- Suggest file structure or component organization
|
||||
- Note integration points with existing systems
|
||||
|
||||
4. CONSIDER edge cases:
|
||||
- Error handling requirements
|
||||
- Loading and empty states
|
||||
- Boundary conditions
|
||||
|
||||
Output ONLY the enhanced technical description. Keep it concise but comprehensive. Do not include explanations about your reasoning.`;
|
||||
|
||||
/**
|
||||
* System prompt for the "simplify" enhancement mode.
|
||||
* Makes verbose descriptions concise and focused.
|
||||
*/
|
||||
export const SIMPLIFY_SYSTEM_PROMPT = `You are an expert editor who excels at making verbose text concise without losing meaning.
|
||||
|
||||
Your task is to simplify a task description while preserving essential information:
|
||||
|
||||
1. IDENTIFY the core message:
|
||||
- Extract the primary goal or requirement
|
||||
- Note truly essential details
|
||||
- Separate nice-to-have from must-have information
|
||||
|
||||
2. ELIMINATE redundancy:
|
||||
- Remove repeated information
|
||||
- Cut unnecessary qualifiers and hedging language
|
||||
- Remove filler words and phrases
|
||||
|
||||
3. CONSOLIDATE related points:
|
||||
- Merge overlapping requirements
|
||||
- Group related items together
|
||||
- Use concise language
|
||||
|
||||
4. PRESERVE critical details:
|
||||
- Keep specific technical requirements
|
||||
- Retain important constraints
|
||||
- Maintain actionable specifics
|
||||
|
||||
Output ONLY the simplified description. Aim for 30-50% reduction in length while keeping all essential information. Do not explain your changes.`;
|
||||
|
||||
/**
|
||||
* System prompt for the "acceptance" enhancement mode.
|
||||
* Adds testable acceptance criteria to task descriptions.
|
||||
*/
|
||||
export const ACCEPTANCE_SYSTEM_PROMPT = `You are a QA specialist skilled at defining testable acceptance criteria for software features.
|
||||
|
||||
Your task is to enhance a task description by adding clear acceptance criteria:
|
||||
|
||||
1. UNDERSTAND the feature:
|
||||
- Identify all user-facing behaviors
|
||||
- Note system state changes
|
||||
- Consider different user roles or scenarios
|
||||
|
||||
2. DEFINE acceptance criteria using Given-When-Then format:
|
||||
- Given: The initial context or preconditions
|
||||
- When: The action or trigger
|
||||
- Then: The expected outcome
|
||||
|
||||
3. COVER key scenarios:
|
||||
- Happy path (successful completion)
|
||||
- Edge cases (boundary conditions)
|
||||
- Error scenarios (what should NOT happen)
|
||||
- Performance requirements if relevant
|
||||
|
||||
4. MAKE criteria testable:
|
||||
- Use specific, measurable outcomes
|
||||
- Avoid vague terms like "quickly" or "easily"
|
||||
- Include specific values where applicable
|
||||
|
||||
Output the original description followed by a clear "Acceptance Criteria:" section with numbered, testable criteria. Do not include explanations about your process.`;
|
||||
|
||||
/**
|
||||
* Few-shot examples for the "improve" enhancement mode
|
||||
*/
|
||||
export const IMPROVE_EXAMPLES: EnhancementExample[] = [
|
||||
{
|
||||
input: "add dark mode",
|
||||
output: `Add Dark Mode Theme Support
|
||||
|
||||
Implement a dark mode option that allows users to switch between light and dark themes throughout the application.
|
||||
|
||||
Requirements:
|
||||
- Add a theme toggle switch in the settings area
|
||||
- Store user's theme preference persistently
|
||||
- Apply dark theme colors to all UI components
|
||||
- Respect system preference as default if user hasn't set a preference
|
||||
- Ensure smooth transition when switching themes`,
|
||||
},
|
||||
{
|
||||
input: "fix the login bug",
|
||||
output: `Fix Login Authentication Flow
|
||||
|
||||
Investigate and resolve issues preventing users from successfully logging in to the application.
|
||||
|
||||
Requirements:
|
||||
- Identify the specific failure point in the login process
|
||||
- Ensure proper credential validation
|
||||
- Handle error states with clear user feedback
|
||||
- Verify session token is correctly stored after successful login
|
||||
- Test with various credential combinations (valid, invalid, edge cases)`,
|
||||
},
|
||||
{
|
||||
input: "make it faster",
|
||||
output: `Improve Application Performance
|
||||
|
||||
Optimize application loading and response times to enhance user experience.
|
||||
|
||||
Requirements:
|
||||
- Profile current performance to identify bottlenecks
|
||||
- Implement code splitting and lazy loading for routes/components
|
||||
- Optimize database queries and add appropriate indexes
|
||||
- Add caching for frequently accessed data
|
||||
- Set performance budget targets (e.g., LCP < 2.5s, FID < 100ms)
|
||||
- Measure and document improvements`,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Few-shot examples for the "technical" enhancement mode
|
||||
*/
|
||||
export const TECHNICAL_EXAMPLES: EnhancementExample[] = [
|
||||
{
|
||||
input: "Add user profile page",
|
||||
output: `Add User Profile Page
|
||||
|
||||
Create a dedicated profile page for viewing and editing user information.
|
||||
|
||||
Technical Implementation:
|
||||
- Frontend: React component at /profile route with form validation
|
||||
- API Endpoint: GET/PUT /api/users/:id for fetching and updating profile
|
||||
- Data Model: Extend User schema with profile fields (avatar, bio, preferences)
|
||||
- File Upload: Integrate cloud storage for avatar images with client-side resizing
|
||||
- State Management: Use optimistic updates for better UX
|
||||
- Validation: Server-side validation matching client-side rules
|
||||
|
||||
Components:
|
||||
- ProfileHeader (avatar, name, edit button)
|
||||
- ProfileForm (editable fields with validation)
|
||||
- AvatarUploader (drag-drop with preview)
|
||||
|
||||
Security: Ensure users can only edit their own profile (auth middleware)`,
|
||||
},
|
||||
{
|
||||
input: "Add search functionality",
|
||||
output: `Add Search Functionality
|
||||
|
||||
Implement full-text search across application content.
|
||||
|
||||
Technical Implementation:
|
||||
- Search Engine: Use Elasticsearch or PostgreSQL full-text search
|
||||
- API: GET /api/search?q={query}&type={type}&page={page}
|
||||
- Indexing: Create search index with relevant fields, update on content changes
|
||||
- Frontend: Debounced search input (300ms) with typeahead suggestions
|
||||
- Results: Paginated results with highlighted matching text
|
||||
|
||||
Architecture:
|
||||
- SearchService class handling query building and execution
|
||||
- SearchIndex worker for background indexing
|
||||
- SearchResults component with filtering and sorting
|
||||
|
||||
Performance:
|
||||
- Implement search result caching (Redis, 5-minute TTL)
|
||||
- Limit results per page (20 items)
|
||||
- Add query complexity limits to prevent abuse`,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Few-shot examples for the "simplify" enhancement mode
|
||||
*/
|
||||
export const SIMPLIFY_EXAMPLES: EnhancementExample[] = [
|
||||
{
|
||||
input: `We need to implement a feature that would allow our users to be able to export their data in various different formats. The formats we are thinking about supporting include CSV which is commonly used for spreadsheet applications, and also JSON which is good for developers, and possibly PDF for people who want a printable version. The user should be able to go somewhere in the UI and select which format they want and then download their data in that format. We should probably also show some kind of progress indicator while the export is happening since it might take a while for large datasets.`,
|
||||
output: `Add Data Export Feature
|
||||
|
||||
Allow users to export their data in CSV, JSON, or PDF format.
|
||||
|
||||
Requirements:
|
||||
- Add export button/menu in the UI
|
||||
- Support three formats: CSV (spreadsheets), JSON (developers), PDF (printing)
|
||||
- Show progress indicator during export
|
||||
- Handle large datasets gracefully`,
|
||||
},
|
||||
{
|
||||
input: `The current notification system is not working very well and users are complaining about it. Sometimes they don't receive notifications at all, and other times they receive too many notifications and feel overwhelmed. We also have had reports that the notifications are not appearing correctly on mobile devices. Additionally, some users have requested the ability to customize which types of notifications they receive. We should look into all of these issues and make the notification system work better overall.`,
|
||||
output: `Fix and Improve Notification System
|
||||
|
||||
Address reliability issues and add user controls for notifications.
|
||||
|
||||
Issues to fix:
|
||||
- Missing notifications (delivery reliability)
|
||||
- Notification overload (implement batching/digest)
|
||||
- Mobile display problems
|
||||
|
||||
Enhancements:
|
||||
- Add notification preferences (per-type controls)
|
||||
- Test across devices and platforms`,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Few-shot examples for the "acceptance" enhancement mode
|
||||
*/
|
||||
export const ACCEPTANCE_EXAMPLES: EnhancementExample[] = [
|
||||
{
|
||||
input: "Add password reset functionality",
|
||||
output: `Add Password Reset Functionality
|
||||
|
||||
Allow users to reset their password via email when they forget it.
|
||||
|
||||
Acceptance Criteria:
|
||||
|
||||
1. Given a user is on the login page, when they click "Forgot Password", then they should see a password reset form requesting their email.
|
||||
|
||||
2. Given a user enters a valid registered email, when they submit the form, then they should receive a password reset email within 2 minutes.
|
||||
|
||||
3. Given a user clicks the reset link in their email, when the link is less than 1 hour old, then they should see a form to enter a new password.
|
||||
|
||||
4. Given a user enters a new valid password (min 8 chars, 1 number, 1 special char), when they submit, then their password should be updated and they should be redirected to login.
|
||||
|
||||
5. Given a user enters an unregistered email, when they submit the reset form, then they should see the same success message (security: don't reveal which emails exist).
|
||||
|
||||
6. Given a reset link older than 1 hour, when a user clicks it, then they should see an expiration message with option to request a new link.`,
|
||||
},
|
||||
{
|
||||
input: "Shopping cart checkout",
|
||||
output: `Shopping Cart Checkout
|
||||
|
||||
Implement the checkout flow for purchasing items in the shopping cart.
|
||||
|
||||
Acceptance Criteria:
|
||||
|
||||
1. Given a user has items in their cart, when they click "Checkout", then they should see an order summary with item details and total price.
|
||||
|
||||
2. Given a user is on the checkout page, when they enter valid shipping information, then the form should validate in real-time and show estimated delivery date.
|
||||
|
||||
3. Given valid shipping info is entered, when the user proceeds to payment, then they should see available payment methods (credit card, PayPal).
|
||||
|
||||
4. Given valid payment details are entered, when the user confirms the order, then the payment should be processed and order confirmation displayed within 5 seconds.
|
||||
|
||||
5. Given a successful order, when confirmation is shown, then the user should receive an email receipt and their cart should be emptied.
|
||||
|
||||
6. Given a payment failure, when the error occurs, then the user should see a clear error message and their cart should remain intact.
|
||||
|
||||
7. Given the user closes the browser during checkout, when they return, then their cart contents should still be available.`,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Map of enhancement modes to their system prompts
|
||||
*/
|
||||
const SYSTEM_PROMPTS: Record<EnhancementMode, string> = {
|
||||
improve: IMPROVE_SYSTEM_PROMPT,
|
||||
technical: TECHNICAL_SYSTEM_PROMPT,
|
||||
simplify: SIMPLIFY_SYSTEM_PROMPT,
|
||||
acceptance: ACCEPTANCE_SYSTEM_PROMPT,
|
||||
};
|
||||
|
||||
/**
|
||||
* Map of enhancement modes to their few-shot examples
|
||||
*/
|
||||
const EXAMPLES: Record<EnhancementMode, EnhancementExample[]> = {
|
||||
improve: IMPROVE_EXAMPLES,
|
||||
technical: TECHNICAL_EXAMPLES,
|
||||
simplify: SIMPLIFY_EXAMPLES,
|
||||
acceptance: ACCEPTANCE_EXAMPLES,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enhancement prompt configuration returned by getEnhancementPrompt
|
||||
*/
|
||||
export interface EnhancementPromptConfig {
|
||||
/** System prompt for the enhancement mode */
|
||||
systemPrompt: string;
|
||||
/** Description of what this mode does */
|
||||
description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptions for each enhancement mode
|
||||
*/
|
||||
const MODE_DESCRIPTIONS: Record<EnhancementMode, string> = {
|
||||
improve: "Transform vague requests into clear, actionable task descriptions",
|
||||
technical: "Add implementation details and technical specifications",
|
||||
simplify: "Make verbose descriptions concise and focused",
|
||||
acceptance: "Add testable acceptance criteria to task descriptions",
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the enhancement prompt configuration for a given mode
|
||||
*
|
||||
* @param mode - The enhancement mode (falls back to 'improve' if invalid)
|
||||
* @returns The enhancement prompt configuration
|
||||
*/
|
||||
export function getEnhancementPrompt(mode: string): EnhancementPromptConfig {
|
||||
const normalizedMode = mode.toLowerCase() as EnhancementMode;
|
||||
const validMode = normalizedMode in SYSTEM_PROMPTS ? normalizedMode : "improve";
|
||||
|
||||
return {
|
||||
systemPrompt: SYSTEM_PROMPTS[validMode],
|
||||
description: MODE_DESCRIPTIONS[validMode],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system prompt for a specific enhancement mode
|
||||
*
|
||||
* @param mode - The enhancement mode to get the prompt for
|
||||
* @returns The system prompt string
|
||||
*/
|
||||
export function getSystemPrompt(mode: EnhancementMode): string {
|
||||
return SYSTEM_PROMPTS[mode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the few-shot examples for a specific enhancement mode
|
||||
*
|
||||
* @param mode - The enhancement mode to get examples for
|
||||
* @returns Array of input/output example pairs
|
||||
*/
|
||||
export function getExamples(mode: EnhancementMode): EnhancementExample[] {
|
||||
return EXAMPLES[mode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a user prompt for enhancement with optional few-shot examples
|
||||
*
|
||||
* @param mode - The enhancement mode
|
||||
* @param text - The text to enhance
|
||||
* @param includeExamples - Whether to include few-shot examples (default: true)
|
||||
* @returns The formatted user prompt string
|
||||
*/
|
||||
export function buildUserPrompt(
|
||||
mode: EnhancementMode,
|
||||
text: string,
|
||||
includeExamples: boolean = true
|
||||
): string {
|
||||
const examples = includeExamples ? getExamples(mode) : [];
|
||||
|
||||
if (examples.length === 0) {
|
||||
return `Please enhance the following task description:\n\n${text}`;
|
||||
}
|
||||
|
||||
// Build few-shot examples section
|
||||
const examplesSection = examples
|
||||
.map(
|
||||
(example, index) =>
|
||||
`Example ${index + 1}:\nInput: ${example.input}\nOutput: ${example.output}`
|
||||
)
|
||||
.join("\n\n---\n\n");
|
||||
|
||||
return `Here are some examples of how to enhance task descriptions:
|
||||
|
||||
${examplesSection}
|
||||
|
||||
---
|
||||
|
||||
Now, please enhance the following task description:
|
||||
|
||||
${text}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a mode is a valid enhancement mode
|
||||
*
|
||||
* @param mode - The mode to check
|
||||
* @returns True if the mode is valid
|
||||
*/
|
||||
export function isValidEnhancementMode(mode: string): mode is EnhancementMode {
|
||||
return mode in SYSTEM_PROMPTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available enhancement modes
|
||||
*
|
||||
* @returns Array of available enhancement mode names
|
||||
*/
|
||||
export function getAvailableEnhancementModes(): EnhancementMode[] {
|
||||
return Object.keys(SYSTEM_PROMPTS) as EnhancementMode[];
|
||||
}
|
||||
22
apps/server/src/routes/enhance-prompt/index.ts
Normal file
22
apps/server/src/routes/enhance-prompt/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Enhance prompt routes - HTTP API for AI-powered text enhancement
|
||||
*
|
||||
* Provides endpoints for enhancing user input text using Claude AI
|
||||
* with different enhancement modes (improve, expand, simplify, etc.)
|
||||
*/
|
||||
|
||||
import { Router } from "express";
|
||||
import { createEnhanceHandler } from "./routes/enhance.js";
|
||||
|
||||
/**
|
||||
* Create the enhance-prompt router
|
||||
*
|
||||
* @returns Express router with enhance-prompt endpoints
|
||||
*/
|
||||
export function createEnhancePromptRoutes(): Router {
|
||||
const router = Router();
|
||||
|
||||
router.post("/", createEnhanceHandler());
|
||||
|
||||
return router;
|
||||
}
|
||||
195
apps/server/src/routes/enhance-prompt/routes/enhance.ts
Normal file
195
apps/server/src/routes/enhance-prompt/routes/enhance.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* POST /enhance-prompt endpoint - Enhance user input text
|
||||
*
|
||||
* Uses Claude AI to enhance text based on the specified enhancement mode.
|
||||
* Supports modes: improve, technical, simplify, acceptance
|
||||
*/
|
||||
|
||||
import type { Request, Response } from "express";
|
||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { createLogger } from "../../../lib/logger.js";
|
||||
import {
|
||||
getSystemPrompt,
|
||||
buildUserPrompt,
|
||||
isValidEnhancementMode,
|
||||
type EnhancementMode,
|
||||
} from "../../../lib/enhancement-prompts.js";
|
||||
import { resolveModelString, CLAUDE_MODEL_MAP } from "../../../lib/model-resolver.js";
|
||||
|
||||
const logger = createLogger("EnhancePrompt");
|
||||
|
||||
/**
|
||||
* Request body for the enhance endpoint
|
||||
*/
|
||||
interface EnhanceRequestBody {
|
||||
/** The original text to enhance */
|
||||
originalText: string;
|
||||
/** The enhancement mode to apply */
|
||||
enhancementMode: string;
|
||||
/** Optional model override */
|
||||
model?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Success response from the enhance endpoint
|
||||
*/
|
||||
interface EnhanceSuccessResponse {
|
||||
success: true;
|
||||
enhancedText: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error response from the enhance endpoint
|
||||
*/
|
||||
interface EnhanceErrorResponse {
|
||||
success: false;
|
||||
error: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract text content from Claude SDK response messages
|
||||
*
|
||||
* @param stream - The async iterable from the query function
|
||||
* @returns The extracted text content
|
||||
*/
|
||||
async function extractTextFromStream(
|
||||
stream: AsyncIterable<{
|
||||
type: string;
|
||||
subtype?: string;
|
||||
result?: string;
|
||||
message?: {
|
||||
content?: Array<{ type: string; text?: string }>;
|
||||
};
|
||||
}>
|
||||
): Promise<string> {
|
||||
let responseText = "";
|
||||
|
||||
for await (const msg of stream) {
|
||||
if (msg.type === "assistant" && msg.message?.content) {
|
||||
for (const block of msg.message.content) {
|
||||
if (block.type === "text" && block.text) {
|
||||
responseText += block.text;
|
||||
}
|
||||
}
|
||||
} else if (msg.type === "result" && msg.subtype === "success") {
|
||||
responseText = msg.result || responseText;
|
||||
}
|
||||
}
|
||||
|
||||
return responseText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the enhance request handler
|
||||
*
|
||||
* @returns Express request handler for text enhancement
|
||||
*/
|
||||
export function createEnhanceHandler(): (
|
||||
req: Request,
|
||||
res: Response
|
||||
) => Promise<void> {
|
||||
return async (req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const { originalText, enhancementMode, model } =
|
||||
req.body as EnhanceRequestBody;
|
||||
|
||||
// Validate required fields
|
||||
if (!originalText || typeof originalText !== "string") {
|
||||
const response: EnhanceErrorResponse = {
|
||||
success: false,
|
||||
error: "originalText is required and must be a string",
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enhancementMode || typeof enhancementMode !== "string") {
|
||||
const response: EnhanceErrorResponse = {
|
||||
success: false,
|
||||
error: "enhancementMode is required and must be a string",
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate text is not empty
|
||||
const trimmedText = originalText.trim();
|
||||
if (trimmedText.length === 0) {
|
||||
const response: EnhanceErrorResponse = {
|
||||
success: false,
|
||||
error: "originalText cannot be empty",
|
||||
};
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate and normalize enhancement mode
|
||||
const normalizedMode = enhancementMode.toLowerCase();
|
||||
const validMode: EnhancementMode = isValidEnhancementMode(normalizedMode)
|
||||
? normalizedMode
|
||||
: "improve";
|
||||
|
||||
logger.info(
|
||||
`Enhancing text with mode: ${validMode}, length: ${trimmedText.length} chars`
|
||||
);
|
||||
|
||||
// Get the system prompt for this mode
|
||||
const systemPrompt = getSystemPrompt(validMode);
|
||||
|
||||
// Build the user prompt with few-shot examples
|
||||
// This helps the model understand this is text transformation, not a coding task
|
||||
const userPrompt = buildUserPrompt(validMode, trimmedText, true);
|
||||
|
||||
// Resolve the model - use the passed model, default to sonnet for quality
|
||||
const resolvedModel = resolveModelString(model, CLAUDE_MODEL_MAP.sonnet);
|
||||
|
||||
logger.debug(`Using model: ${resolvedModel}`);
|
||||
|
||||
// Call Claude SDK with minimal configuration for text transformation
|
||||
// Key: no tools, just text completion
|
||||
const stream = query({
|
||||
prompt: userPrompt,
|
||||
options: {
|
||||
model: resolvedModel,
|
||||
systemPrompt,
|
||||
maxTurns: 1,
|
||||
allowedTools: [],
|
||||
permissionMode: "acceptEdits",
|
||||
},
|
||||
});
|
||||
|
||||
// Extract the enhanced text from the response
|
||||
const enhancedText = await extractTextFromStream(stream);
|
||||
|
||||
if (!enhancedText || enhancedText.trim().length === 0) {
|
||||
logger.warn("Received empty response from Claude");
|
||||
const response: EnhanceErrorResponse = {
|
||||
success: false,
|
||||
error: "Failed to generate enhanced text - empty response",
|
||||
};
|
||||
res.status(500).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Enhancement complete, output length: ${enhancedText.length} chars`
|
||||
);
|
||||
|
||||
const response: EnhanceSuccessResponse = {
|
||||
success: true,
|
||||
enhancedText: enhancedText.trim(),
|
||||
};
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Unknown error occurred";
|
||||
logger.error("Enhancement failed:", errorMessage);
|
||||
|
||||
const response: EnhanceErrorResponse = {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
res.status(500).json(response);
|
||||
}
|
||||
};
|
||||
}
|
||||
233
apps/server/tests/unit/lib/enhancement-prompts.test.ts
Normal file
233
apps/server/tests/unit/lib/enhancement-prompts.test.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
getEnhancementPrompt,
|
||||
getSystemPrompt,
|
||||
getExamples,
|
||||
buildUserPrompt,
|
||||
isValidEnhancementMode,
|
||||
getAvailableEnhancementModes,
|
||||
IMPROVE_SYSTEM_PROMPT,
|
||||
TECHNICAL_SYSTEM_PROMPT,
|
||||
SIMPLIFY_SYSTEM_PROMPT,
|
||||
ACCEPTANCE_SYSTEM_PROMPT,
|
||||
IMPROVE_EXAMPLES,
|
||||
TECHNICAL_EXAMPLES,
|
||||
SIMPLIFY_EXAMPLES,
|
||||
ACCEPTANCE_EXAMPLES,
|
||||
type EnhancementMode,
|
||||
} from "@/lib/enhancement-prompts.js";
|
||||
|
||||
describe("enhancement-prompts.ts", () => {
|
||||
describe("System Prompt Constants", () => {
|
||||
it("should have non-empty improve system prompt", () => {
|
||||
expect(IMPROVE_SYSTEM_PROMPT).toBeDefined();
|
||||
expect(IMPROVE_SYSTEM_PROMPT.length).toBeGreaterThan(100);
|
||||
expect(IMPROVE_SYSTEM_PROMPT).toContain("ANALYZE");
|
||||
expect(IMPROVE_SYSTEM_PROMPT).toContain("CLARIFY");
|
||||
});
|
||||
|
||||
it("should have non-empty technical system prompt", () => {
|
||||
expect(TECHNICAL_SYSTEM_PROMPT).toBeDefined();
|
||||
expect(TECHNICAL_SYSTEM_PROMPT.length).toBeGreaterThan(100);
|
||||
expect(TECHNICAL_SYSTEM_PROMPT).toContain("technical");
|
||||
});
|
||||
|
||||
it("should have non-empty simplify system prompt", () => {
|
||||
expect(SIMPLIFY_SYSTEM_PROMPT).toBeDefined();
|
||||
expect(SIMPLIFY_SYSTEM_PROMPT.length).toBeGreaterThan(100);
|
||||
expect(SIMPLIFY_SYSTEM_PROMPT).toContain("simplify");
|
||||
});
|
||||
|
||||
it("should have non-empty acceptance system prompt", () => {
|
||||
expect(ACCEPTANCE_SYSTEM_PROMPT).toBeDefined();
|
||||
expect(ACCEPTANCE_SYSTEM_PROMPT.length).toBeGreaterThan(100);
|
||||
expect(ACCEPTANCE_SYSTEM_PROMPT).toContain("acceptance criteria");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Example Constants", () => {
|
||||
it("should have improve examples with input and output", () => {
|
||||
expect(IMPROVE_EXAMPLES).toBeDefined();
|
||||
expect(IMPROVE_EXAMPLES.length).toBeGreaterThan(0);
|
||||
IMPROVE_EXAMPLES.forEach((example) => {
|
||||
expect(example.input).toBeDefined();
|
||||
expect(example.output).toBeDefined();
|
||||
expect(example.input.length).toBeGreaterThan(0);
|
||||
expect(example.output.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("should have technical examples with input and output", () => {
|
||||
expect(TECHNICAL_EXAMPLES).toBeDefined();
|
||||
expect(TECHNICAL_EXAMPLES.length).toBeGreaterThan(0);
|
||||
TECHNICAL_EXAMPLES.forEach((example) => {
|
||||
expect(example.input).toBeDefined();
|
||||
expect(example.output).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("should have simplify examples with input and output", () => {
|
||||
expect(SIMPLIFY_EXAMPLES).toBeDefined();
|
||||
expect(SIMPLIFY_EXAMPLES.length).toBeGreaterThan(0);
|
||||
SIMPLIFY_EXAMPLES.forEach((example) => {
|
||||
expect(example.input).toBeDefined();
|
||||
expect(example.output).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("should have acceptance examples with input and output", () => {
|
||||
expect(ACCEPTANCE_EXAMPLES).toBeDefined();
|
||||
expect(ACCEPTANCE_EXAMPLES.length).toBeGreaterThan(0);
|
||||
ACCEPTANCE_EXAMPLES.forEach((example) => {
|
||||
expect(example.input).toBeDefined();
|
||||
expect(example.output).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getEnhancementPrompt", () => {
|
||||
it("should return config for improve mode", () => {
|
||||
const config = getEnhancementPrompt("improve");
|
||||
expect(config.systemPrompt).toBe(IMPROVE_SYSTEM_PROMPT);
|
||||
expect(config.description).toContain("clear");
|
||||
});
|
||||
|
||||
it("should return config for technical mode", () => {
|
||||
const config = getEnhancementPrompt("technical");
|
||||
expect(config.systemPrompt).toBe(TECHNICAL_SYSTEM_PROMPT);
|
||||
expect(config.description).toContain("technical");
|
||||
});
|
||||
|
||||
it("should return config for simplify mode", () => {
|
||||
const config = getEnhancementPrompt("simplify");
|
||||
expect(config.systemPrompt).toBe(SIMPLIFY_SYSTEM_PROMPT);
|
||||
expect(config.description).toContain("concise");
|
||||
});
|
||||
|
||||
it("should return config for acceptance mode", () => {
|
||||
const config = getEnhancementPrompt("acceptance");
|
||||
expect(config.systemPrompt).toBe(ACCEPTANCE_SYSTEM_PROMPT);
|
||||
expect(config.description).toContain("acceptance");
|
||||
});
|
||||
|
||||
it("should handle case-insensitive mode", () => {
|
||||
const config = getEnhancementPrompt("IMPROVE");
|
||||
expect(config.systemPrompt).toBe(IMPROVE_SYSTEM_PROMPT);
|
||||
});
|
||||
|
||||
it("should fall back to improve for invalid mode", () => {
|
||||
const config = getEnhancementPrompt("invalid-mode");
|
||||
expect(config.systemPrompt).toBe(IMPROVE_SYSTEM_PROMPT);
|
||||
});
|
||||
|
||||
it("should fall back to improve for empty string", () => {
|
||||
const config = getEnhancementPrompt("");
|
||||
expect(config.systemPrompt).toBe(IMPROVE_SYSTEM_PROMPT);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getSystemPrompt", () => {
|
||||
it("should return correct system prompt for each mode", () => {
|
||||
expect(getSystemPrompt("improve")).toBe(IMPROVE_SYSTEM_PROMPT);
|
||||
expect(getSystemPrompt("technical")).toBe(TECHNICAL_SYSTEM_PROMPT);
|
||||
expect(getSystemPrompt("simplify")).toBe(SIMPLIFY_SYSTEM_PROMPT);
|
||||
expect(getSystemPrompt("acceptance")).toBe(ACCEPTANCE_SYSTEM_PROMPT);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getExamples", () => {
|
||||
it("should return correct examples for each mode", () => {
|
||||
expect(getExamples("improve")).toBe(IMPROVE_EXAMPLES);
|
||||
expect(getExamples("technical")).toBe(TECHNICAL_EXAMPLES);
|
||||
expect(getExamples("simplify")).toBe(SIMPLIFY_EXAMPLES);
|
||||
expect(getExamples("acceptance")).toBe(ACCEPTANCE_EXAMPLES);
|
||||
});
|
||||
|
||||
it("should return arrays with example objects", () => {
|
||||
const modes: EnhancementMode[] = ["improve", "technical", "simplify", "acceptance"];
|
||||
modes.forEach((mode) => {
|
||||
const examples = getExamples(mode);
|
||||
expect(Array.isArray(examples)).toBe(true);
|
||||
expect(examples.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildUserPrompt", () => {
|
||||
const testText = "Add a logout button";
|
||||
|
||||
it("should build prompt with examples by default", () => {
|
||||
const prompt = buildUserPrompt("improve", testText);
|
||||
expect(prompt).toContain("Example 1:");
|
||||
expect(prompt).toContain(testText);
|
||||
expect(prompt).toContain("Now, please enhance the following task description:");
|
||||
});
|
||||
|
||||
it("should build prompt without examples when includeExamples is false", () => {
|
||||
const prompt = buildUserPrompt("improve", testText, false);
|
||||
expect(prompt).not.toContain("Example 1:");
|
||||
expect(prompt).toContain(testText);
|
||||
expect(prompt).toContain("Please enhance the following task description:");
|
||||
});
|
||||
|
||||
it("should include all examples for improve mode", () => {
|
||||
const prompt = buildUserPrompt("improve", testText);
|
||||
IMPROVE_EXAMPLES.forEach((example, index) => {
|
||||
expect(prompt).toContain(`Example ${index + 1}:`);
|
||||
expect(prompt).toContain(example.input);
|
||||
});
|
||||
});
|
||||
|
||||
it("should include separator between examples", () => {
|
||||
const prompt = buildUserPrompt("improve", testText);
|
||||
expect(prompt).toContain("---");
|
||||
});
|
||||
|
||||
it("should work with all enhancement modes", () => {
|
||||
const modes: EnhancementMode[] = ["improve", "technical", "simplify", "acceptance"];
|
||||
modes.forEach((mode) => {
|
||||
const prompt = buildUserPrompt(mode, testText);
|
||||
expect(prompt).toContain(testText);
|
||||
expect(prompt.length).toBeGreaterThan(100);
|
||||
});
|
||||
});
|
||||
|
||||
it("should preserve the original text exactly", () => {
|
||||
const specialText = "Add feature with special chars: <>&\"'";
|
||||
const prompt = buildUserPrompt("improve", specialText);
|
||||
expect(prompt).toContain(specialText);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidEnhancementMode", () => {
|
||||
it("should return true for valid modes", () => {
|
||||
expect(isValidEnhancementMode("improve")).toBe(true);
|
||||
expect(isValidEnhancementMode("technical")).toBe(true);
|
||||
expect(isValidEnhancementMode("simplify")).toBe(true);
|
||||
expect(isValidEnhancementMode("acceptance")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for invalid modes", () => {
|
||||
expect(isValidEnhancementMode("invalid")).toBe(false);
|
||||
expect(isValidEnhancementMode("IMPROVE")).toBe(false); // case-sensitive
|
||||
expect(isValidEnhancementMode("")).toBe(false);
|
||||
expect(isValidEnhancementMode("random")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAvailableEnhancementModes", () => {
|
||||
it("should return all four enhancement modes", () => {
|
||||
const modes = getAvailableEnhancementModes();
|
||||
expect(modes).toHaveLength(4);
|
||||
expect(modes).toContain("improve");
|
||||
expect(modes).toContain("technical");
|
||||
expect(modes).toContain("simplify");
|
||||
expect(modes).toContain("acceptance");
|
||||
});
|
||||
|
||||
it("should return an array", () => {
|
||||
const modes = getAvailableEnhancementModes();
|
||||
expect(Array.isArray(modes)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user