feat(ai): add AI-assisted commit splitting flow

Introduce a split-planning path for staged changes that asks supported AI
providers to group files into ordered Conventional Commit messages. The
plan is validated before execution so every staged file is assigned once
and unsafe states are rejected.

A new dialog lets users review and adjust the proposed groups before
creating the commits in sequence, with safeguards to preserve remaining
changes if something fails.
This commit is contained in:
Christoph Brandau
2026-07-28 15:01:32 +02:00
parent 3246dfdcfd
commit f0e87d67d5
9 changed files with 471 additions and 5 deletions
+16 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { Check, LoaderCircle, RotateCcw, Settings, ShieldCheck, Sparkles } from "@lucide/svelte";
import { Check, GitCommitHorizontal, LoaderCircle, RotateCcw, Settings, ShieldCheck, Sparkles } from "@lucide/svelte";
import type { CommitAiPhase, CommitAiProvider } from "../types";
interface Props {
@@ -14,12 +14,14 @@
commitAiPhase: CommitAiPhase;
commitAiGenerating: boolean;
commitAiReviewing: boolean;
commitAiSplitting: boolean;
canAmend: boolean;
amendMode: boolean;
onCommit: () => void;
onCommitMessageChange: (msg: string) => void;
onGenerateCommitMessage: () => void;
onReviewStaged: () => void;
onSplitStaged: () => void;
onOpenAiSettings: () => void;
onToggleAmend: (checked: boolean) => void;
onUndoLastCommit: () => void;
@@ -37,12 +39,14 @@
commitAiPhase = "idle",
commitAiGenerating = false,
commitAiReviewing = false,
commitAiSplitting = false,
canAmend = false,
amendMode = false,
onCommit = () => {},
onCommitMessageChange = () => {},
onGenerateCommitMessage = () => {},
onReviewStaged = () => {},
onSplitStaged = () => {},
onOpenAiSettings = () => {},
onToggleAmend = () => {},
onUndoLastCommit = () => {},
@@ -70,6 +74,7 @@
(commitAiProvider !== "local" || commitAiPhase === "ready"),
);
let canReview = $derived(canGenerate && commitAiProvider !== "local");
let canSplit = $derived(canReview && stagedCount > 1 && !commitAiSplitting);
</script>
<section class="panel commit-panel" aria-label="Commit">
@@ -80,6 +85,16 @@
</div>
<div class="commit-head-actions">
<span class="pill pill-count">{stagedCount} staged</span>
<button
class="commit-review-button"
type="button"
onclick={onSplitStaged}
disabled={!canSplit}
title={commitAiProvider === "local" ? "Commit splitting currently requires an API provider" : "Suggest logical commits for the staged files"}
>
{#if commitAiSplitting}<LoaderCircle class="spin" size={14} aria-hidden="true" />{:else}<GitCommitHorizontal size={14} aria-hidden="true" />{/if}
Split
</button>
<button
class="commit-review-button"
type="button"