feat: expand magic prompts coverage and split generation prompts (#835)
- Add configurable visible/instructions prompt families for commit/PR generation, PR checks/comments flows, and git conflict resolution helpers. - Refactor prompt sending to explicit visible + synthetic parts instead of newline-based splitting, with legacy override migration for old keys. - Polish Magic Prompts settings UX with grouped sidebar entries, tooltip-based descriptions, AI icon, and validation that visible prompts cannot be empty across web and VS Code runtimes.
This commit is contained in:
committed by
GitHub
parent
2e5b02e753
commit
5f0d1623ae
@@ -14,6 +14,7 @@ import { useInputStore } from '@/sync/input-store';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { toast } from '@/components/ui';
|
||||
import { getConflictDetails, type MergeConflictDetails } from '@/lib/gitApi';
|
||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||
|
||||
interface ConflictDialogProps {
|
||||
open: boolean;
|
||||
@@ -65,36 +66,29 @@ export const ConflictDialog: React.FC<ConflictDialogProps> = ({
|
||||
});
|
||||
}, [open, directory]);
|
||||
|
||||
const buildConflictContext = React.useCallback((): {
|
||||
const buildConflictContext = React.useCallback(async (): Promise<{
|
||||
visibleText: string;
|
||||
instructionsText: string;
|
||||
payloadText: string;
|
||||
} | null => {
|
||||
} | null> => {
|
||||
if (!conflictDetails) return null;
|
||||
|
||||
const operationLabel = operation === 'merge' ? 'merge' : 'rebase';
|
||||
const headRef = conflictDetails.headInfo || (operation === 'merge' ? 'MERGE_HEAD' : 'REBASE_HEAD');
|
||||
const continueCmd = operation === 'merge' ? 'git commit --no-edit' : 'git rebase --continue';
|
||||
|
||||
const visibleText = `Resolve ${operationLabel} conflicts, stage the resolved files, and complete the ${operationLabel}. Preserve the intent of changes from ${headRef}.`;
|
||||
const visibleText = await renderMagicPrompt('git.conflict.resolve.visible', {
|
||||
operation_label: operationLabel,
|
||||
head_ref: headRef,
|
||||
});
|
||||
|
||||
const instructionsText = `Git ${operationLabel} operation is in progress with conflicts.
|
||||
- Directory: ${directory}
|
||||
- Operation: ${operation}
|
||||
- Head Info: ${conflictDetails.headInfo || 'N/A'}
|
||||
|
||||
Required steps:
|
||||
1. Read each conflicted file to understand the conflict markers (<<<<<<< HEAD, =======, >>>>>>> ...)
|
||||
2. Edit each file to resolve conflicts by choosing the correct code or merging both changes appropriately
|
||||
3. Stage all resolved files with: git add <file>
|
||||
4. Complete the ${operationLabel} with: ${continueCmd}
|
||||
|
||||
Important:
|
||||
- Remove ALL conflict markers from files (<<<<<<< HEAD, =======, >>>>>>>)
|
||||
- Make sure the final code is syntactically correct and preserves intent from both sides
|
||||
- Do not leave any files with unresolved conflict markers
|
||||
- After completing all steps, confirm the ${operationLabel} was successful
|
||||
`;
|
||||
const instructionsText = await renderMagicPrompt('git.conflict.resolve.instructions', {
|
||||
operation_label: operationLabel,
|
||||
directory,
|
||||
operation,
|
||||
head_info: conflictDetails.headInfo || 'N/A',
|
||||
continue_cmd: continueCmd,
|
||||
});
|
||||
|
||||
const payloadText = `${operationLabel} conflict context (JSON)\n${JSON.stringify(
|
||||
{
|
||||
@@ -122,8 +116,8 @@ Important:
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
const handleResolveInCurrentSession = () => {
|
||||
const context = buildConflictContext();
|
||||
const handleResolveInCurrentSession = async () => {
|
||||
const context = await buildConflictContext();
|
||||
if (!context) {
|
||||
toast.error('No conflict details available');
|
||||
return;
|
||||
@@ -146,8 +140,8 @@ Important:
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
const handleResolveInNewSession = () => {
|
||||
const context = buildConflictContext();
|
||||
const handleResolveInNewSession = async () => {
|
||||
const context = await buildConflictContext();
|
||||
if (!context) {
|
||||
toast.error('No conflict details available');
|
||||
return;
|
||||
@@ -234,7 +228,9 @@ Important:
|
||||
<div className="flex flex-col gap-2 pt-2">
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={handleResolveInNewSession}
|
||||
onClick={() => {
|
||||
void handleResolveInNewSession();
|
||||
}}
|
||||
disabled={isLoading || !conflictDetails}
|
||||
className="w-full gap-2"
|
||||
>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useInputStore } from '@/sync/input-store';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { execCommand } from '@/lib/execCommands';
|
||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||
import {
|
||||
abortIntegrate,
|
||||
computeIntegratePlan,
|
||||
@@ -184,29 +185,18 @@ export const IntegrateCommitsSection: React.FC<{
|
||||
|
||||
const openNewSessionDraft = useSessionUIStore((s) => s.openNewSessionDraft);
|
||||
|
||||
const buildConflictContext = React.useCallback((payload: { state: IntegrateInProgress; details: IntegrateConflictDetails }) => {
|
||||
const visibleText = `Resolve cherry-pick conflicts, stage the resolved files, and continue the cherry-pick. Keep intent of commit ${payload.state.currentCommit} onto branch ${payload.state.targetBranch}.`;
|
||||
const instructionsText = `Worktree commit integration (cherry-pick) is in progress with conflicts.
|
||||
- Repo root: ${payload.state.repoRoot}
|
||||
- Temp target worktree: ${payload.state.tempWorktreePath}
|
||||
- Source branch: ${payload.state.sourceBranch}
|
||||
- Target branch: ${payload.state.targetBranch}
|
||||
- Current commit: ${payload.state.currentCommit}
|
||||
|
||||
Required steps:
|
||||
1. Read each conflicted file in the temp worktree to understand the conflict markers (<<<<<<< HEAD, =======, >>>>>>> ...)
|
||||
2. Edit each file to resolve conflicts by choosing the correct code or merging both changes appropriately
|
||||
3. Stage all resolved files with: git add <file>
|
||||
4. Complete the cherry-pick with: git cherry-pick --continue
|
||||
|
||||
Important:
|
||||
- Work inside the temp worktree directory: ${payload.state.tempWorktreePath}
|
||||
- Remove ALL conflict markers from files (<<<<<<< HEAD, =======, >>>>>>>)
|
||||
- Preserve the intent of the commit being applied
|
||||
- Make sure the final code is syntactically correct
|
||||
- Do not leave any files with unresolved conflict markers
|
||||
- After completing all steps, confirm the cherry-pick was successful
|
||||
`;
|
||||
const buildConflictContext = React.useCallback(async (payload: { state: IntegrateInProgress; details: IntegrateConflictDetails }) => {
|
||||
const visibleText = await renderMagicPrompt('git.integrate.cherrypick.resolve.visible', {
|
||||
current_commit: payload.state.currentCommit,
|
||||
target_branch: payload.state.targetBranch,
|
||||
});
|
||||
const instructionsText = await renderMagicPrompt('git.integrate.cherrypick.resolve.instructions', {
|
||||
repo_root: payload.state.repoRoot,
|
||||
temp_worktree_path: payload.state.tempWorktreePath,
|
||||
source_branch: payload.state.sourceBranch,
|
||||
target_branch: payload.state.targetBranch,
|
||||
current_commit: payload.state.currentCommit,
|
||||
});
|
||||
const payloadText = `Cherry-pick conflict context (JSON)\n${JSON.stringify({
|
||||
repoRoot: payload.state.repoRoot,
|
||||
tempWorktreePath: payload.state.tempWorktreePath,
|
||||
@@ -227,11 +217,11 @@ Important:
|
||||
const setPendingInputText = useInputStore((s) => s.setPendingInputText);
|
||||
const setPendingSyntheticParts = useInputStore((s) => s.setPendingSyntheticParts);
|
||||
|
||||
const handleResolveWithAi = React.useCallback((
|
||||
const handleResolveWithAi = React.useCallback(async (
|
||||
payload: { state: IntegrateInProgress; details: IntegrateConflictDetails },
|
||||
useNewSession: boolean
|
||||
) => {
|
||||
const context = buildConflictContext(payload);
|
||||
const context = await buildConflictContext(payload);
|
||||
|
||||
if (useNewSession) {
|
||||
// Open new session with the conflict context as initial prompt + synthetic parts
|
||||
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/collapsible';
|
||||
import { generatePullRequestDescription } from '@/lib/gitApi';
|
||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||
import { openExternalUrl } from '@/lib/url';
|
||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
@@ -831,13 +832,8 @@ export const PullRequestSection: React.FC<{
|
||||
return;
|
||||
}
|
||||
|
||||
const visibleText = 'Review these PR failed checks and propose likely fixes. Do not implement until I confirm.';
|
||||
const instructionsText = `Use the attached checks payload.
|
||||
- Summarize what is failing.
|
||||
- Prioritize check annotations/errors over generic status text.
|
||||
- Identify likely root cause(s).
|
||||
- Propose a minimal fix plan and verification steps.
|
||||
- No speculation: ask for missing info if needed.`;
|
||||
const visibleText = await renderMagicPrompt('github.pr.checks.review.visible');
|
||||
const instructionsText = await renderMagicPrompt('github.pr.checks.review.instructions');
|
||||
const failedAnnotations = failed.flatMap((run) => {
|
||||
const annotations = Array.isArray(run.annotations) ? run.annotations : [];
|
||||
return annotations.map((annotation) => ({
|
||||
@@ -888,12 +884,8 @@ export const PullRequestSection: React.FC<{
|
||||
return;
|
||||
}
|
||||
|
||||
const visibleText = 'Review these PR comments and propose the required changes and next actions. Do not implement until I confirm.';
|
||||
const instructionsText = `Use the attached comments payload.
|
||||
- Identify required vs optional changes.
|
||||
- Call out intent/implementation mismatch if present.
|
||||
- Propose a minimal plan and verification steps.
|
||||
- No speculation: ask for missing info if needed.`;
|
||||
const visibleText = await renderMagicPrompt('github.pr.comments.review.visible');
|
||||
const instructionsText = await renderMagicPrompt('github.pr.comments.review.instructions');
|
||||
const payloadText = `GitHub PR comments (JSON)\n${JSON.stringify({
|
||||
repo: context.repo ?? null,
|
||||
pr: context.pr ?? null,
|
||||
@@ -908,7 +900,7 @@ export const PullRequestSection: React.FC<{
|
||||
}
|
||||
}, [directory, dispatchSyntheticPrompt, github, pr, resolveChatDispatchTarget, setActiveMainTab]);
|
||||
|
||||
const sendSingleCommentToChat = React.useCallback((comment: TimelineCommentItem) => {
|
||||
const sendSingleCommentToChat = React.useCallback(async (comment: TimelineCommentItem) => {
|
||||
setCommentsDialogOpen(false);
|
||||
setActiveMainTab('chat');
|
||||
|
||||
@@ -917,12 +909,8 @@ export const PullRequestSection: React.FC<{
|
||||
return;
|
||||
}
|
||||
|
||||
const visibleText = 'Address this comment from PR and propose required changes. Do not implement until I confirm.';
|
||||
const instructionsText = `Use the attached single-comment payload.
|
||||
- Explain what the reviewer is asking for.
|
||||
- Identify exact code areas likely impacted.
|
||||
- Propose a minimal implementation plan and verification steps.
|
||||
- Call out ambiguity and ask focused follow-up questions if needed.`;
|
||||
const visibleText = await renderMagicPrompt('github.pr.comment.single.visible');
|
||||
const instructionsText = await renderMagicPrompt('github.pr.comment.single.instructions');
|
||||
const payloadText = `GitHub PR comment (JSON)\n${JSON.stringify({
|
||||
repo: commentsDetails?.repo ?? null,
|
||||
pr: commentsDetails?.pr ?? pr ?? null,
|
||||
@@ -1947,7 +1935,9 @@ export const PullRequestSection: React.FC<{
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 px-0 has-[>svg]:px-0 sm:px-2 sm:has-[>svg]:px-2.5 text-[var(--status-success)] hover:bg-[var(--status-success-background)] hover:text-[var(--status-success)] justify-start"
|
||||
onClick={() => sendSingleCommentToChat(comment)}
|
||||
onClick={() => {
|
||||
void sendSingleCommentToChat(comment);
|
||||
}}
|
||||
aria-label="Send this comment to agent"
|
||||
>
|
||||
<RiAiGenerate2 className="size-3.5" />
|
||||
|
||||
Reference in New Issue
Block a user