Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/web/src/components/GitActionsControl.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,5 +481,24 @@ export function resolveDefaultBranchActionDialogCopy(input: {
};
}

export function buildHookFailureAgentPrompt(failure: GitActionFailure): string {
const sections: string[] = [
"A git commit hook failed and blocked my commit. Please analyze the errors and fix them so I can retry.",
];
if (failure.command) {
sections.push(`**Command that failed:**\n\`\`\`\n${failure.command}\n\`\`\``);
}
if (failure.detail) {
sections.push(`**Error summary:**\n${failure.detail}`);
}
if (failure.rawMessage) {
sections.push(`**Full hook output:**\n\`\`\`\n${failure.rawMessage}\n\`\`\``);
}
sections.push(
"Please:\n1. Analyze each error in the hook output\n2. Fix the affected files\n3. Let me know when the fixes are ready so I can retry the commit",
);
return sections.join("\n\n");
}

// Re-export from shared for backwards compatibility in this module's exports
export { resolveAutoFeatureBranchName } from "@okcode/shared/git";
31 changes: 30 additions & 1 deletion apps/web/src/components/GitActionsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
resolveGitFailureRetryLabel,
resolveQuickAction,
resolveSyncAction,
buildHookFailureAgentPrompt,
formatOpenPullRequestLabel,
summarizeGitFailure,
summarizeGitResult,
Expand Down Expand Up @@ -79,7 +80,7 @@ import {
gitStatusQueryOptions,
invalidateGitQueries,
} from "~/lib/gitReactQuery";
import { randomUUID } from "~/lib/utils";
import { newCommandId, newMessageId, randomUUID } from "~/lib/utils";
import { resolvePathLinkTarget } from "~/terminal-links";
import { readNativeApi } from "~/nativeApi";
import { isWsRequestError } from "~/wsTransport";
Expand Down Expand Up @@ -783,6 +784,29 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions
void runGitActionWithToast(retryInput);
}, [gitActionFailureDialog]);

const fixWithAgent = useCallback(async () => {
if (!gitActionFailureDialog || !activeThreadId) return;
const api = readNativeApi();
if (!api) return;
const promptText = buildHookFailureAgentPrompt(gitActionFailureDialog.failure);
setGitActionFailureDialog(null);
await api.orchestration.dispatchCommand({
type: "thread.turn.start",
commandId: newCommandId(),
threadId: activeThreadId,
message: {
messageId: newMessageId(),
role: "user",
text: promptText,
attachments: [],
},
assistantDeliveryMode: settings.enableAssistantStreaming ? "streaming" : "buffered",
runtimeMode: "approval-required",
interactionMode: "code",
createdAt: new Date().toISOString(),
});
}, [gitActionFailureDialog, activeThreadId, settings.enableAssistantStreaming]);

const checkoutFeatureBranchAndContinuePendingAction = useCallback(() => {
if (!pendingDefaultBranchAction) return;
const { action, commitMessage, forcePushOnlyProgress, onConfirmed, filePaths } =
Expand Down Expand Up @@ -1574,6 +1598,11 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions
<Button variant="outline" size="sm" onClick={() => setGitActionFailureDialog(null)}>
Close
</Button>
{gitActionFailureDialog?.failure.code === "hook_failed" && (
<Button variant="outline" size="sm" disabled={!activeThreadId} onClick={fixWithAgent}>
Fix with Agent
</Button>
)}
<Button
size="sm"
disabled={!gitActionFailureDialog || isGitActionRunning}
Expand Down
10 changes: 8 additions & 2 deletions apps/web/src/session-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,14 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo
};
const itemType = extractWorkLogItemType(payload);
const requestKind = extractWorkLogRequestKind(payload);
if (payload && typeof payload.detail === "string" && payload.detail.length > 0) {
const detail = stripTrailingExitCode(payload.detail).output;
const rawDetail =
typeof payload?.detail === "string" && payload.detail.length > 0
? payload.detail
: typeof payload?.message === "string" && payload.message.length > 0
? payload.message
: null;
if (rawDetail) {
const detail = stripTrailingExitCode(rawDetail).output;
if (detail) {
entry.detail = detail;
}
Expand Down
Loading