feat: add customizable AI prompts with enhanced UX

Add comprehensive prompt customization system allowing users to customize
all AI prompts (Auto Mode, Agent Runner, Backlog Plan, Enhancement) through
the Settings UI.

## Features

### Core Customization System
- New TypeScript types for prompt customization with enabled flag
- CustomPrompt interface with value and enabled state
- Prompts preserved even when disabled (no data loss)
- Merged prompt system (custom overrides defaults when enabled)
- Persistent storage in ~/.automaker/settings.json

### Settings UI
- New "Prompt Customization" section in Settings
- 4 tabs: Auto Mode, Agent, Backlog Plan, Enhancement
- Toggle-based editing (read-only default → editable custom)
- Dynamic textarea height based on prompt length (120px-600px)
- Visual state indicators (Custom/Default labels)

### Warning System
- Critical prompt warnings for Backlog Plan (JSON format requirement)
- Field-level warnings when editing critical prompts
- Info banners for Auto Mode planning markers
- Color-coded warnings (blue=info, amber=critical)

### Backend Integration
- Auto Mode service loads prompts from settings
- Agent service loads prompts from settings
- Backlog Plan service loads prompts from settings
- Enhancement endpoint loads prompts from settings
- Settings sync includes promptCustomization field

### Files Changed
- libs/types/src/prompts.ts - Type definitions
- libs/prompts/src/defaults.ts - Default prompt values
- libs/prompts/src/merge.ts - Merge utilities
- apps/ui/src/components/views/settings-view/prompts/ - UI components
- apps/server/src/lib/settings-helpers.ts - getPromptCustomization()
- All service files updated to use customizable prompts

## Technical Details

Prompt storage format:
```json
{
  "promptCustomization": {
    "autoMode": {
      "planningLite": {
        "value": "Custom prompt text...",
        "enabled": true
      }
    }
  }
}
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Stephan Rieche
2025-12-29 23:17:20 +01:00
parent 25c9259b50
commit bc0ef47323
20 changed files with 1338 additions and 265 deletions

View File

@@ -155,7 +155,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/enhance-prompt', createEnhancePromptRoutes(settingsService));
app.use('/api/worktree', createWorktreeRoutes());
app.use('/api/git', createGitRoutes());
app.use('/api/setup', createSetupRoutes());

View File

@@ -4,7 +4,13 @@
import type { SettingsService } from '../services/settings-service.js';
import type { ContextFilesResult, ContextFileInfo } from '@automaker/utils';
import type { MCPServerConfig, McpServerConfig } from '@automaker/types';
import type { MCPServerConfig, McpServerConfig, PromptCustomization } from '@automaker/types';
import {
mergeAutoModePrompts,
mergeAgentPrompts,
mergeBacklogPlanPrompts,
mergeEnhancementPrompts,
} from '@automaker/prompts';
/**
* Get the autoLoadClaudeMd setting, with project settings taking precedence over global.
@@ -255,3 +261,43 @@ function convertToSdkFormat(server: MCPServerConfig): McpServerConfig {
env: server.env,
};
}
/**
* Get prompt customization from global settings and merge with defaults.
* Returns prompts merged with built-in defaults - custom prompts override defaults.
*
* @param settingsService - Optional settings service instance
* @param logPrefix - Prefix for log messages
* @returns Promise resolving to merged prompts for all categories
*/
export async function getPromptCustomization(
settingsService?: SettingsService | null,
logPrefix = '[PromptHelper]'
): Promise<{
autoMode: ReturnType<typeof mergeAutoModePrompts>;
agent: ReturnType<typeof mergeAgentPrompts>;
backlogPlan: ReturnType<typeof mergeBacklogPlanPrompts>;
enhancement: ReturnType<typeof mergeEnhancementPrompts>;
}> {
let customization: PromptCustomization = {};
if (settingsService) {
try {
const globalSettings = await settingsService.getGlobalSettings();
customization = globalSettings.promptCustomization || {};
console.log(`${logPrefix} Loaded prompt customization from settings`);
} catch (error) {
console.error(`${logPrefix} Failed to load prompt customization:`, error);
// Fall through to use empty customization (all defaults)
}
} else {
console.log(`${logPrefix} SettingsService not available, using default prompts`);
}
return {
autoMode: mergeAutoModePrompts(customization.autoMode),
agent: mergeAgentPrompts(customization.agent),
backlogPlan: mergeBacklogPlanPrompts(customization.backlogPlan),
enhancement: mergeEnhancementPrompts(customization.enhancement),
};
}

View File

@@ -8,7 +8,7 @@ import { FeatureLoader } from '../../services/feature-loader.js';
import { ProviderFactory } from '../../providers/provider-factory.js';
import { logger, setRunningState, getErrorMessage } from './common.js';
import type { SettingsService } from '../../services/settings-service.js';
import { getAutoLoadClaudeMdSetting } from '../../lib/settings-helpers.js';
import { getAutoLoadClaudeMdSetting, getPromptCustomization } from '../../lib/settings-helpers.js';
const featureLoader = new FeatureLoader();
@@ -79,72 +79,17 @@ export async function generateBacklogPlan(
content: `Loaded ${features.length} features from backlog`,
});
// Load prompts from settings
const prompts = await getPromptCustomization(settingsService, '[BacklogPlan]');
// Build the system prompt
const systemPrompt = `You are an AI assistant helping to modify a software project's feature backlog.
You will be given the current list of features and a user request to modify the backlog.
const systemPrompt = prompts.backlogPlan.systemPrompt;
IMPORTANT CONTEXT (automatically injected):
- Remember to update the dependency graph if deleting existing features
- Remember to define dependencies on new features hooked into relevant existing ones
- Maintain dependency graph integrity (no orphaned dependencies)
- When deleting a feature, identify which other features depend on it
Your task is to analyze the request and produce a structured JSON plan with:
1. Features to ADD (include title, description, category, and dependencies)
2. Features to UPDATE (specify featureId and the updates)
3. Features to DELETE (specify featureId)
4. A summary of the changes
5. Any dependency updates needed (removed dependencies due to deletions, new dependencies for new features)
Respond with ONLY a JSON object in this exact format:
\`\`\`json
{
"changes": [
{
"type": "add",
"feature": {
"title": "Feature title",
"description": "Feature description",
"category": "Category name",
"dependencies": ["existing-feature-id"],
"priority": 1
},
"reason": "Why this feature should be added"
},
{
"type": "update",
"featureId": "existing-feature-id",
"feature": {
"title": "Updated title"
},
"reason": "Why this feature should be updated"
},
{
"type": "delete",
"featureId": "feature-id-to-delete",
"reason": "Why this feature should be deleted"
}
],
"summary": "Brief overview of all proposed changes",
"dependencyUpdates": [
{
"featureId": "feature-that-depended-on-deleted",
"removedDependencies": ["deleted-feature-id"],
"addedDependencies": []
}
]
}
\`\`\``;
// Build the user prompt
const userPrompt = `Current Features in Backlog:
${formatFeaturesForPrompt(features)}
---
User Request: ${prompt}
Please analyze the current backlog and the user's request, then provide a JSON plan for the modifications.`;
// Build the user prompt from template
const currentFeatures = formatFeaturesForPrompt(features);
const userPrompt = prompts.backlogPlan.userPromptTemplate
.replace('{{currentFeatures}}', currentFeatures)
.replace('{{userRequest}}', prompt);
events.emit('backlog-plan:event', {
type: 'backlog_plan_progress',

View File

@@ -6,17 +6,19 @@
*/
import { Router } from 'express';
import type { SettingsService } from '../../services/settings-service.js';
import { createEnhanceHandler } from './routes/enhance.js';
/**
* Create the enhance-prompt router
*
* @param settingsService - Settings service for loading custom prompts
* @returns Express router with enhance-prompt endpoints
*/
export function createEnhancePromptRoutes(): Router {
export function createEnhancePromptRoutes(settingsService?: SettingsService): Router {
const router = Router();
router.post('/', createEnhanceHandler());
router.post('/', createEnhanceHandler(settingsService));
return router;
}

View File

@@ -10,8 +10,9 @@ import { query } from '@anthropic-ai/claude-agent-sdk';
import { createLogger } from '@automaker/utils';
import { resolveModelString } from '@automaker/model-resolver';
import { CLAUDE_MODEL_MAP } from '@automaker/types';
import type { SettingsService } from '../../../services/settings-service.js';
import { getPromptCustomization } from '../../../lib/settings-helpers.js';
import {
getSystemPrompt,
buildUserPrompt,
isValidEnhancementMode,
type EnhancementMode,
@@ -83,9 +84,12 @@ async function extractTextFromStream(
/**
* Create the enhance request handler
*
* @param settingsService - Optional settings service for loading custom prompts
* @returns Express request handler for text enhancement
*/
export function createEnhanceHandler(): (req: Request, res: Response) => Promise<void> {
export function createEnhanceHandler(
settingsService?: SettingsService
): (req: Request, res: Response) => Promise<void> {
return async (req: Request, res: Response): Promise<void> => {
try {
const { originalText, enhancementMode, model } = req.body as EnhanceRequestBody;
@@ -128,8 +132,20 @@ export function createEnhanceHandler(): (req: Request, res: Response) => Promise
logger.info(`Enhancing text with mode: ${validMode}, length: ${trimmedText.length} chars`);
// Get the system prompt for this mode
const systemPrompt = getSystemPrompt(validMode);
// Load enhancement prompts from settings (merges custom + defaults)
const prompts = await getPromptCustomization(settingsService, '[EnhancePrompt]');
// Get the system prompt for this mode from merged prompts
const systemPrompt =
validMode === 'improve'
? prompts.enhancement.improveSystemPrompt
: validMode === 'technical'
? prompts.enhancement.technicalSystemPrompt
: validMode === 'simplify'
? prompts.enhancement.simplifySystemPrompt
: prompts.enhancement.acceptanceSystemPrompt;
logger.debug(`Using ${validMode} system prompt (length: ${systemPrompt.length} chars)`);
// Build the user prompt with few-shot examples
// This helps the model understand this is text transformation, not a coding task

View File

@@ -23,6 +23,7 @@ import {
filterClaudeMdFromContext,
getMCPServersFromSettings,
getMCPPermissionSettings,
getPromptCustomization,
} from '../lib/settings-helpers.js';
interface Message {
@@ -75,6 +76,7 @@ export class AgentService {
private metadataFile: string;
private events: EventEmitter;
private settingsService: SettingsService | null = null;
private agentSystemPrompt: string | null = null;
constructor(dataDir: string, events: EventEmitter, settingsService?: SettingsService) {
this.stateDir = path.join(dataDir, 'agent-sessions');
@@ -246,7 +248,7 @@ export class AgentService {
const contextFilesPrompt = filterClaudeMdFromContext(contextResult, autoLoadClaudeMd);
// Build combined system prompt with base prompt and context files
const baseSystemPrompt = this.getSystemPrompt();
const baseSystemPrompt = await this.getSystemPrompt();
const combinedSystemPrompt = contextFilesPrompt
? `${contextFilesPrompt}\n\n${baseSystemPrompt}`
: baseSystemPrompt;
@@ -781,38 +783,13 @@ export class AgentService {
this.events.emit('agent:stream', { sessionId, ...data });
}
private getSystemPrompt(): string {
return `You are an AI assistant helping users build software. You are part of the Automaker application,
which is designed to help developers plan, design, and implement software projects autonomously.
**Feature Storage:**
Features are stored in .automaker/features/{id}/feature.json - each feature has its own folder.
Use the UpdateFeatureStatus tool to manage features, not direct file edits.
Your role is to:
- Help users define their project requirements and specifications
- Ask clarifying questions to better understand their needs
- Suggest technical approaches and architectures
- Guide them through the development process
- Be conversational and helpful
- Write, edit, and modify code files as requested
- Execute commands and tests
- Search and analyze the codebase
When discussing projects, help users think through:
- Core functionality and features
- Technical stack choices
- Data models and architecture
- User experience considerations
- Testing strategies
You have full access to the codebase and can:
- Read files to understand existing code
- Write new files
- Edit existing files
- Run bash commands
- Search for code patterns
- Execute tests and builds`;
private async getSystemPrompt(): Promise<string> {
// Load from settings if not already cached
if (!this.agentSystemPrompt) {
const prompts = await getPromptCustomization(this.settingsService, '[AgentService]');
this.agentSystemPrompt = prompts.agent.systemPrompt;
}
return this.agentSystemPrompt;
}
private generateId(): string {

View File

@@ -39,6 +39,7 @@ import {
filterClaudeMdFromContext,
getMCPServersFromSettings,
getMCPPermissionSettings,
getPromptCustomization,
} from '../lib/settings-helpers.js';
const execAsync = promisify(exec);
@@ -67,162 +68,6 @@ interface PlanSpec {
tasks?: ParsedTask[];
}
const PLANNING_PROMPTS = {
lite: `## Planning Phase (Lite Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the plan. Start DIRECTLY with the planning outline format below. Silently analyze the codebase first, then output ONLY the structured plan.
Create a brief planning outline:
1. **Goal**: What are we accomplishing? (1 sentence)
2. **Approach**: How will we do it? (2-3 sentences)
3. **Files to Touch**: List files and what changes
4. **Tasks**: Numbered task list (3-7 items)
5. **Risks**: Any gotchas to watch for
After generating the outline, output:
"[PLAN_GENERATED] Planning outline complete."
Then proceed with implementation.`,
lite_with_approval: `## Planning Phase (Lite Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the plan. Start DIRECTLY with the planning outline format below. Silently analyze the codebase first, then output ONLY the structured plan.
Create a brief planning outline:
1. **Goal**: What are we accomplishing? (1 sentence)
2. **Approach**: How will we do it? (2-3 sentences)
3. **Files to Touch**: List files and what changes
4. **Tasks**: Numbered task list (3-7 items)
5. **Risks**: Any gotchas to watch for
After generating the outline, output:
"[SPEC_GENERATED] Please review the planning outline above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until you receive explicit approval.`,
spec: `## Specification Phase (Spec Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the spec. Start DIRECTLY with the specification format below. Silently analyze the codebase first, then output ONLY the structured specification.
Generate a specification with an actionable task breakdown. WAIT for approval before implementing.
### Specification Format
1. **Problem**: What problem are we solving? (user perspective)
2. **Solution**: Brief approach (1-2 sentences)
3. **Acceptance Criteria**: 3-5 items in GIVEN-WHEN-THEN format
- GIVEN [context], WHEN [action], THEN [outcome]
4. **Files to Modify**:
| File | Purpose | Action |
|------|---------|--------|
| path/to/file | description | create/modify/delete |
5. **Implementation Tasks**:
Use this EXACT format for each task (the system will parse these):
\`\`\`tasks
- [ ] T001: [Description] | File: [path/to/file]
- [ ] T002: [Description] | File: [path/to/file]
- [ ] T003: [Description] | File: [path/to/file]
\`\`\`
Task ID rules:
- Sequential: T001, T002, T003, etc.
- Description: Clear action (e.g., "Create user model", "Add API endpoint")
- File: Primary file affected (helps with context)
- Order by dependencies (foundational tasks first)
6. **Verification**: How to confirm feature works
After generating the spec, output on its own line:
"[SPEC_GENERATED] Please review the specification above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until you receive explicit approval.
When approved, execute tasks SEQUENTIALLY in order. For each task:
1. BEFORE starting, output: "[TASK_START] T###: Description"
2. Implement the task
3. AFTER completing, output: "[TASK_COMPLETE] T###: Brief summary"
This allows real-time progress tracking during implementation.`,
full: `## Full Specification Phase (Full SDD Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the spec. Start DIRECTLY with the specification format below. Silently analyze the codebase first, then output ONLY the structured specification.
Generate a comprehensive specification with phased task breakdown. WAIT for approval before implementing.
### Specification Format
1. **Problem Statement**: 2-3 sentences from user perspective
2. **User Story**: As a [user], I want [goal], so that [benefit]
3. **Acceptance Criteria**: Multiple scenarios with GIVEN-WHEN-THEN
- **Happy Path**: GIVEN [context], WHEN [action], THEN [expected outcome]
- **Edge Cases**: GIVEN [edge condition], WHEN [action], THEN [handling]
- **Error Handling**: GIVEN [error condition], WHEN [action], THEN [error response]
4. **Technical Context**:
| Aspect | Value |
|--------|-------|
| Affected Files | list of files |
| Dependencies | external libs if any |
| Constraints | technical limitations |
| Patterns to Follow | existing patterns in codebase |
5. **Non-Goals**: What this feature explicitly does NOT include
6. **Implementation Tasks**:
Use this EXACT format for each task (the system will parse these):
\`\`\`tasks
## Phase 1: Foundation
- [ ] T001: [Description] | File: [path/to/file]
- [ ] T002: [Description] | File: [path/to/file]
## Phase 2: Core Implementation
- [ ] T003: [Description] | File: [path/to/file]
- [ ] T004: [Description] | File: [path/to/file]
## Phase 3: Integration & Testing
- [ ] T005: [Description] | File: [path/to/file]
- [ ] T006: [Description] | File: [path/to/file]
\`\`\`
Task ID rules:
- Sequential across all phases: T001, T002, T003, etc.
- Description: Clear action verb + target
- File: Primary file affected
- Order by dependencies within each phase
- Phase structure helps organize complex work
7. **Success Metrics**: How we know it's done (measurable criteria)
8. **Risks & Mitigations**:
| Risk | Mitigation |
|------|------------|
| description | approach |
After generating the spec, output on its own line:
"[SPEC_GENERATED] Please review the comprehensive specification above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until you receive explicit approval.
When approved, execute tasks SEQUENTIALLY by phase. For each task:
1. BEFORE starting, output: "[TASK_START] T###: Description"
2. Implement the task
3. AFTER completing, output: "[TASK_COMPLETE] T###: Brief summary"
After completing all tasks in a phase, output:
"[PHASE_COMPLETE] Phase N complete"
This allows real-time progress tracking during implementation.`,
};
/**
* Parse tasks from generated spec content
* Looks for the ```tasks code block and extracts task lines
@@ -355,6 +200,7 @@ export class AutoModeService {
private config: AutoModeConfig | null = null;
private pendingApprovals = new Map<string, PendingApproval>();
private settingsService: SettingsService | null = null;
private planningPrompts: Record<string, string> | null = null;
constructor(events: EventEmitter, settingsService?: SettingsService) {
this.events = events;
@@ -593,7 +439,7 @@ export class AutoModeService {
} else {
// Normal flow: build prompt with planning phase
const featurePrompt = this.buildFeaturePrompt(feature);
const planningPrefix = this.getPlanningPromptPrefix(feature);
const planningPrefix = await this.getPlanningPromptPrefix(feature);
prompt = planningPrefix + featurePrompt;
// Emit planning mode info
@@ -1756,23 +1602,43 @@ Format your response as a structured markdown document.`;
return firstLine.substring(0, 57) + '...';
}
/**
* Load planning prompts from settings
*/
private async loadPlanningPrompts(): Promise<void> {
if (this.planningPrompts) {
return; // Already loaded
}
const prompts = await getPromptCustomization(this.settingsService, '[AutoMode]');
this.planningPrompts = {
lite: prompts.autoMode.planningLite,
lite_with_approval: prompts.autoMode.planningLiteWithApproval,
spec: prompts.autoMode.planningSpec,
full: prompts.autoMode.planningFull,
};
}
/**
* Get the planning prompt prefix based on feature's planning mode
*/
private getPlanningPromptPrefix(feature: Feature): string {
private async getPlanningPromptPrefix(feature: Feature): Promise<string> {
const mode = feature.planningMode || 'skip';
if (mode === 'skip') {
return ''; // No planning phase
}
// Load prompts if not already loaded
await this.loadPlanningPrompts();
// For lite mode, use the approval variant if requirePlanApproval is true
let promptKey: string = mode;
if (mode === 'lite' && feature.requirePlanApproval === true) {
promptKey = 'lite_with_approval';
}
const planningPrompt = PLANNING_PROMPTS[promptKey as keyof typeof PLANNING_PROMPTS];
const planningPrompt = this.planningPrompts![promptKey];
if (!planningPrompt) {
return '';
}

View File

@@ -19,6 +19,7 @@ import { KeyboardShortcutsSection } from './settings-view/keyboard-shortcuts/key
import { FeatureDefaultsSection } from './settings-view/feature-defaults/feature-defaults-section';
import { DangerZoneSection } from './settings-view/danger-zone/danger-zone-section';
import { MCPServersSection } from './settings-view/mcp-servers';
import { PromptCustomizationSection } from './settings-view/prompts';
import type { Project as SettingsProject, Theme } from './settings-view/shared/types';
import type { Project as ElectronProject } from '@/lib/electron';
@@ -53,6 +54,8 @@ export function SettingsView() {
setAutoLoadClaudeMd,
enableSandboxMode,
setEnableSandboxMode,
promptCustomization,
setPromptCustomization,
} = useAppStore();
// Hide usage tracking when using API key (only show for Claude Code CLI users)
@@ -119,6 +122,13 @@ export function SettingsView() {
);
case 'mcp-servers':
return <MCPServersSection />;
case 'prompts':
return (
<PromptCustomizationSection
promptCustomization={promptCustomization}
onPromptCustomizationChange={setPromptCustomization}
/>
);
case 'ai-enhancement':
return <AIEnhancementSection />;
case 'appearance':

View File

@@ -10,6 +10,7 @@ import {
Trash2,
Sparkles,
Plug,
MessageSquareText,
} from 'lucide-react';
import type { SettingsViewId } from '../hooks/use-settings-view';
@@ -24,6 +25,7 @@ export const NAV_ITEMS: NavigationItem[] = [
{ id: 'api-keys', label: 'API Keys', icon: Key },
{ id: 'claude', label: 'Claude', icon: Terminal },
{ id: 'mcp-servers', label: 'MCP Servers', icon: Plug },
{ id: 'prompts', label: 'Prompt Customization', icon: MessageSquareText },
{ id: 'ai-enhancement', label: 'AI Enhancement', icon: Sparkles },
{ id: 'appearance', label: 'Appearance', icon: Palette },
{ id: 'terminal', label: 'Terminal', icon: SquareTerminal },

View File

@@ -4,6 +4,7 @@ export type SettingsViewId =
| 'api-keys'
| 'claude'
| 'mcp-servers'
| 'prompts'
| 'ai-enhancement'
| 'appearance'
| 'terminal'

View File

@@ -0,0 +1 @@
export { PromptCustomizationSection } from './prompt-customization-section';

View File

@@ -0,0 +1,445 @@
import { useState } from 'react';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import {
MessageSquareText,
Bot,
KanbanSquare,
Sparkles,
RotateCcw,
Info,
AlertTriangle,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import type { PromptCustomization, CustomPrompt } from '@automaker/types';
import {
DEFAULT_AUTO_MODE_PROMPTS,
DEFAULT_AGENT_PROMPTS,
DEFAULT_BACKLOG_PLAN_PROMPTS,
DEFAULT_ENHANCEMENT_PROMPTS,
} from '@automaker/prompts';
interface PromptCustomizationSectionProps {
promptCustomization?: PromptCustomization;
onPromptCustomizationChange: (customization: PromptCustomization) => void;
}
interface PromptFieldProps {
label: string;
description: string;
defaultValue: string;
customValue?: CustomPrompt;
onCustomValueChange: (value: CustomPrompt | undefined) => void;
critical?: boolean; // Whether this prompt requires strict output format
}
/**
* Calculate dynamic minimum height based on content length
* Ensures long prompts have adequate space
*/
function calculateMinHeight(text: string): string {
const lines = text.split('\n').length;
const estimatedLines = Math.max(lines, Math.ceil(text.length / 80));
// Min 120px, scales up for longer content, max 600px
const minHeight = Math.min(Math.max(120, estimatedLines * 20), 600);
return `${minHeight}px`;
}
/**
* PromptField Component
*
* Shows a prompt with a toggle to switch between default and custom mode.
* - Toggle OFF: Shows default prompt in read-only mode, custom value is preserved but not used
* - Toggle ON: Allows editing, custom value is used instead of default
*
* IMPORTANT: Custom value is ALWAYS preserved, even when toggle is OFF.
* This prevents users from losing their work when temporarily switching to default.
*/
function PromptField({
label,
description,
defaultValue,
customValue,
onCustomValueChange,
critical = false,
}: PromptFieldProps) {
const isEnabled = customValue?.enabled ?? false;
const displayValue = isEnabled ? (customValue?.value ?? defaultValue) : defaultValue;
const minHeight = calculateMinHeight(displayValue);
const handleToggle = (enabled: boolean) => {
if (enabled) {
// Enable custom mode - preserve existing custom value or initialize with default
const value = customValue?.value ?? defaultValue;
onCustomValueChange({ value, enabled: true });
} else {
// Disable custom mode - preserve custom value but mark as disabled
const value = customValue?.value ?? defaultValue;
onCustomValueChange({ value, enabled: false });
}
};
const handleTextChange = (newValue: string) => {
// Only allow editing when enabled
if (isEnabled) {
onCustomValueChange({ value: newValue, enabled: true });
}
};
return (
<div className="space-y-2">
{critical && isEnabled && (
<div className="flex items-start gap-2 p-3 rounded-lg bg-amber-500/10 border border-amber-500/20">
<AlertTriangle className="w-4 h-4 text-amber-500 mt-0.5 flex-shrink-0" />
<div className="flex-1">
<p className="text-xs font-medium text-amber-500">Critical Prompt</p>
<p className="text-xs text-muted-foreground mt-1">
This prompt requires a specific output format. Changing it incorrectly may break
functionality. Only modify if you understand the expected structure.
</p>
</div>
</div>
)}
<div className="flex items-center justify-between">
<Label htmlFor={label} className="text-sm font-medium">
{label}
</Label>
<div className="flex items-center gap-2">
<span className="text-xs text-muted-foreground">{isEnabled ? 'Custom' : 'Default'}</span>
<Switch
checked={isEnabled}
onCheckedChange={handleToggle}
className="data-[state=checked]:bg-brand-500"
/>
</div>
</div>
<Textarea
id={label}
value={displayValue}
onChange={(e) => handleTextChange(e.target.value)}
readOnly={!isEnabled}
style={{ minHeight }}
className={cn(
'font-mono text-xs resize-y',
!isEnabled && 'cursor-not-allowed bg-muted/50 text-muted-foreground'
)}
/>
<p className="text-xs text-muted-foreground">{description}</p>
</div>
);
}
/**
* PromptCustomizationSection Component
*
* Allows users to customize AI prompts for different parts of the application:
* - Auto Mode (feature implementation)
* - Agent Runner (interactive chat)
* - Backlog Plan (Kanban planning)
* - Enhancement (feature description improvement)
*/
export function PromptCustomizationSection({
promptCustomization = {},
onPromptCustomizationChange,
}: PromptCustomizationSectionProps) {
const [activeTab, setActiveTab] = useState('auto-mode');
const updatePrompt = (
category: keyof PromptCustomization,
field: string,
value: CustomPrompt | undefined
) => {
const updated = {
...promptCustomization,
[category]: {
...promptCustomization[category],
[field]: value,
},
};
onPromptCustomizationChange(updated);
};
const resetToDefaults = (category: keyof PromptCustomization) => {
const updated = {
...promptCustomization,
[category]: {},
};
onPromptCustomizationChange(updated);
};
const resetAllToDefaults = () => {
onPromptCustomizationChange({});
};
return (
<div
className={cn(
'rounded-2xl overflow-hidden',
'border border-border/50',
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
'shadow-sm shadow-black/5'
)}
data-testid="prompt-customization-section"
>
{/* Header */}
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-brand-500/20 to-brand-600/10 flex items-center justify-center border border-brand-500/20">
<MessageSquareText className="w-5 h-5 text-brand-500" />
</div>
<h2 className="text-lg font-semibold text-foreground tracking-tight">
Prompt Customization
</h2>
</div>
<Button variant="outline" size="sm" onClick={resetAllToDefaults} className="gap-2">
<RotateCcw className="w-4 h-4" />
Reset All to Defaults
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">
Customize AI prompts for Auto Mode, Agent Runner, and other features.
</p>
</div>
{/* Info Banner */}
<div className="px-6 pt-6">
<div className="flex items-start gap-3 p-4 rounded-xl bg-blue-500/10 border border-blue-500/20">
<Info className="w-5 h-5 text-blue-500 mt-0.5 flex-shrink-0" />
<div className="space-y-1">
<p className="text-sm text-foreground font-medium">How to Customize Prompts</p>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
Toggle the switch to enable custom mode and edit the prompt. When disabled, the
default built-in prompt is used. You can use the default as a starting point by
enabling the toggle.
</p>
</div>
</div>
</div>
{/* Tabs */}
<div className="p-6">
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid grid-cols-4 w-full">
<TabsTrigger value="auto-mode" className="gap-2">
<Bot className="w-4 h-4" />
Auto Mode
</TabsTrigger>
<TabsTrigger value="agent" className="gap-2">
<MessageSquareText className="w-4 h-4" />
Agent
</TabsTrigger>
<TabsTrigger value="backlog-plan" className="gap-2">
<KanbanSquare className="w-4 h-4" />
Backlog Plan
</TabsTrigger>
<TabsTrigger value="enhancement" className="gap-2">
<Sparkles className="w-4 h-4" />
Enhancement
</TabsTrigger>
</TabsList>
{/* Auto Mode Tab */}
<TabsContent value="auto-mode" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Auto Mode Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('autoMode')}
className="gap-2"
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
{/* Info Banner for Auto Mode */}
<div className="flex items-start gap-3 p-4 rounded-xl bg-blue-500/10 border border-blue-500/20">
<Info className="w-5 h-5 text-blue-500 mt-0.5 flex-shrink-0" />
<div className="space-y-1">
<p className="text-sm text-foreground font-medium">Planning Mode Markers</p>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
Planning prompts use special markers like{' '}
<code className="px-1 py-0.5 rounded bg-muted text-xs">[PLAN_GENERATED]</code> and{' '}
<code className="px-1 py-0.5 rounded bg-muted text-xs">[SPEC_GENERATED]</code> to
control the Auto Mode workflow. These markers must be preserved for proper
functionality.
</p>
</div>
</div>
<div className="space-y-4">
<PromptField
label="Planning: Lite Mode"
description="Quick planning outline without approval requirement"
defaultValue={DEFAULT_AUTO_MODE_PROMPTS.planningLite}
customValue={promptCustomization?.autoMode?.planningLite}
onCustomValueChange={(value) => updatePrompt('autoMode', 'planningLite', value)}
critical={true}
/>
<PromptField
label="Planning: Lite with Approval"
description="Planning outline that waits for user approval"
defaultValue={DEFAULT_AUTO_MODE_PROMPTS.planningLiteWithApproval}
customValue={promptCustomization?.autoMode?.planningLiteWithApproval}
onCustomValueChange={(value) =>
updatePrompt('autoMode', 'planningLiteWithApproval', value)
}
critical={true}
/>
<PromptField
label="Planning: Spec Mode"
description="Detailed specification with task breakdown"
defaultValue={DEFAULT_AUTO_MODE_PROMPTS.planningSpec}
customValue={promptCustomization?.autoMode?.planningSpec}
onCustomValueChange={(value) => updatePrompt('autoMode', 'planningSpec', value)}
critical={true}
/>
<PromptField
label="Planning: Full SDD Mode"
description="Comprehensive Software Design Document with phased implementation"
defaultValue={DEFAULT_AUTO_MODE_PROMPTS.planningFull}
customValue={promptCustomization?.autoMode?.planningFull}
onCustomValueChange={(value) => updatePrompt('autoMode', 'planningFull', value)}
critical={true}
/>
</div>
</TabsContent>
{/* Agent Tab */}
<TabsContent value="agent" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Agent Runner Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('agent')}
className="gap-2"
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField
label="System Prompt"
description="Defines the AI's role and behavior in interactive chat sessions"
defaultValue={DEFAULT_AGENT_PROMPTS.systemPrompt}
customValue={promptCustomization?.agent?.systemPrompt}
onCustomValueChange={(value) => updatePrompt('agent', 'systemPrompt', value)}
/>
</div>
</TabsContent>
{/* Backlog Plan Tab */}
<TabsContent value="backlog-plan" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Backlog Planning Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('backlogPlan')}
className="gap-2"
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
{/* Critical Warning for Backlog Plan */}
<div className="flex items-start gap-3 p-4 rounded-xl bg-amber-500/10 border border-amber-500/20">
<AlertTriangle className="w-5 h-5 text-amber-500 mt-0.5 flex-shrink-0" />
<div className="space-y-1">
<p className="text-sm text-foreground font-medium">Warning: Critical Prompts</p>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
Backlog plan prompts require a strict JSON output format. Modifying these prompts
incorrectly can break the backlog planning feature and potentially corrupt your
feature data. Only customize if you fully understand the expected output
structure.
</p>
</div>
</div>
<div className="space-y-4">
<PromptField
label="System Prompt"
description="Defines how the AI modifies the feature backlog (Plan button on Kanban board)"
defaultValue={DEFAULT_BACKLOG_PLAN_PROMPTS.systemPrompt}
customValue={promptCustomization?.backlogPlan?.systemPrompt}
onCustomValueChange={(value) => updatePrompt('backlogPlan', 'systemPrompt', value)}
critical={true}
/>
</div>
</TabsContent>
{/* Enhancement Tab */}
<TabsContent value="enhancement" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Enhancement Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('enhancement')}
className="gap-2"
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField
label="Improve Mode"
description="Transform vague requests into clear, actionable tasks"
defaultValue={DEFAULT_ENHANCEMENT_PROMPTS.improveSystemPrompt}
customValue={promptCustomization?.enhancement?.improveSystemPrompt}
onCustomValueChange={(value) =>
updatePrompt('enhancement', 'improveSystemPrompt', value)
}
/>
<PromptField
label="Technical Mode"
description="Add implementation details and technical specifications"
defaultValue={DEFAULT_ENHANCEMENT_PROMPTS.technicalSystemPrompt}
customValue={promptCustomization?.enhancement?.technicalSystemPrompt}
onCustomValueChange={(value) =>
updatePrompt('enhancement', 'technicalSystemPrompt', value)
}
/>
<PromptField
label="Simplify Mode"
description="Make verbose descriptions concise and focused"
defaultValue={DEFAULT_ENHANCEMENT_PROMPTS.simplifySystemPrompt}
customValue={promptCustomization?.enhancement?.simplifySystemPrompt}
onCustomValueChange={(value) =>
updatePrompt('enhancement', 'simplifySystemPrompt', value)
}
/>
<PromptField
label="Acceptance Criteria Mode"
description="Add testable acceptance criteria to descriptions"
defaultValue={DEFAULT_ENHANCEMENT_PROMPTS.acceptanceSystemPrompt}
customValue={promptCustomization?.enhancement?.acceptanceSystemPrompt}
onCustomValueChange={(value) =>
updatePrompt('enhancement', 'acceptanceSystemPrompt', value)
}
/>
</div>
</TabsContent>
</Tabs>
</div>
</div>
);
}

View File

@@ -231,6 +231,7 @@ export async function syncSettingsToServer(): Promise<boolean> {
mcpServers: state.mcpServers,
mcpAutoApproveTools: state.mcpAutoApproveTools,
mcpUnrestrictedTools: state.mcpUnrestrictedTools,
promptCustomization: state.promptCustomization,
projects: state.projects,
trashedProjects: state.trashedProjects,
projectHistory: state.projectHistory,

View File

@@ -11,6 +11,7 @@ import type {
FeatureStatusWithPipeline,
PipelineConfig,
PipelineStep,
PromptCustomization,
} from '@automaker/types';
// Re-export ThemeMode for convenience
@@ -492,6 +493,9 @@ export interface AppState {
mcpAutoApproveTools: boolean; // Auto-approve MCP tool calls without permission prompts
mcpUnrestrictedTools: boolean; // Allow unrestricted tools when MCP servers are enabled
// Prompt Customization
promptCustomization: PromptCustomization; // Custom prompts for Auto Mode, Agent, Backlog Plan, Enhancement
// Project Analysis
projectAnalysis: ProjectAnalysis | null;
isAnalyzing: boolean;
@@ -774,6 +778,9 @@ export interface AppActions {
setMcpAutoApproveTools: (enabled: boolean) => Promise<void>;
setMcpUnrestrictedTools: (enabled: boolean) => Promise<void>;
// Prompt Customization actions
setPromptCustomization: (customization: PromptCustomization) => Promise<void>;
// AI Profile actions
addAIProfile: (profile: Omit<AIProfile, 'id'>) => void;
updateAIProfile: (id: string, updates: Partial<AIProfile>) => void;
@@ -972,6 +979,7 @@ const initialState: AppState = {
mcpServers: [], // No MCP servers configured by default
mcpAutoApproveTools: true, // Default to enabled - bypass permission prompts for MCP tools
mcpUnrestrictedTools: true, // Default to enabled - don't filter allowedTools when MCP enabled
promptCustomization: {}, // Empty by default - all prompts use built-in defaults
aiProfiles: DEFAULT_AI_PROFILES,
projectAnalysis: null,
isAnalyzing: false,
@@ -1628,6 +1636,14 @@ export const useAppStore = create<AppState & AppActions>()(
await syncSettingsToServer();
},
// Prompt Customization actions
setPromptCustomization: async (customization) => {
set({ promptCustomization: customization });
// Sync to server settings file
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
await syncSettingsToServer();
},
// AI Profile actions
addAIProfile: (profile) => {
const id = `profile-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
@@ -2909,6 +2925,8 @@ export const useAppStore = create<AppState & AppActions>()(
mcpServers: state.mcpServers,
mcpAutoApproveTools: state.mcpAutoApproveTools,
mcpUnrestrictedTools: state.mcpUnrestrictedTools,
// Prompt customization
promptCustomization: state.promptCustomization,
// Profiles and sessions
aiProfiles: state.aiProfiles,
chatSessions: state.chatSessions,

View File

@@ -0,0 +1,403 @@
/**
* Default Prompts Library
*
* Central repository for all default AI prompts used throughout the application.
* These prompts can be overridden by user customization in settings.
*
* Extracted from:
* - apps/server/src/services/auto-mode-service.ts (Auto Mode planning prompts)
* - apps/server/src/services/agent-service.ts (Agent Runner system prompt)
* - apps/server/src/routes/backlog-plan/generate-plan.ts (Backlog planning prompts)
*/
import type {
ResolvedAutoModePrompts,
ResolvedAgentPrompts,
ResolvedBacklogPlanPrompts,
ResolvedEnhancementPrompts,
} from '@automaker/types';
/**
* ========================================================================
* AUTO MODE PROMPTS
* ========================================================================
*/
export const DEFAULT_AUTO_MODE_PLANNING_LITE = `## Planning Phase (Lite Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the plan. Start DIRECTLY with the planning outline format below.
Create a brief planning outline:
1. **Goal**: What are we accomplishing? (1 sentence)
2. **Approach**: How will we do it? (2-3 sentences)
3. **Files to Touch**: List files and what changes
4. **Tasks**: Numbered task list (3-7 items)
5. **Risks**: Any gotchas to watch for
After generating the outline, output:
"[PLAN_GENERATED] Planning outline complete."
Then proceed with implementation.
`;
export const DEFAULT_AUTO_MODE_PLANNING_LITE_WITH_APPROVAL = `## Planning Phase (Lite Mode with Approval)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the plan. Start DIRECTLY with the planning outline format below.
Create a brief planning outline:
1. **Goal**: What are we accomplishing? (1 sentence)
2. **Approach**: How will we do it? (2-3 sentences)
3. **Files to Touch**: List files and what changes
4. **Tasks**: Numbered task list (3-7 items)
5. **Risks**: Any gotchas to watch for
After generating the outline, output:
"[SPEC_GENERATED] Please review the plan above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until approval is received.
`;
export const DEFAULT_AUTO_MODE_PLANNING_SPEC = `## Specification Phase (Spec Mode)
Generate a specification with an actionable task breakdown. WAIT for approval before implementing.
### Specification Format
1. **Problem**: What problem are we solving? (user perspective)
2. **Solution**: Brief approach (1-2 sentences)
3. **Acceptance Criteria**: 3-5 items in GIVEN-WHEN-THEN format
4. **Files to Modify**:
| File | Changes |
|------|---------|
| path/to/file | Brief description |
5. **Implementation Tasks**:
\`\`\`tasks
- [ ] T001: [Description] | File: [path/to/file]
- [ ] T002: [Description] | File: [path/to/file]
- [ ] T003: [Description] | File: [path/to/file]
\`\`\`
6. **Verification**: How to confirm feature works
After generating the spec, output:
"[SPEC_GENERATED] Please review the specification above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until approval is received.
`;
export const DEFAULT_AUTO_MODE_PLANNING_FULL = `## Software Design Document (Full Mode)
Generate a comprehensive specification with phased task breakdown. WAIT for approval before implementing.
### SDD Format
#### 1. Problem Statement
Brief description of the problem we're solving (user perspective)
#### 2. User Story
AS A [user type]
I WANT TO [action]
SO THAT [benefit]
#### 3. Acceptance Criteria
Multiple scenarios in GIVEN-WHEN-THEN format:
- **Scenario 1**: [Name]
- GIVEN [context]
- WHEN [action]
- THEN [expected outcome]
#### 4. Technical Context
- **Existing Components**: What's already in place
- **Integration Points**: Where this feature connects
- **Constraints**: Technical or business limitations
#### 5. Non-Goals
What this feature explicitly does NOT include (to prevent scope creep)
#### 6. Implementation Plan
**Phase 1: Foundation**
\`\`\`tasks
- [ ] T001: [Description] | File: [path]
- [ ] T002: [Description] | File: [path]
\`\`\`
**Phase 2: Core Implementation**
\`\`\`tasks
- [ ] T003: [Description] | File: [path]
- [ ] T004: [Description] | File: [path]
\`\`\`
**Phase 3: Integration & Testing**
\`\`\`tasks
- [ ] T005: [Description] | File: [path]
- [ ] T006: [Description] | File: [path]
\`\`\`
#### 7. Success Metrics
How we'll measure if this feature is working correctly
#### 8. Risks & Mitigations
| Risk | Impact | Mitigation |
|------|--------|------------|
| [risk] | [H/M/L] | [approach] |
After generating the SDD, output:
"[SPEC_GENERATED] Please review the specification above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until approval is received.
`;
export const DEFAULT_AUTO_MODE_FEATURE_PROMPT_TEMPLATE = `## Feature Implementation Task
**Feature ID:** {{featureId}}
**Title:** {{title}}
**Description:** {{description}}
{{#if spec}}
**Specification:**
{{spec}}
{{/if}}
{{#if imagePaths}}
**Context Images:**
{{#each imagePaths}}
- {{this}}
{{/each}}
{{/if}}
{{#if dependencies}}
**Dependencies:**
This feature depends on: {{dependencies}}
{{/if}}
{{#if verificationInstructions}}
**Verification:**
{{verificationInstructions}}
{{/if}}
`;
export const DEFAULT_AUTO_MODE_FOLLOW_UP_PROMPT_TEMPLATE = `## Follow-up on Feature Implementation
{{featurePrompt}}
## Previous Agent Work
{{previousContext}}
## Follow-up Instructions
{{followUpInstructions}}
## Task
Address the follow-up instructions above.
`;
export const DEFAULT_AUTO_MODE_CONTINUATION_PROMPT_TEMPLATE = `## Continuing Feature Implementation
{{featurePrompt}}
## Previous Context
{{previousContext}}
## Instructions
Review the previous work and continue the implementation.
`;
export const DEFAULT_AUTO_MODE_PIPELINE_STEP_PROMPT_TEMPLATE = `## Pipeline Step: {{stepName}}
### Feature Context
{{featurePrompt}}
### Previous Work
{{previousContext}}
### Pipeline Step Instructions
{{stepInstructions}}
`;
/**
* Default Auto Mode prompts (from auto-mode-service.ts)
*/
export const DEFAULT_AUTO_MODE_PROMPTS: ResolvedAutoModePrompts = {
planningLite: DEFAULT_AUTO_MODE_PLANNING_LITE,
planningLiteWithApproval: DEFAULT_AUTO_MODE_PLANNING_LITE_WITH_APPROVAL,
planningSpec: DEFAULT_AUTO_MODE_PLANNING_SPEC,
planningFull: DEFAULT_AUTO_MODE_PLANNING_FULL,
featurePromptTemplate: DEFAULT_AUTO_MODE_FEATURE_PROMPT_TEMPLATE,
followUpPromptTemplate: DEFAULT_AUTO_MODE_FOLLOW_UP_PROMPT_TEMPLATE,
continuationPromptTemplate: DEFAULT_AUTO_MODE_CONTINUATION_PROMPT_TEMPLATE,
pipelineStepPromptTemplate: DEFAULT_AUTO_MODE_PIPELINE_STEP_PROMPT_TEMPLATE,
};
/**
* ========================================================================
* AGENT RUNNER PROMPTS
* ========================================================================
*/
export const DEFAULT_AGENT_SYSTEM_PROMPT = `You are an AI assistant helping users build software. You are part of the Automaker application,
which is designed to help developers plan, design, and implement software projects autonomously.
**Feature Storage:**
Features are stored in .automaker/features/{id}/feature.json - each feature has its own folder.
Use the UpdateFeatureStatus tool to manage features, not direct file edits.
Your role is to:
- Help users define their project requirements and specifications
- Ask clarifying questions to better understand their needs
- Suggest technical approaches and architectures
- Guide them through the development process
- Be conversational and helpful
- Write, edit, and modify code files as requested
- Execute commands and tests
- Search and analyze the codebase
**Tools Available:**
You have access to several tools:
- UpdateFeatureStatus: Update feature status (NOT file edits)
- Read/Write/Edit: File operations
- Bash: Execute commands
- Glob/Grep: Search codebase
- WebSearch/WebFetch: Research online
**Important Guidelines:**
1. When users want to add or modify features, help them create clear feature definitions
2. Use UpdateFeatureStatus tool to manage features in the backlog
3. Be proactive in suggesting improvements and best practices
4. Ask questions when requirements are unclear
5. Guide users toward good software design principles
Remember: You're a collaborative partner in the development process. Be helpful, clear, and thorough.`;
/**
* Default Agent Runner prompts (from agent-service.ts)
*/
export const DEFAULT_AGENT_PROMPTS: ResolvedAgentPrompts = {
systemPrompt: DEFAULT_AGENT_SYSTEM_PROMPT,
};
/**
* ========================================================================
* BACKLOG PLAN PROMPTS
* ========================================================================
*/
export const DEFAULT_BACKLOG_PLAN_SYSTEM_PROMPT = `You are an AI assistant helping to modify a software project's feature backlog.
You will be given the current list of features and a user request to modify the backlog.
IMPORTANT CONTEXT (automatically injected):
- Remember to update the dependency graph if deleting existing features
- Remember to define dependencies on new features hooked into relevant existing ones
- Maintain dependency graph integrity (no orphaned dependencies)
- When deleting a feature, identify which other features depend on it
Your task is to analyze the request and produce a structured JSON plan with:
1. Features to ADD (include title, description, category, and dependencies)
2. Features to UPDATE (specify featureId and the updates)
3. Features to DELETE (specify featureId)
4. A summary of the changes
5. Any dependency updates needed (removed dependencies due to deletions, new dependencies for new features)
Respond with ONLY a JSON object in this exact format:
{
"plan": {
"add": [
{
"title": "string",
"description": "string",
"category": "feature" | "bug" | "enhancement" | "refactor",
"dependencies": ["featureId1", "featureId2"]
}
],
"update": [
{
"featureId": "string",
"updates": {
"title"?: "string",
"description"?: "string",
"category"?: "feature" | "bug" | "enhancement" | "refactor",
"priority"?: number,
"dependencies"?: ["featureId1"]
}
}
],
"delete": ["featureId1", "featureId2"],
"summary": "Brief summary of all changes",
"dependencyUpdates": [
{
"featureId": "string",
"action": "remove_dependency" | "add_dependency",
"dependencyId": "string",
"reason": "string"
}
]
}
}
Important rules:
- Only include fields that need to change in updates
- Ensure dependency references are valid (don't reference deleted features)
- Provide clear, actionable descriptions
- Maintain category consistency (feature, bug, enhancement, refactor)
- When adding dependencies, ensure the referenced features exist or are being added in the same plan
`;
export const DEFAULT_BACKLOG_PLAN_USER_PROMPT_TEMPLATE = `Current Features in Backlog:
{{currentFeatures}}
---
User Request: {{userRequest}}
Please analyze the current backlog and the user's request, then provide a JSON plan for the modifications.`;
/**
* Default Backlog Plan prompts (from backlog-plan/generate-plan.ts)
*/
export const DEFAULT_BACKLOG_PLAN_PROMPTS: ResolvedBacklogPlanPrompts = {
systemPrompt: DEFAULT_BACKLOG_PLAN_SYSTEM_PROMPT,
userPromptTemplate: DEFAULT_BACKLOG_PLAN_USER_PROMPT_TEMPLATE,
};
/**
* ========================================================================
* ENHANCEMENT PROMPTS
* ========================================================================
* Note: Enhancement prompts are already defined in enhancement.ts
* We import and re-export them here for consistency
*/
import {
IMPROVE_SYSTEM_PROMPT,
TECHNICAL_SYSTEM_PROMPT,
SIMPLIFY_SYSTEM_PROMPT,
ACCEPTANCE_SYSTEM_PROMPT,
} from './enhancement.js';
/**
* Default Enhancement prompts (from libs/prompts/src/enhancement.ts)
*/
export const DEFAULT_ENHANCEMENT_PROMPTS: ResolvedEnhancementPrompts = {
improveSystemPrompt: IMPROVE_SYSTEM_PROMPT,
technicalSystemPrompt: TECHNICAL_SYSTEM_PROMPT,
simplifySystemPrompt: SIMPLIFY_SYSTEM_PROMPT,
acceptanceSystemPrompt: ACCEPTANCE_SYSTEM_PROMPT,
};
/**
* ========================================================================
* COMBINED DEFAULTS
* ========================================================================
*/
/**
* All default prompts in one object for easy access
*/
export const DEFAULT_PROMPTS = {
autoMode: DEFAULT_AUTO_MODE_PROMPTS,
agent: DEFAULT_AGENT_PROMPTS,
backlogPlan: DEFAULT_BACKLOG_PLAN_PROMPTS,
enhancement: DEFAULT_ENHANCEMENT_PROMPTS,
} as const;

View File

@@ -23,3 +23,40 @@ export {
// Re-export types from @automaker/types
export type { EnhancementMode, EnhancementExample } from '@automaker/types';
// Default prompts
export {
DEFAULT_AUTO_MODE_PLANNING_LITE,
DEFAULT_AUTO_MODE_PLANNING_LITE_WITH_APPROVAL,
DEFAULT_AUTO_MODE_PLANNING_SPEC,
DEFAULT_AUTO_MODE_PLANNING_FULL,
DEFAULT_AUTO_MODE_FEATURE_PROMPT_TEMPLATE,
DEFAULT_AUTO_MODE_FOLLOW_UP_PROMPT_TEMPLATE,
DEFAULT_AUTO_MODE_CONTINUATION_PROMPT_TEMPLATE,
DEFAULT_AUTO_MODE_PIPELINE_STEP_PROMPT_TEMPLATE,
DEFAULT_AUTO_MODE_PROMPTS,
DEFAULT_AGENT_SYSTEM_PROMPT,
DEFAULT_AGENT_PROMPTS,
DEFAULT_BACKLOG_PLAN_SYSTEM_PROMPT,
DEFAULT_BACKLOG_PLAN_USER_PROMPT_TEMPLATE,
DEFAULT_BACKLOG_PLAN_PROMPTS,
DEFAULT_ENHANCEMENT_PROMPTS,
DEFAULT_PROMPTS,
} from './defaults.js';
// Prompt merging utilities
export {
mergeAutoModePrompts,
mergeAgentPrompts,
mergeBacklogPlanPrompts,
mergeEnhancementPrompts,
mergeAllPrompts,
} from './merge.js';
// Re-export resolved prompt types from @automaker/types
export type {
ResolvedAutoModePrompts,
ResolvedAgentPrompts,
ResolvedBacklogPlanPrompts,
ResolvedEnhancementPrompts,
} from '@automaker/types';

130
libs/prompts/src/merge.ts Normal file
View File

@@ -0,0 +1,130 @@
/**
* Prompt Merging Utilities
*
* Merges user-customized prompts with built-in defaults.
* Used by services to get effective prompts at runtime.
*
* Custom prompts have an `enabled` flag - when true, the custom value is used.
* When false or undefined, the default is used instead.
*/
import type {
PromptCustomization,
AutoModePrompts,
AgentPrompts,
BacklogPlanPrompts,
EnhancementPrompts,
CustomPrompt,
ResolvedAutoModePrompts,
ResolvedAgentPrompts,
ResolvedBacklogPlanPrompts,
ResolvedEnhancementPrompts,
} from '@automaker/types';
import {
DEFAULT_AUTO_MODE_PROMPTS,
DEFAULT_AGENT_PROMPTS,
DEFAULT_BACKLOG_PLAN_PROMPTS,
DEFAULT_ENHANCEMENT_PROMPTS,
} from './defaults.js';
/**
* Resolve a custom prompt to its effective string value
* Returns the custom value if enabled=true, otherwise returns the default
*/
function resolvePrompt(custom: CustomPrompt | undefined, defaultValue: string): string {
return custom?.enabled ? custom.value : defaultValue;
}
/**
* Merge custom Auto Mode prompts with defaults
* Custom prompts override defaults only when enabled=true
*/
export function mergeAutoModePrompts(custom?: AutoModePrompts): ResolvedAutoModePrompts {
return {
planningLite: resolvePrompt(custom?.planningLite, DEFAULT_AUTO_MODE_PROMPTS.planningLite),
planningLiteWithApproval: resolvePrompt(
custom?.planningLiteWithApproval,
DEFAULT_AUTO_MODE_PROMPTS.planningLiteWithApproval
),
planningSpec: resolvePrompt(custom?.planningSpec, DEFAULT_AUTO_MODE_PROMPTS.planningSpec),
planningFull: resolvePrompt(custom?.planningFull, DEFAULT_AUTO_MODE_PROMPTS.planningFull),
featurePromptTemplate: resolvePrompt(
custom?.featurePromptTemplate,
DEFAULT_AUTO_MODE_PROMPTS.featurePromptTemplate
),
followUpPromptTemplate: resolvePrompt(
custom?.followUpPromptTemplate,
DEFAULT_AUTO_MODE_PROMPTS.followUpPromptTemplate
),
continuationPromptTemplate: resolvePrompt(
custom?.continuationPromptTemplate,
DEFAULT_AUTO_MODE_PROMPTS.continuationPromptTemplate
),
pipelineStepPromptTemplate: resolvePrompt(
custom?.pipelineStepPromptTemplate,
DEFAULT_AUTO_MODE_PROMPTS.pipelineStepPromptTemplate
),
};
}
/**
* Merge custom Agent prompts with defaults
* Custom prompts override defaults only when enabled=true
*/
export function mergeAgentPrompts(custom?: AgentPrompts): ResolvedAgentPrompts {
return {
systemPrompt: resolvePrompt(custom?.systemPrompt, DEFAULT_AGENT_PROMPTS.systemPrompt),
};
}
/**
* Merge custom Backlog Plan prompts with defaults
* Custom prompts override defaults only when enabled=true
*/
export function mergeBacklogPlanPrompts(custom?: BacklogPlanPrompts): ResolvedBacklogPlanPrompts {
return {
systemPrompt: resolvePrompt(custom?.systemPrompt, DEFAULT_BACKLOG_PLAN_PROMPTS.systemPrompt),
userPromptTemplate: resolvePrompt(
custom?.userPromptTemplate,
DEFAULT_BACKLOG_PLAN_PROMPTS.userPromptTemplate
),
};
}
/**
* Merge custom Enhancement prompts with defaults
* Custom prompts override defaults only when enabled=true
*/
export function mergeEnhancementPrompts(custom?: EnhancementPrompts): ResolvedEnhancementPrompts {
return {
improveSystemPrompt: resolvePrompt(
custom?.improveSystemPrompt,
DEFAULT_ENHANCEMENT_PROMPTS.improveSystemPrompt
),
technicalSystemPrompt: resolvePrompt(
custom?.technicalSystemPrompt,
DEFAULT_ENHANCEMENT_PROMPTS.technicalSystemPrompt
),
simplifySystemPrompt: resolvePrompt(
custom?.simplifySystemPrompt,
DEFAULT_ENHANCEMENT_PROMPTS.simplifySystemPrompt
),
acceptanceSystemPrompt: resolvePrompt(
custom?.acceptanceSystemPrompt,
DEFAULT_ENHANCEMENT_PROMPTS.acceptanceSystemPrompt
),
};
}
/**
* Merge all custom prompts with defaults
* Returns a complete PromptCustomization with all fields populated
*/
export function mergeAllPrompts(custom?: PromptCustomization) {
return {
autoMode: mergeAutoModePrompts(custom?.autoMode),
agent: mergeAgentPrompts(custom?.agent),
backlogPlan: mergeBacklogPlanPrompts(custom?.backlogPlan),
enhancement: mergeEnhancementPrompts(custom?.enhancement),
};
}

View File

@@ -49,6 +49,21 @@ export { specOutputSchema } from './spec.js';
// Enhancement types
export type { EnhancementMode, EnhancementExample } from './enhancement.js';
// Prompt customization types
export type {
CustomPrompt,
AutoModePrompts,
AgentPrompts,
BacklogPlanPrompts,
EnhancementPrompts,
PromptCustomization,
ResolvedAutoModePrompts,
ResolvedAgentPrompts,
ResolvedBacklogPlanPrompts,
ResolvedEnhancementPrompts,
} from './prompts.js';
export { DEFAULT_PROMPT_CUSTOMIZATION } from './prompts.js';
// Settings types and constants
export type {
ThemeMode,

153
libs/types/src/prompts.ts Normal file
View File

@@ -0,0 +1,153 @@
/**
* Prompt Customization Types
*
* Defines the structure for customizable AI prompts used throughout the application.
* Allows users to modify prompts for Auto Mode, Agent Runner, and Backlog Planning.
*/
/**
* CustomPrompt - A custom prompt with its value and enabled state
*
* The value is always preserved even when disabled, so users don't lose their work.
*/
export interface CustomPrompt {
/** The custom prompt text */
value: string;
/** Whether this custom prompt should be used (when false, default is used instead) */
enabled: boolean;
}
/**
* AutoModePrompts - Customizable prompts for Auto Mode feature implementation
*
* Controls how the AI plans and implements features in autonomous mode.
*/
export interface AutoModePrompts {
/** Planning mode: Quick outline without approval (lite mode) */
planningLite?: CustomPrompt;
/** Planning mode: Quick outline with approval required (lite with approval) */
planningLiteWithApproval?: CustomPrompt;
/** Planning mode: Detailed specification with task breakdown (spec mode) */
planningSpec?: CustomPrompt;
/** Planning mode: Comprehensive Software Design Document (full SDD mode) */
planningFull?: CustomPrompt;
/** Template for building feature implementation prompts */
featurePromptTemplate?: CustomPrompt;
/** Template for follow-up prompts when resuming work */
followUpPromptTemplate?: CustomPrompt;
/** Template for continuation prompts */
continuationPromptTemplate?: CustomPrompt;
/** Template for pipeline step execution prompts */
pipelineStepPromptTemplate?: CustomPrompt;
}
/**
* AgentPrompts - Customizable prompts for Agent Runner (chat mode)
*
* Controls the AI's behavior in interactive chat sessions.
*/
export interface AgentPrompts {
/** System prompt defining the agent's role and behavior in chat */
systemPrompt?: CustomPrompt;
}
/**
* BacklogPlanPrompts - Customizable prompts for Kanban board planning
*
* Controls how the AI modifies the feature backlog via the Plan button.
*/
export interface BacklogPlanPrompts {
/** System prompt for backlog plan generation (defines output format and rules) */
systemPrompt?: CustomPrompt;
/** Template for user prompt (includes current features and user request) */
userPromptTemplate?: CustomPrompt;
}
/**
* EnhancementPrompts - Customizable prompts for feature description enhancement
*
* Controls how the AI enhances feature titles and descriptions.
*/
export interface EnhancementPrompts {
/** System prompt for "improve" mode (vague → clear) */
improveSystemPrompt?: CustomPrompt;
/** System prompt for "technical" mode (add technical details) */
technicalSystemPrompt?: CustomPrompt;
/** System prompt for "simplify" mode (verbose → concise) */
simplifySystemPrompt?: CustomPrompt;
/** System prompt for "acceptance" mode (add acceptance criteria) */
acceptanceSystemPrompt?: CustomPrompt;
}
/**
* PromptCustomization - Complete set of customizable prompts
*
* All fields are optional. Undefined values fall back to built-in defaults.
* Stored in GlobalSettings to allow user customization.
*/
export interface PromptCustomization {
/** Auto Mode prompts (feature implementation) */
autoMode?: AutoModePrompts;
/** Agent Runner prompts (interactive chat) */
agent?: AgentPrompts;
/** Backlog planning prompts (Plan button) */
backlogPlan?: BacklogPlanPrompts;
/** Enhancement prompts (feature description improvement) */
enhancement?: EnhancementPrompts;
}
/**
* Default empty prompt customization (all undefined → use built-in defaults)
*/
export const DEFAULT_PROMPT_CUSTOMIZATION: PromptCustomization = {
autoMode: {},
agent: {},
backlogPlan: {},
enhancement: {},
};
/**
* Resolved prompt types - all fields are required strings (ready to use)
* Used for default prompts and merged prompts after resolving custom values
*/
export interface ResolvedAutoModePrompts {
planningLite: string;
planningLiteWithApproval: string;
planningSpec: string;
planningFull: string;
featurePromptTemplate: string;
followUpPromptTemplate: string;
continuationPromptTemplate: string;
pipelineStepPromptTemplate: string;
}
export interface ResolvedAgentPrompts {
systemPrompt: string;
}
export interface ResolvedBacklogPlanPrompts {
systemPrompt: string;
userPromptTemplate: string;
}
export interface ResolvedEnhancementPrompts {
improveSystemPrompt: string;
technicalSystemPrompt: string;
simplifySystemPrompt: string;
acceptanceSystemPrompt: string;
}

View File

@@ -7,6 +7,7 @@
*/
import type { AgentModel } from './model.js';
import type { PromptCustomization } from './prompts.js';
// Re-export AgentModel for convenience
export type { AgentModel };
@@ -360,6 +361,10 @@ export interface GlobalSettings {
mcpAutoApproveTools?: boolean;
/** Allow unrestricted tools when MCP servers are enabled (don't filter allowedTools) */
mcpUnrestrictedTools?: boolean;
// Prompt Customization
/** Custom prompts for Auto Mode, Agent Runner, Backlog Planning, and Enhancements */
promptCustomization?: PromptCustomization;
}
/**