forked from anomalyco/opencode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgit-commit.ts
More file actions
149 lines (127 loc) · 4.37 KB
/
git-commit.ts
File metadata and controls
149 lines (127 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { z } from "zod";
import { Tool } from "openagent-ai";
export const gitCommit = Tool.define({
id: "commit",
description: `Commits current changes to the git repository with conventional commit format. It automatically stages all changes before committing.
- Validates git repository status and presence
- Stages all changes automatically
- Supports conventional commit format with optional prefix and scope
- Returns detailed metadata about the commit
Use this tool when you need to save your current work progress to version control`,
parameters: z.object({
message: z
.string()
.describe("The commit message describing what was changed"),
commitPrefix: z
.string()
.optional()
.describe(
"Optional commit type prefix (feat, fix, chore, docs, etc.). Defaults to 'feat' when feature name is provided",
),
featureName: z
.string()
.optional()
.describe(
"Optional feature name to scope the commit (e.g. 'auth', 'ui', 'api')",
),
}),
async execute(params, { metadata }) {
const { message, commitPrefix, featureName } = params;
try {
// Check if we're in a git repository
const gitCheck = Bun.spawn(["git", "rev-parse", "--git-dir"], {
stdout: "pipe",
stderr: "pipe",
});
const gitCheckExit = await gitCheck.exited;
if (gitCheckExit !== 0) {
throw new Error("Not in a git repository");
}
// Check for changes
const statusProc = Bun.spawn(["git", "status", "--porcelain"], {
stdout: "pipe",
stderr: "pipe",
});
const statusOutput = await new Response(statusProc.stdout).text();
const statusExit = await statusProc.exited;
if (statusExit !== 0) {
throw new Error("Failed to check git status");
}
if (!statusOutput.trim()) {
metadata({
committed: false,
hasChanges: false,
title: "No changes to commit",
});
return {
metadata: {
committed: false,
hasChanges: false,
title: "No changes to commit",
},
output: "No changes detected in the repository",
};
}
// Stage all changes
const addProc = Bun.spawn(["git", "add", "."], {
stdout: "pipe",
stderr: "pipe",
});
const addExit = await addProc.exited;
if (addExit !== 0) {
const addError = await new Response(addProc.stderr).text();
throw new Error(`Failed to stage changes: ${addError}`);
}
// Build commit message
let finalMessage = message;
if (featureName) {
const prefix = commitPrefix || "feat";
finalMessage = `${prefix}(${featureName}): ${message}`;
} else if (commitPrefix) {
finalMessage = `${commitPrefix}: ${message}`;
}
// Commit changes
const commitProc = Bun.spawn(["git", "commit", "-m", finalMessage], {
stdout: "pipe",
stderr: "pipe",
});
const commitOutput = await new Response(commitProc.stdout).text();
const commitError = await new Response(commitProc.stderr).text();
const commitExit = await commitProc.exited;
if (commitExit !== 0) {
throw new Error(`Failed to commit: ${commitError}`);
}
// Extract commit hash from output
const hashMatch = commitOutput.match(/\[[\w-]+\s+([a-f0-9]+)]/);
const commitHash = hashMatch ? hashMatch[1] : "unknown";
// Get file change statistics
const changedFiles = statusOutput.trim().split("\n").length;
const resultMetadata = {
committed: true,
hasChanges: true,
commitHash,
changedFiles,
commitMessage: finalMessage,
title: `Committed: ${finalMessage}`,
};
metadata(resultMetadata);
return {
metadata: resultMetadata,
output: `Successfully committed changes with message: "${finalMessage}"\nCommit hash: ${commitHash}\nFiles changed: ${changedFiles}`,
};
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
const errorMetadata = {
committed: false,
hasChanges: false,
error: errorMessage,
title: "Commit failed",
};
return {
metadata: errorMetadata,
output: `Failed to commit changes: ${errorMessage}`,
};
}
},
});