refactor: create reusable PromptTabContent component and add {{count}} placeholder

- Create PromptTabContent reusable component in prompt-customization-section.tsx
- Update all tabs (Agent, Commit Message, Title Generation, Ideation, App Spec,
  Context Description, Suggestions, Task Execution) to use the new component
- Add {{count}} placeholder to DEFAULT_SUGGESTIONS_SYSTEM_PROMPT for dynamic
  suggestion count

Addresses PR review comments from Gemini.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Shirone
2026-01-15 21:00:32 +01:00
parent 3a2ba6dbfe
commit 1abf219230
2 changed files with 326 additions and 376 deletions

View File

@@ -65,6 +65,44 @@ function calculateMinHeight(text: string): string {
return `${minHeight}px`; return `${minHeight}px`;
} }
/**
* PromptTabContent Component
*
* Reusable container for prompt customization tabs.
* Provides consistent header with title and reset button.
*/
interface PromptTabContentProps {
value: string;
title: string;
category: keyof PromptCustomization;
onReset: (category: keyof PromptCustomization) => void;
children: React.ReactNode;
infoBanner?: React.ReactNode;
}
function PromptTabContent({
value,
title,
category,
onReset,
children,
infoBanner,
}: PromptTabContentProps) {
return (
<TabsContent value={value} className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">{title}</h3>
<Button variant="ghost" size="sm" onClick={() => onReset(category)} className="gap-2">
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
{infoBanner}
<div className="space-y-4">{children}</div>
</TabsContent>
);
}
/** /**
* PromptField Component * PromptField Component
* *
@@ -423,21 +461,12 @@ export function PromptCustomizationSection({
</TabsContent> </TabsContent>
{/* Agent Tab */} {/* Agent Tab */}
<TabsContent value="agent" className="space-y-6 mt-6"> <PromptTabContent
<div className="flex items-center justify-between mb-4"> value="agent"
<h3 className="text-sm font-medium text-foreground">Agent Runner Prompts</h3> title="Agent Runner Prompts"
<Button category="agent"
variant="ghost" onReset={resetToDefaults}
size="sm"
onClick={() => resetToDefaults('agent')}
className="gap-2"
> >
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField <PromptField
label="System Prompt" label="System Prompt"
description="Defines the AI's role and behavior in interactive chat sessions" description="Defines the AI's role and behavior in interactive chat sessions"
@@ -445,8 +474,7 @@ export function PromptCustomizationSection({
customValue={promptCustomization?.agent?.systemPrompt} customValue={promptCustomization?.agent?.systemPrompt}
onCustomValueChange={(value) => updatePrompt('agent', 'systemPrompt', value)} onCustomValueChange={(value) => updatePrompt('agent', 'systemPrompt', value)}
/> />
</div> </PromptTabContent>
</TabsContent>
{/* Backlog Plan Tab */} {/* Backlog Plan Tab */}
<TabsContent value="backlog-plan" className="space-y-6 mt-6"> <TabsContent value="backlog-plan" className="space-y-6 mt-6">
@@ -569,49 +597,28 @@ export function PromptCustomizationSection({
</TabsContent> </TabsContent>
{/* Commit Message Tab */} {/* Commit Message Tab */}
<TabsContent value="commit-message" className="space-y-6 mt-6"> <PromptTabContent
<div className="flex items-center justify-between mb-4"> value="commit-message"
<h3 className="text-sm font-medium text-foreground">Commit Message Prompts</h3> title="Commit Message Prompts"
<Button category="commitMessage"
variant="ghost" onReset={resetToDefaults}
size="sm"
onClick={() => resetToDefaults('commitMessage')}
className="gap-2"
> >
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField <PromptField
label="System Prompt" label="System Prompt"
description="Instructions for generating git commit messages from diffs. The AI will receive the git diff and generate a conventional commit message." description="Instructions for generating git commit messages from diffs. The AI will receive the git diff and generate a conventional commit message."
defaultValue={DEFAULT_COMMIT_MESSAGE_PROMPTS.systemPrompt} defaultValue={DEFAULT_COMMIT_MESSAGE_PROMPTS.systemPrompt}
customValue={promptCustomization?.commitMessage?.systemPrompt} customValue={promptCustomization?.commitMessage?.systemPrompt}
onCustomValueChange={(value) => onCustomValueChange={(value) => updatePrompt('commitMessage', 'systemPrompt', value)}
updatePrompt('commitMessage', 'systemPrompt', value)
}
/> />
</div> </PromptTabContent>
</TabsContent>
{/* Title Generation Tab */} {/* Title Generation Tab */}
<TabsContent value="title-generation" className="space-y-6 mt-6"> <PromptTabContent
<div className="flex items-center justify-between mb-4"> value="title-generation"
<h3 className="text-sm font-medium text-foreground">Title Generation Prompts</h3> title="Title Generation Prompts"
<Button category="titleGeneration"
variant="ghost" onReset={resetToDefaults}
size="sm"
onClick={() => resetToDefaults('titleGeneration')}
className="gap-2"
> >
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField <PromptField
label="System Prompt" label="System Prompt"
description="Instructions for generating concise, descriptive feature titles from descriptions. Used when auto-generating titles for new features." description="Instructions for generating concise, descriptive feature titles from descriptions. Used when auto-generating titles for new features."
@@ -621,8 +628,7 @@ export function PromptCustomizationSection({
updatePrompt('titleGeneration', 'systemPrompt', value) updatePrompt('titleGeneration', 'systemPrompt', value)
} }
/> />
</div> </PromptTabContent>
</TabsContent>
{/* Issue Validation Tab */} {/* Issue Validation Tab */}
<TabsContent value="issue-validation" className="space-y-6 mt-6"> <TabsContent value="issue-validation" className="space-y-6 mt-6">
@@ -667,21 +673,12 @@ export function PromptCustomizationSection({
</TabsContent> </TabsContent>
{/* Ideation Tab */} {/* Ideation Tab */}
<TabsContent value="ideation" className="space-y-6 mt-6"> <PromptTabContent
<div className="flex items-center justify-between mb-4"> value="ideation"
<h3 className="text-sm font-medium text-foreground">Ideation Prompts</h3> title="Ideation Prompts"
<Button category="ideation"
variant="ghost" onReset={resetToDefaults}
size="sm"
onClick={() => resetToDefaults('ideation')}
className="gap-2"
> >
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField <PromptField
label="Ideation Chat System Prompt" label="Ideation Chat System Prompt"
description="System prompt for AI-powered ideation chat conversations. Guides the AI to brainstorm and suggest feature ideas." description="System prompt for AI-powered ideation chat conversations. Guides the AI to brainstorm and suggest feature ideas."
@@ -702,25 +699,15 @@ export function PromptCustomizationSection({
} }
critical={true} critical={true}
/> />
</div> </PromptTabContent>
</TabsContent>
{/* App Spec Tab */} {/* App Spec Tab */}
<TabsContent value="app-spec" className="space-y-6 mt-6"> <PromptTabContent
<div className="flex items-center justify-between mb-4"> value="app-spec"
<h3 className="text-sm font-medium text-foreground">App Specification Prompts</h3> title="App Specification Prompts"
<Button category="appSpec"
variant="ghost" onReset={resetToDefaults}
size="sm"
onClick={() => resetToDefaults('appSpec')}
className="gap-2"
> >
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField <PromptField
label="Generate Spec System Prompt" label="Generate Spec System Prompt"
description="System prompt for generating project specifications from overview" description="System prompt for generating project specifications from overview"
@@ -752,25 +739,15 @@ export function PromptCustomizationSection({
} }
critical={true} critical={true}
/> />
</div> </PromptTabContent>
</TabsContent>
{/* Context Description Tab */} {/* Context Description Tab */}
<TabsContent value="context-description" className="space-y-6 mt-6"> <PromptTabContent
<div className="flex items-center justify-between mb-4"> value="context-description"
<h3 className="text-sm font-medium text-foreground">Context Description Prompts</h3> title="Context Description Prompts"
<Button category="contextDescription"
variant="ghost" onReset={resetToDefaults}
size="sm"
onClick={() => resetToDefaults('contextDescription')}
className="gap-2"
> >
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField <PromptField
label="Describe File Prompt" label="Describe File Prompt"
description="Prompt for generating descriptions of text files added as context" description="Prompt for generating descriptions of text files added as context"
@@ -790,33 +767,21 @@ export function PromptCustomizationSection({
updatePrompt('contextDescription', 'describeImagePrompt', value) updatePrompt('contextDescription', 'describeImagePrompt', value)
} }
/> />
</div> </PromptTabContent>
</TabsContent>
{/* Suggestions Tab */} {/* Suggestions Tab */}
<TabsContent value="suggestions" className="space-y-6 mt-6"> <PromptTabContent
<div className="flex items-center justify-between mb-4"> value="suggestions"
<h3 className="text-sm font-medium text-foreground">Suggestions Prompts</h3> title="Suggestions Prompts"
<Button category="suggestions"
variant="ghost" onReset={resetToDefaults}
size="sm"
onClick={() => resetToDefaults('suggestions')}
className="gap-2"
> >
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField <PromptField
label="Features Suggestion Prompt" label="Features Suggestion Prompt"
description="Prompt for analyzing the project and suggesting new features" description="Prompt for analyzing the project and suggesting new features"
defaultValue={DEFAULT_SUGGESTIONS_PROMPTS.featuresPrompt} defaultValue={DEFAULT_SUGGESTIONS_PROMPTS.featuresPrompt}
customValue={promptCustomization?.suggestions?.featuresPrompt} customValue={promptCustomization?.suggestions?.featuresPrompt}
onCustomValueChange={(value) => onCustomValueChange={(value) => updatePrompt('suggestions', 'featuresPrompt', value)}
updatePrompt('suggestions', 'featuresPrompt', value)
}
/> />
<PromptField <PromptField
@@ -834,9 +799,7 @@ export function PromptCustomizationSection({
description="Prompt for analyzing security vulnerabilities" description="Prompt for analyzing security vulnerabilities"
defaultValue={DEFAULT_SUGGESTIONS_PROMPTS.securityPrompt} defaultValue={DEFAULT_SUGGESTIONS_PROMPTS.securityPrompt}
customValue={promptCustomization?.suggestions?.securityPrompt} customValue={promptCustomization?.suggestions?.securityPrompt}
onCustomValueChange={(value) => onCustomValueChange={(value) => updatePrompt('suggestions', 'securityPrompt', value)}
updatePrompt('suggestions', 'securityPrompt', value)
}
/> />
<PromptField <PromptField
@@ -856,32 +819,22 @@ export function PromptCustomizationSection({
customValue={promptCustomization?.suggestions?.baseTemplate} customValue={promptCustomization?.suggestions?.baseTemplate}
onCustomValueChange={(value) => updatePrompt('suggestions', 'baseTemplate', value)} onCustomValueChange={(value) => updatePrompt('suggestions', 'baseTemplate', value)}
/> />
</div> </PromptTabContent>
</TabsContent>
{/* Task Execution Tab */} {/* Task Execution Tab */}
<TabsContent value="task-execution" className="space-y-6 mt-6"> <PromptTabContent
<div className="flex items-center justify-between mb-4"> value="task-execution"
<h3 className="text-sm font-medium text-foreground">Task Execution Prompts</h3> title="Task Execution Prompts"
<Button category="taskExecution"
variant="ghost" onReset={resetToDefaults}
size="sm" infoBanner={
onClick={() => resetToDefaults('taskExecution')}
className="gap-2"
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
{/* Info Banner for Task Execution */}
<div className="flex items-start gap-3 p-4 rounded-xl bg-blue-500/10 border border-blue-500/20"> <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" /> <Info className="w-5 h-5 text-blue-500 mt-0.5 shrink-0" />
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm text-foreground font-medium">Template Variables</p> <p className="text-sm text-foreground font-medium">Template Variables</p>
<p className="text-xs text-muted-foreground/80 leading-relaxed"> <p className="text-xs text-muted-foreground/80 leading-relaxed">
Task execution prompts use Handlebars syntax for variable substitution. Variables Task execution prompts use Handlebars syntax for variable substitution.
include{' '} Variables include{' '}
<code className="px-1 py-0.5 rounded bg-muted text-xs">{'{{taskId}}'}</code>,{' '} <code className="px-1 py-0.5 rounded bg-muted text-xs">{'{{taskId}}'}</code>,{' '}
<code className="px-1 py-0.5 rounded bg-muted text-xs"> <code className="px-1 py-0.5 rounded bg-muted text-xs">
{'{{taskDescription}}'} {'{{taskDescription}}'}
@@ -894,8 +847,8 @@ export function PromptCustomizationSection({
</p> </p>
</div> </div>
</div> </div>
}
<div className="space-y-4"> >
<PromptField <PromptField
label="Task Prompt Template" label="Task Prompt Template"
description="Template for building individual task execution prompts" description="Template for building individual task execution prompts"
@@ -941,9 +894,7 @@ export function PromptCustomizationSection({
label="Learning Extraction User Template" label="Learning Extraction User Template"
description="User prompt template for learning extraction. Variables: featureTitle, implementationLog" description="User prompt template for learning extraction. Variables: featureTitle, implementationLog"
defaultValue={DEFAULT_TASK_EXECUTION_PROMPTS.learningExtractionUserPromptTemplate} defaultValue={DEFAULT_TASK_EXECUTION_PROMPTS.learningExtractionUserPromptTemplate}
customValue={ customValue={promptCustomization?.taskExecution?.learningExtractionUserPromptTemplate}
promptCustomization?.taskExecution?.learningExtractionUserPromptTemplate
}
onCustomValueChange={(value) => onCustomValueChange={(value) =>
updatePrompt('taskExecution', 'learningExtractionUserPromptTemplate', value) updatePrompt('taskExecution', 'learningExtractionUserPromptTemplate', value)
} }
@@ -989,8 +940,7 @@ export function PromptCustomizationSection({
updatePrompt('taskExecution', 'projectAnalysisPrompt', value) updatePrompt('taskExecution', 'projectAnalysisPrompt', value)
} }
/> />
</div> </PromptTabContent>
</TabsContent>
</Tabs> </Tabs>
</div> </div>
</div> </div>

View File

@@ -604,7 +604,7 @@ IMPORTANT: You do NOT have access to any tools. You CANNOT read files, search co
You must generate suggestions based ONLY on the project context provided below. You must generate suggestions based ONLY on the project context provided below.
Do NOT say "I'll analyze" or "Let me explore" - you cannot do those things. Do NOT say "I'll analyze" or "Let me explore" - you cannot do those things.
Based on the project context and the user's prompt, generate creative and actionable feature suggestions. Based on the project context and the user's prompt, generate exactly {{count}} creative and actionable feature suggestions.
YOUR RESPONSE MUST BE ONLY A JSON ARRAY - nothing else. No explanation, no preamble, no markdown code fences. YOUR RESPONSE MUST BE ONLY A JSON ARRAY - nothing else. No explanation, no preamble, no markdown code fences.