Skip to content

Commit a11d2aa

Browse files
authored
Merge pull request #9414 from gitbutlerapp/split-commit-tool
Split commit tool
2 parents 83faa6f + 58198fd commit a11d2aa

File tree

11 files changed

+449
-52
lines changed

11 files changed

+449
-52
lines changed

apps/desktop/src/components/FeedItemKind.svelte

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,13 @@
186186
{parsedCall.parameters?.branchName}
187187
</span>
188188
</p>
189+
{:else if parsedCall.name === 'split_commit'}
190+
<p class="operation__title">
191+
Split commit
192+
<span>
193+
{parsedCall.parameters?.sourceCommitId.substring(0, 7)}
194+
</span>
195+
</p>
189196
{/if}
190197
</div>
191198

apps/desktop/src/lib/ai/tool.ts

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ type ToolName =
1010
| 'get_commit_details'
1111
| 'squash_commits'
1212
| 'split_branch'
13-
| 'get_branch_changes';
13+
| 'get_branch_changes'
14+
| 'split_commit';
1415

1516
export type ToolCall = {
1617
name: ToolName;
@@ -193,6 +194,40 @@ function isGetBranchChangesParameters(params: unknown): params is GetBranchChang
193194
);
194195
}
195196

197+
type CommitShard = {
198+
messageTitle: string;
199+
messageBody: string;
200+
files: string[];
201+
};
202+
203+
function isCommitShard(shard: unknown): shard is CommitShard {
204+
return (
205+
typeof shard === 'object' &&
206+
shard !== null &&
207+
typeof (shard as CommitShard).messageTitle === 'string' &&
208+
typeof (shard as CommitShard).messageBody === 'string' &&
209+
Array.isArray((shard as CommitShard).files) &&
210+
(shard as CommitShard).files.every((file) => typeof file === 'string')
211+
);
212+
}
213+
214+
type SplitCommitParameters = {
215+
sourceStackId: string;
216+
sourceCommitId: string;
217+
shards: CommitShard[];
218+
};
219+
220+
function isSplitCommitParameters(params: unknown): params is SplitCommitParameters {
221+
return (
222+
typeof params === 'object' &&
223+
params !== null &&
224+
typeof (params as SplitCommitParameters).sourceStackId === 'string' &&
225+
typeof (params as SplitCommitParameters).sourceCommitId === 'string' &&
226+
Array.isArray((params as SplitCommitParameters).shards) &&
227+
(params as SplitCommitParameters).shards.every(isCommitShard)
228+
);
229+
}
230+
196231
interface BaseParsedToolCall {
197232
name: ToolName;
198233
parameters:
@@ -206,6 +241,7 @@ interface BaseParsedToolCall {
206241
| SquashCommitsToolParams
207242
| SplitBranchToolParams
208243
| GetBranchChangesParameters
244+
| SplitCommitParameters
209245
| undefined;
210246
result: string;
211247
isError: boolean;
@@ -265,6 +301,11 @@ interface ParsedGetBranchChangesToolCall extends BaseParsedToolCall {
265301
parameters: GetBranchChangesParameters | undefined;
266302
}
267303

304+
interface ParsedSplitCommitToolCall extends BaseParsedToolCall {
305+
name: 'split_commit';
306+
parameters: SplitCommitParameters | undefined;
307+
}
308+
268309
export type ParsedToolCall =
269310
| ParsedCommitToolCall
270311
| ParsedCreateBranchToolCall
@@ -275,7 +316,8 @@ export type ParsedToolCall =
275316
| ParsedGetCommitDetailsToolCall
276317
| ParsedSquashCommitsToolCall
277318
| ParsedSplitBranchToolCall
278-
| ParsedGetBranchChangesToolCall;
319+
| ParsedGetBranchChangesToolCall
320+
| ParsedSplitCommitToolCall;
279321

280322
function safeParseJson(jsonString: string): unknown {
281323
try {
@@ -326,6 +368,8 @@ export function getToolCallIcon(name: ToolName, isError: boolean): IconName {
326368
}
327369

328370
switch (name) {
371+
case 'split_commit':
372+
return 'branch-shadow-commit';
329373
case 'commit':
330374
return 'commit';
331375
case 'amend':
@@ -469,5 +513,16 @@ export function parseToolCall(toolCall: ToolCall): ParsedToolCall {
469513
rawResult
470514
};
471515
}
516+
case 'split_commit': {
517+
const parameters = isSplitCommitParameters(rawParams) ? rawParams : undefined;
518+
return {
519+
name: toolCall.name,
520+
parameters,
521+
result: toolCall.result,
522+
isError,
523+
rawParameters: rawParams,
524+
rawResult
525+
};
526+
}
472527
}
473528
}

crates/but-action/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub fn freestyle(
9090
- `absorb`: Take a set of file changes and amend them into the existing commits in the project.
9191
This requires you to figure out where the changes should go based on the locks, assingments and any other user provided information.
9292
- `split a commit`: Take an existing commit and split it into multiple commits based on the the user directive.
93-
This is a multi-step operation where you will need to create one or more black commits, and the move the file changes from the original commit to the new commits.
93+
This can be achieved by using the `split_commit` tool.
9494
- `split a branch`: Take an existing branch and split it into two branches. This basically takes a set of committed file changes and moves them to a new branch, removing them from the original branch.
9595
This is useful when you want to separate the changes into a new branch for further work.
9696
In order to do this, you will need to get the branch changes for the intended source branch (call the `get_branch_changes` tool), and then call the split branch tool with the changes you want to split off.

crates/but-tools/src/tool.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,8 @@ impl ToolResult for Result<gix::ObjectId, anyhow::Error> {
144144
result_to_json(self, action_identifier, "gix::ObjectId")
145145
}
146146
}
147+
impl ToolResult for Result<Vec<gix::ObjectId>, anyhow::Error> {
148+
fn to_json(&self, action_identifier: &str) -> serde_json::Value {
149+
result_to_json(self, action_identifier, "Vec<gix::ObjectId>")
150+
}
151+
}

0 commit comments

Comments
 (0)