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`;
}
/**
* 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
*
@@ -423,21 +461,12 @@ export function PromptCustomizationSection({
</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"
<PromptTabContent
value="agent"
title="Agent Runner Prompts"
category="agent"
onReset={resetToDefaults}
>
<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"
@@ -445,8 +474,7 @@ export function PromptCustomizationSection({
customValue={promptCustomization?.agent?.systemPrompt}
onCustomValueChange={(value) => updatePrompt('agent', 'systemPrompt', value)}
/>
</div>
</TabsContent>
</PromptTabContent>
{/* Backlog Plan Tab */}
<TabsContent value="backlog-plan" className="space-y-6 mt-6">
@@ -569,49 +597,28 @@ export function PromptCustomizationSection({
</TabsContent>
{/* Commit Message Tab */}
<TabsContent value="commit-message" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Commit Message Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('commitMessage')}
className="gap-2"
<PromptTabContent
value="commit-message"
title="Commit Message Prompts"
category="commitMessage"
onReset={resetToDefaults}
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField
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."
defaultValue={DEFAULT_COMMIT_MESSAGE_PROMPTS.systemPrompt}
customValue={promptCustomization?.commitMessage?.systemPrompt}
onCustomValueChange={(value) =>
updatePrompt('commitMessage', 'systemPrompt', value)
}
onCustomValueChange={(value) => updatePrompt('commitMessage', 'systemPrompt', value)}
/>
</div>
</TabsContent>
</PromptTabContent>
{/* Title Generation Tab */}
<TabsContent value="title-generation" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Title Generation Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('titleGeneration')}
className="gap-2"
<PromptTabContent
value="title-generation"
title="Title Generation Prompts"
category="titleGeneration"
onReset={resetToDefaults}
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField
label="System Prompt"
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)
}
/>
</div>
</TabsContent>
</PromptTabContent>
{/* Issue Validation Tab */}
<TabsContent value="issue-validation" className="space-y-6 mt-6">
@@ -667,21 +673,12 @@ export function PromptCustomizationSection({
</TabsContent>
{/* Ideation Tab */}
<TabsContent value="ideation" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Ideation Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('ideation')}
className="gap-2"
<PromptTabContent
value="ideation"
title="Ideation Prompts"
category="ideation"
onReset={resetToDefaults}
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField
label="Ideation Chat System Prompt"
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}
/>
</div>
</TabsContent>
</PromptTabContent>
{/* App Spec Tab */}
<TabsContent value="app-spec" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">App Specification Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('appSpec')}
className="gap-2"
<PromptTabContent
value="app-spec"
title="App Specification Prompts"
category="appSpec"
onReset={resetToDefaults}
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField
label="Generate Spec System Prompt"
description="System prompt for generating project specifications from overview"
@@ -752,25 +739,15 @@ export function PromptCustomizationSection({
}
critical={true}
/>
</div>
</TabsContent>
</PromptTabContent>
{/* Context Description Tab */}
<TabsContent value="context-description" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Context Description Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('contextDescription')}
className="gap-2"
<PromptTabContent
value="context-description"
title="Context Description Prompts"
category="contextDescription"
onReset={resetToDefaults}
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField
label="Describe File Prompt"
description="Prompt for generating descriptions of text files added as context"
@@ -790,33 +767,21 @@ export function PromptCustomizationSection({
updatePrompt('contextDescription', 'describeImagePrompt', value)
}
/>
</div>
</TabsContent>
</PromptTabContent>
{/* Suggestions Tab */}
<TabsContent value="suggestions" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Suggestions Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('suggestions')}
className="gap-2"
<PromptTabContent
value="suggestions"
title="Suggestions Prompts"
category="suggestions"
onReset={resetToDefaults}
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
<div className="space-y-4">
<PromptField
label="Features Suggestion Prompt"
description="Prompt for analyzing the project and suggesting new features"
defaultValue={DEFAULT_SUGGESTIONS_PROMPTS.featuresPrompt}
customValue={promptCustomization?.suggestions?.featuresPrompt}
onCustomValueChange={(value) =>
updatePrompt('suggestions', 'featuresPrompt', value)
}
onCustomValueChange={(value) => updatePrompt('suggestions', 'featuresPrompt', value)}
/>
<PromptField
@@ -834,9 +799,7 @@ export function PromptCustomizationSection({
description="Prompt for analyzing security vulnerabilities"
defaultValue={DEFAULT_SUGGESTIONS_PROMPTS.securityPrompt}
customValue={promptCustomization?.suggestions?.securityPrompt}
onCustomValueChange={(value) =>
updatePrompt('suggestions', 'securityPrompt', value)
}
onCustomValueChange={(value) => updatePrompt('suggestions', 'securityPrompt', value)}
/>
<PromptField
@@ -856,32 +819,22 @@ export function PromptCustomizationSection({
customValue={promptCustomization?.suggestions?.baseTemplate}
onCustomValueChange={(value) => updatePrompt('suggestions', 'baseTemplate', value)}
/>
</div>
</TabsContent>
</PromptTabContent>
{/* Task Execution Tab */}
<TabsContent value="task-execution" className="space-y-6 mt-6">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-foreground">Task Execution Prompts</h3>
<Button
variant="ghost"
size="sm"
onClick={() => resetToDefaults('taskExecution')}
className="gap-2"
>
<RotateCcw className="w-3 h-3" />
Reset Section
</Button>
</div>
{/* Info Banner for Task Execution */}
<PromptTabContent
value="task-execution"
title="Task Execution Prompts"
category="taskExecution"
onReset={resetToDefaults}
infoBanner={
<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">
<p className="text-sm text-foreground font-medium">Template Variables</p>
<p className="text-xs text-muted-foreground/80 leading-relaxed">
Task execution prompts use Handlebars syntax for variable substitution. Variables
include{' '}
Task execution prompts use Handlebars syntax for variable substitution.
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">
{'{{taskDescription}}'}
@@ -894,8 +847,8 @@ export function PromptCustomizationSection({
</p>
</div>
</div>
<div className="space-y-4">
}
>
<PromptField
label="Task Prompt Template"
description="Template for building individual task execution prompts"
@@ -941,9 +894,7 @@ export function PromptCustomizationSection({
label="Learning Extraction User Template"
description="User prompt template for learning extraction. Variables: featureTitle, implementationLog"
defaultValue={DEFAULT_TASK_EXECUTION_PROMPTS.learningExtractionUserPromptTemplate}
customValue={
promptCustomization?.taskExecution?.learningExtractionUserPromptTemplate
}
customValue={promptCustomization?.taskExecution?.learningExtractionUserPromptTemplate}
onCustomValueChange={(value) =>
updatePrompt('taskExecution', 'learningExtractionUserPromptTemplate', value)
}
@@ -989,8 +940,7 @@ export function PromptCustomizationSection({
updatePrompt('taskExecution', 'projectAnalysisPrompt', value)
}
/>
</div>
</TabsContent>
</PromptTabContent>
</Tabs>
</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.
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.