@reachdevel/opencode-git-commit 0.0.0-stage → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +87 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.js +103 -0
- package/package.json +43 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Levent Kurt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,88 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Git Autocommit Plugin for OpenCode
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/js/@reachdevel%2Fopencode-git-commit)
|
|
4
|
+
|
|
5
|
+
An OpenCode plugin that automatically commits changes when your coding session becomes idle.
|
|
6
|
+
|
|
7
|
+
Requires OpenCode v2 (uses the `@opencode/plugin` v2 plugin API).
|
|
8
|
+
|
|
9
|
+
## What It Does
|
|
10
|
+
|
|
11
|
+
This plugin monitors your OpenCode session and automatically creates git commits when:
|
|
12
|
+
|
|
13
|
+
1. The session goes idle (no active operations)
|
|
14
|
+
2. There are uncommitted changes in your working directory
|
|
15
|
+
3. A git repository exists (or can be initialized)
|
|
16
|
+
|
|
17
|
+
The commit message is intelligently extracted from the last assistant message in your conversation, making commits contextual and meaningful.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Automatic Commits** - Commits changes whenever your session becomes idle
|
|
22
|
+
- **Smart Commit Messages** - Extracts context from your last conversation to create meaningful commit messages
|
|
23
|
+
- **Auto-initialize** - Automatically initializes a git repository if one doesn't exist
|
|
24
|
+
- **Respects .gitignore** - Git handles ignored files automatically
|
|
25
|
+
- **Conventional Commits** - Uses `feat:` or `fix:` prefixes based on message content
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
### Via Command Line
|
|
30
|
+
```bash
|
|
31
|
+
opencode plugin add @reachdevel/opencode-git-commit@latest
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Via Configuration
|
|
35
|
+
Add to your `opencode.json`:
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"$schema": "https://opencode.ai/config.json",
|
|
39
|
+
"plugins": [
|
|
40
|
+
"@reachdevel/opencode-git-commit@latest"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Manual Installation
|
|
46
|
+
```bash
|
|
47
|
+
npm install @reachdevel/opencode-git-commit
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Then add to `opencode.json`:
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"$schema": "https://opencode.ai/config.json",
|
|
54
|
+
"plugins": [
|
|
55
|
+
"file:///path/to/node_modules/@reachdevel/opencode-git-commit/dist/index.js"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## How It Works
|
|
61
|
+
|
|
62
|
+
1. When a `session.idle` event is triggered, the plugin checks for git changes
|
|
63
|
+
2. If changes exist, it fetches the last assistant message from your session
|
|
64
|
+
3. Extracts the first line, sanitizes it, and uses it as the commit message
|
|
65
|
+
4. Stages all changes with `git add .`
|
|
66
|
+
5. Commits with `--no-verify` to bypass hooks
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
The plugin works out of the box with no configuration needed. Just install and it will start auto-committing.
|
|
71
|
+
|
|
72
|
+
To disable it for a project, remove its id from the plugin list:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"plugins": ["*", "-git-autocommit"]
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Requirements
|
|
81
|
+
|
|
82
|
+
- OpenCode with plugin support
|
|
83
|
+
- Node.js (for running the plugin)
|
|
84
|
+
- Git installed on your system
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { Plugin } from "@opencode/plugin";
|
|
4
|
+
const exec = promisify(execFile);
|
|
5
|
+
const SERVICE = "git-autocommit";
|
|
6
|
+
const TERMINAL = new Set([
|
|
7
|
+
"session.execution.succeeded",
|
|
8
|
+
"session.execution.failed",
|
|
9
|
+
"session.execution.interrupted",
|
|
10
|
+
]);
|
|
11
|
+
function isTerminal(event) {
|
|
12
|
+
return TERMINAL.has(event.type);
|
|
13
|
+
}
|
|
14
|
+
function log(level, message) {
|
|
15
|
+
const line = `[${SERVICE}] ${message}`;
|
|
16
|
+
if (level === "error")
|
|
17
|
+
console.error(line);
|
|
18
|
+
else
|
|
19
|
+
console.log(line);
|
|
20
|
+
}
|
|
21
|
+
async function git(cwd, args) {
|
|
22
|
+
const { stdout } = await exec("git", ["-C", cwd, ...args], { cwd });
|
|
23
|
+
return stdout;
|
|
24
|
+
}
|
|
25
|
+
async function isRepository(cwd) {
|
|
26
|
+
try {
|
|
27
|
+
await git(cwd, ["rev-parse", "--git-dir"]);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function commitMessageFrom(messages) {
|
|
35
|
+
const assistant = [...messages].reverse().find((m) => m.type === "assistant");
|
|
36
|
+
const text = assistant?.content?.find((part) => part.type === "text")?.text;
|
|
37
|
+
if (!text)
|
|
38
|
+
return undefined;
|
|
39
|
+
const sanitized = text
|
|
40
|
+
.split("\n")[0]
|
|
41
|
+
.replace(/[^a-zA-Z0-9 ]/g, "")
|
|
42
|
+
.substring(0, 50)
|
|
43
|
+
.trim();
|
|
44
|
+
return sanitized || undefined;
|
|
45
|
+
}
|
|
46
|
+
function buildCommitMessage(text) {
|
|
47
|
+
const type = text.toLowerCase().includes("fix") ? "fix" : "feat";
|
|
48
|
+
return `${type}: ${text}`;
|
|
49
|
+
}
|
|
50
|
+
const GitAutocommitPlugin = Plugin.define({
|
|
51
|
+
id: "git-autocommit",
|
|
52
|
+
setup(ctx) {
|
|
53
|
+
log("info", `Plugin initialized for ${ctx.location.directory}`);
|
|
54
|
+
const controller = new AbortController();
|
|
55
|
+
const pending = new Map();
|
|
56
|
+
const run = (directory, sessionID) => {
|
|
57
|
+
const previous = pending.get(directory) ?? Promise.resolve();
|
|
58
|
+
const next = previous.then(async () => {
|
|
59
|
+
const cwd = directory;
|
|
60
|
+
// Initialize a repository if the location has none yet
|
|
61
|
+
if (!(await isRepository(cwd))) {
|
|
62
|
+
await git(cwd, ["init"]);
|
|
63
|
+
log("info", "Initialized git repository");
|
|
64
|
+
}
|
|
65
|
+
const status = await git(cwd, ["status", "--porcelain"]);
|
|
66
|
+
if (!status.trim()) {
|
|
67
|
+
log("debug", "No changes detected");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const messages = await ctx.session.context({ sessionID });
|
|
71
|
+
const text = commitMessageFrom(messages) ?? "automated update";
|
|
72
|
+
const message = buildCommitMessage(text);
|
|
73
|
+
// Stage and commit (git respects .gitignore by default)
|
|
74
|
+
await git(cwd, ["add", "."]);
|
|
75
|
+
// Fall back to a bot identity only when the machine has no git identity configured
|
|
76
|
+
const identity = process.env.GIT_AUTHOR_EMAIL || (await git(cwd, ["config", "--get", "user.email"]).catch(() => ""))
|
|
77
|
+
? []
|
|
78
|
+
: ["-c", "user.name=opencode", "-c", "user.email=opencode@localhost"];
|
|
79
|
+
await git(cwd, [...identity, "commit", "-m", message, "--no-verify"]);
|
|
80
|
+
log("info", `Committed: ${message}`);
|
|
81
|
+
});
|
|
82
|
+
const settled = next.catch((err) => {
|
|
83
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
84
|
+
log("error", message);
|
|
85
|
+
});
|
|
86
|
+
pending.set(directory, settled);
|
|
87
|
+
return settled;
|
|
88
|
+
};
|
|
89
|
+
void (async () => {
|
|
90
|
+
for await (const event of ctx.event.subscribe({ signal: controller.signal })) {
|
|
91
|
+
// OpenCode v2 does not publish session.idle to plugins. The terminal
|
|
92
|
+
// execution events are what actually signal the session finished.
|
|
93
|
+
if (!isTerminal(event))
|
|
94
|
+
continue;
|
|
95
|
+
log("debug", `Session finished (${event.type})`);
|
|
96
|
+
await run(ctx.location.directory, event.data.sessionID);
|
|
97
|
+
}
|
|
98
|
+
})();
|
|
99
|
+
return () => controller.abort();
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
export { GitAutocommitPlugin };
|
|
103
|
+
export default GitAutocommitPlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reachdevel/opencode-git-commit",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Zero-config background git auto-commit plugin for OpenCode CLI",
|
|
5
|
+
"author": "Levent Kurt <reachdevel@gmail.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/reachdevel/opencode-git-commit.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/reachdevel/opencode-git-commit#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/reachdevel/opencode-git-commit/issues"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.json",
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
26
|
+
"typecheck": "tsc --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"opencode",
|
|
30
|
+
"opencode-plugin",
|
|
31
|
+
"git",
|
|
32
|
+
"autocommit",
|
|
33
|
+
"automation"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@opencode/plugin": "^2.0.31"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|