diff --git a/apps/code/src/renderer/features/git-interaction/components/CreatePrDialog.tsx b/apps/code/src/renderer/features/git-interaction/components/CreatePrDialog.tsx
index ffc50c117..bf269dd61 100644
--- a/apps/code/src/renderer/features/git-interaction/components/CreatePrDialog.tsx
+++ b/apps/code/src/renderer/features/git-interaction/components/CreatePrDialog.tsx
@@ -2,9 +2,11 @@ import {
ErrorContainer,
GenerateButton,
} from "@features/git-interaction/components/GitInteractionDialogs";
+import { useFixWithAgent } from "@features/git-interaction/hooks/useFixWithAgent";
import { useGitInteractionStore } from "@features/git-interaction/state/gitInteractionStore";
import type { CreatePrStep } from "@features/git-interaction/types";
import type { DiffStats } from "@features/git-interaction/utils/diffStats";
+import { buildCreatePrFlowErrorPrompt } from "@features/git-interaction/utils/errorPrompts";
import {
CheckCircle,
Circle,
@@ -133,6 +135,9 @@ export function CreatePrDialog({
}: CreatePrDialogProps) {
const store = useGitInteractionStore();
const { actions } = store;
+ const { canFixWithAgent, fixWithAgent } = useFixWithAgent(() =>
+ buildCreatePrFlowErrorPrompt(store.createPrFailedStep),
+ );
const { createPrStep: step } = store;
const isExecuting = step !== "idle" && step !== "complete";
@@ -299,7 +304,17 @@ export function CreatePrDialog({
/>
{step === "error" && store.createPrError && (
-
+ {
+ fixWithAgent(store.createPrError ?? "");
+ actions.closeCreatePr();
+ }
+ : undefined
+ }
+ />
)}
diff --git a/apps/code/src/renderer/features/git-interaction/components/GitInteractionDialogs.tsx b/apps/code/src/renderer/features/git-interaction/components/GitInteractionDialogs.tsx
index 4dfe7610a..6f9f964d5 100644
--- a/apps/code/src/renderer/features/git-interaction/components/GitInteractionDialogs.tsx
+++ b/apps/code/src/renderer/features/git-interaction/components/GitInteractionDialogs.tsx
@@ -26,7 +26,13 @@ import { useState } from "react";
const ICON_SIZE = 14;
-export function ErrorContainer({ error }: { error: string }) {
+export function ErrorContainer({
+ error,
+ onFixWithAgent,
+}: {
+ error: string;
+ onFixWithAgent?: () => void;
+}) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
@@ -59,16 +65,30 @@ export function ErrorContainer({ error }: { error: string }) {
>
{error}
-
-
-
-
-
+
+ {onFixWithAgent && (
+
+
+
+
+
+ )}
+
+
+
+
+
+
diff --git a/apps/code/src/renderer/features/git-interaction/hooks/useFixWithAgent.ts b/apps/code/src/renderer/features/git-interaction/hooks/useFixWithAgent.ts
new file mode 100644
index 000000000..ae767ddf1
--- /dev/null
+++ b/apps/code/src/renderer/features/git-interaction/hooks/useFixWithAgent.ts
@@ -0,0 +1,52 @@
+import { DEFAULT_TAB_IDS } from "@features/panels/constants/panelConstants";
+import { usePanelLayoutStore } from "@features/panels/store/panelLayoutStore";
+import { findTabInTree } from "@features/panels/store/panelTree";
+import { getSessionService } from "@features/sessions/service/service";
+import { useSessionForTask } from "@features/sessions/stores/sessionStore";
+import { useNavigationStore } from "@stores/navigationStore";
+import { useCallback } from "react";
+import type { FixWithAgentPrompt } from "../utils/errorPrompts";
+
+/**
+ * Hook that sends a structured error prompt to the active agent session.
+ * Derives taskId and session readiness from stores.
+ *
+ * `canFixWithAgent` is true when there's an active, connected session.
+ */
+export function useFixWithAgent(
+ buildPrompt: (error: string) => FixWithAgentPrompt,
+): {
+ canFixWithAgent: boolean;
+ fixWithAgent: (error: string) => Promise;
+} {
+ const taskId = useNavigationStore((s) =>
+ s.view.type === "task-detail" ? s.view.data?.id : undefined,
+ );
+ const session = useSessionForTask(taskId);
+ const isSessionReady = session?.status === "connected";
+
+ const canFixWithAgent = !!(taskId && isSessionReady);
+
+ const fixWithAgent = useCallback(
+ async (error: string) => {
+ if (!taskId || !isSessionReady) return;
+
+ const { label, context } = buildPrompt(error);
+
+ const prompt = `${context}\n\n\`\`\`\n${error}\n\`\`\``;
+ getSessionService().sendPrompt(taskId, prompt);
+
+ const { taskLayouts, setActiveTab } = usePanelLayoutStore.getState();
+ const layout = taskLayouts[taskId];
+ if (layout) {
+ const result = findTabInTree(layout.panelTree, DEFAULT_TAB_IDS.LOGS);
+ if (result) {
+ setActiveTab(taskId, result.panelId, DEFAULT_TAB_IDS.LOGS);
+ }
+ }
+ },
+ [buildPrompt, taskId, isSessionReady],
+ );
+
+ return { canFixWithAgent, fixWithAgent };
+}
diff --git a/apps/code/src/renderer/features/git-interaction/utils/errorPrompts.ts b/apps/code/src/renderer/features/git-interaction/utils/errorPrompts.ts
new file mode 100644
index 000000000..607102a2e
--- /dev/null
+++ b/apps/code/src/renderer/features/git-interaction/utils/errorPrompts.ts
@@ -0,0 +1,39 @@
+import type { CreatePrStep } from "../types";
+
+export interface FixWithAgentPrompt {
+ label: string;
+ context: string;
+}
+
+export function buildCreatePrFlowErrorPrompt(
+ failedStep: CreatePrStep | null,
+): FixWithAgentPrompt {
+ return {
+ label: `Fix PR creation error`,
+ context: `The user tried to create a pull request using the Create PR button in the UI, but it failed at the ${failedStep} step.
+
+This flow is supposed to follow these steps:
+1. [creating-branch] Create a new feature branch, if needed (required if on default branch, optional otherwise)
+2. [committing] Commit changes
+3. [pushing] Push to remote
+4. [creating-pr] Create PR
+
+When an error occurs, the app automatically performs a rollback. This means you are likely in the pre-error state, e.g. back on the user's original branch without any new commits.
+
+Rollback scenarios:
+1. Branch creation fails -> check out user's original branch
+2. Commit fails -> use git reset to get back to the user's original state
+
+Common errors and resolutions:
+- **Duplicate branch names** - guide the user towards using a different branch name, or sorting out any git issues that have led them to this state
+- **Commit hook failures** - this may be the result of missing dependencies, check failure (e.g. lints), or something else. Ensure you fully understand the issue before proceeding.
+
+Your task is to help the user diagnose and fix the underlying issue (e.g. resolve merge conflicts, fix authentication, clean up git state).
+
+IMPORTANT:
+- Do NOT attempt to complete the PR flow yourself (no commit, push, or gh pr create). The user will retry via the "Create PR" button once the issue is resolved.
+- You may fix the underlying issue, but always confirm destructive actions with the user first.
+- Start by diagnosing: run read-only commands to understand the current state before taking action.
+- When the issue is resolved, remind the user to retry by clicking the "Create PR" button in the UI.`,
+ };
+}
diff --git a/apps/code/src/renderer/features/sessions/components/session-update/parseFileMentions.tsx b/apps/code/src/renderer/features/sessions/components/session-update/parseFileMentions.tsx
index 4b5e75d2e..6079b3ec8 100644
--- a/apps/code/src/renderer/features/sessions/components/session-update/parseFileMentions.tsx
+++ b/apps/code/src/renderer/features/sessions/components/session-update/parseFileMentions.tsx
@@ -2,7 +2,7 @@ import {
baseComponents,
defaultRemarkPlugins,
} from "@features/editor/components/MarkdownRenderer";
-import { File, GithubLogo } from "@phosphor-icons/react";
+import { File, GithubLogo, Warning } from "@phosphor-icons/react";
import { Code, Text } from "@radix-ui/themes";
import type { ReactNode } from "react";
import { memo } from "react";
@@ -10,8 +10,9 @@ import type { Components } from "react-markdown";
import ReactMarkdown from "react-markdown";
const MENTION_TAG_REGEX =
- /|/g;
-const MENTION_TAG_TEST = /<(?:file\s+path|github_issue\s+number)="[^"]+"/;
+ /||[\s\S]*?<\/error_context>/g;
+const MENTION_TAG_TEST =
+ /<(?:file\s+path|github_issue\s+number|error_context\s+label)="[^"]+"/;
const inlineComponents: Components = {
...baseComponents,
@@ -113,6 +114,14 @@ export function parseMentionTags(content: string): ReactNode[] {
onClick={issueUrl ? () => window.open(issueUrl, "_blank") : undefined}
/>,
);
+ } else if (match[5]) {
+ parts.push(
+ }
+ label={match[5]}
+ />,
+ );
}
lastIndex = matchIndex + match[0].length;