Skip to content

Commit adef521

Browse files
BunsDevclaude
andauthored
Add warning shortcut for failed git hooks (#84)
* Add agent shortcut for failed git hooks - Build a prompt from hook failure details - Add a "Fix with Agent" action for hook_failed commits * Use message fallback for work log details - Prefer `payload.message` when `detail` is missing - Preserve work log detail extraction across more event shapes --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 142ebf1 commit adef521

3 files changed

Lines changed: 57 additions & 3 deletions

File tree

apps/web/src/components/GitActionsControl.logic.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,5 +481,24 @@ export function resolveDefaultBranchActionDialogCopy(input: {
481481
};
482482
}
483483

484+
export function buildHookFailureAgentPrompt(failure: GitActionFailure): string {
485+
const sections: string[] = [
486+
"A git commit hook failed and blocked my commit. Please analyze the errors and fix them so I can retry.",
487+
];
488+
if (failure.command) {
489+
sections.push(`**Command that failed:**\n\`\`\`\n${failure.command}\n\`\`\``);
490+
}
491+
if (failure.detail) {
492+
sections.push(`**Error summary:**\n${failure.detail}`);
493+
}
494+
if (failure.rawMessage) {
495+
sections.push(`**Full hook output:**\n\`\`\`\n${failure.rawMessage}\n\`\`\``);
496+
}
497+
sections.push(
498+
"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",
499+
);
500+
return sections.join("\n\n");
501+
}
502+
484503
// Re-export from shared for backwards compatibility in this module's exports
485504
export { resolveAutoFeatureBranchName } from "@okcode/shared/git";

apps/web/src/components/GitActionsControl.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
resolveGitFailureRetryLabel,
3636
resolveQuickAction,
3737
resolveSyncAction,
38+
buildHookFailureAgentPrompt,
3839
formatOpenPullRequestLabel,
3940
summarizeGitFailure,
4041
summarizeGitResult,
@@ -79,7 +80,7 @@ import {
7980
gitStatusQueryOptions,
8081
invalidateGitQueries,
8182
} from "~/lib/gitReactQuery";
82-
import { randomUUID } from "~/lib/utils";
83+
import { newCommandId, newMessageId, randomUUID } from "~/lib/utils";
8384
import { resolvePathLinkTarget } from "~/terminal-links";
8485
import { readNativeApi } from "~/nativeApi";
8586
import { isWsRequestError } from "~/wsTransport";
@@ -783,6 +784,29 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions
783784
void runGitActionWithToast(retryInput);
784785
}, [gitActionFailureDialog]);
785786

787+
const fixWithAgent = useCallback(async () => {
788+
if (!gitActionFailureDialog || !activeThreadId) return;
789+
const api = readNativeApi();
790+
if (!api) return;
791+
const promptText = buildHookFailureAgentPrompt(gitActionFailureDialog.failure);
792+
setGitActionFailureDialog(null);
793+
await api.orchestration.dispatchCommand({
794+
type: "thread.turn.start",
795+
commandId: newCommandId(),
796+
threadId: activeThreadId,
797+
message: {
798+
messageId: newMessageId(),
799+
role: "user",
800+
text: promptText,
801+
attachments: [],
802+
},
803+
assistantDeliveryMode: settings.enableAssistantStreaming ? "streaming" : "buffered",
804+
runtimeMode: "approval-required",
805+
interactionMode: "code",
806+
createdAt: new Date().toISOString(),
807+
});
808+
}, [gitActionFailureDialog, activeThreadId, settings.enableAssistantStreaming]);
809+
786810
const checkoutFeatureBranchAndContinuePendingAction = useCallback(() => {
787811
if (!pendingDefaultBranchAction) return;
788812
const { action, commitMessage, forcePushOnlyProgress, onConfirmed, filePaths } =
@@ -1574,6 +1598,11 @@ export default function GitActionsControl({ gitCwd, activeThreadId }: GitActions
15741598
<Button variant="outline" size="sm" onClick={() => setGitActionFailureDialog(null)}>
15751599
Close
15761600
</Button>
1601+
{gitActionFailureDialog?.failure.code === "hook_failed" && (
1602+
<Button variant="outline" size="sm" disabled={!activeThreadId} onClick={fixWithAgent}>
1603+
Fix with Agent
1604+
</Button>
1605+
)}
15771606
<Button
15781607
size="sm"
15791608
disabled={!gitActionFailureDialog || isGitActionRunning}

apps/web/src/session-logic.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,8 +501,14 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo
501501
};
502502
const itemType = extractWorkLogItemType(payload);
503503
const requestKind = extractWorkLogRequestKind(payload);
504-
if (payload && typeof payload.detail === "string" && payload.detail.length > 0) {
505-
const detail = stripTrailingExitCode(payload.detail).output;
504+
const rawDetail =
505+
typeof payload?.detail === "string" && payload.detail.length > 0
506+
? payload.detail
507+
: typeof payload?.message === "string" && payload.message.length > 0
508+
? payload.message
509+
: null;
510+
if (rawDetail) {
511+
const detail = stripTrailingExitCode(rawDetail).output;
506512
if (detail) {
507513
entry.detail = detail;
508514
}

0 commit comments

Comments
 (0)