@reachdevel/opencode-git-commit 0.0.0-stage → 2.0.0

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 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
- # Temporary Holding Version
1
+ # Git Autocommit Plugin for OpenCode
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://badge.fury.io/js/@reachdevel%2Fopencode-git-commit.svg)](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
@@ -0,0 +1,4 @@
1
+ import { Plugin } from "@opencode/plugin";
2
+ declare const GitAutocommitPlugin: Plugin.Plugin;
3
+ export { GitAutocommitPlugin };
4
+ export default GitAutocommitPlugin;
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
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
+ function log(level, message) {
7
+ const line = `[${SERVICE}] ${message}`;
8
+ if (level === "error")
9
+ console.error(line);
10
+ else
11
+ console.log(line);
12
+ }
13
+ async function git(cwd, args) {
14
+ const { stdout } = await exec("git", ["-C", cwd, ...args], { cwd });
15
+ return stdout;
16
+ }
17
+ async function isRepository(cwd) {
18
+ try {
19
+ await git(cwd, ["rev-parse", "--git-dir"]);
20
+ return true;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ function commitMessageFrom(messages) {
27
+ const assistant = [...messages].reverse().find((m) => m.type === "assistant");
28
+ const text = assistant?.content?.find((part) => part.type === "text")?.text;
29
+ if (!text)
30
+ return undefined;
31
+ const sanitized = text
32
+ .split("\n")[0]
33
+ .replace(/[^a-zA-Z0-9 ]/g, "")
34
+ .substring(0, 50)
35
+ .trim();
36
+ return sanitized || undefined;
37
+ }
38
+ function buildCommitMessage(text) {
39
+ const type = text.toLowerCase().includes("fix") ? "fix" : "feat";
40
+ return `${type}: ${text}`;
41
+ }
42
+ const GitAutocommitPlugin = Plugin.define({
43
+ id: "git-autocommit",
44
+ setup(ctx) {
45
+ log("info", `Plugin initialized for ${ctx.location.directory}`);
46
+ const controller = new AbortController();
47
+ const pending = new Map();
48
+ const run = (directory, sessionID) => {
49
+ const previous = pending.get(directory) ?? Promise.resolve();
50
+ const next = previous.then(async () => {
51
+ const cwd = directory;
52
+ // Initialize a repository if the location has none yet
53
+ if (!(await isRepository(cwd))) {
54
+ await git(cwd, ["init"]);
55
+ log("info", "Initialized git repository");
56
+ }
57
+ const status = await git(cwd, ["status", "--porcelain"]);
58
+ if (!status.trim()) {
59
+ log("debug", "No changes detected");
60
+ return;
61
+ }
62
+ const messages = await ctx.session.context({ sessionID });
63
+ const text = commitMessageFrom(messages) ?? "automated update";
64
+ const message = buildCommitMessage(text);
65
+ // Stage and commit (git respects .gitignore by default)
66
+ await git(cwd, ["add", "."]);
67
+ // Fall back to a bot identity only when the machine has no git identity configured
68
+ const identity = process.env.GIT_AUTHOR_EMAIL || (await git(cwd, ["config", "--get", "user.email"]).catch(() => ""))
69
+ ? []
70
+ : ["-c", "user.name=opencode", "-c", "user.email=opencode@localhost"];
71
+ await git(cwd, [...identity, "commit", "-m", message, "--no-verify"]);
72
+ log("info", `Committed: ${message}`);
73
+ });
74
+ const settled = next.catch((err) => {
75
+ const message = err instanceof Error ? err.message : String(err);
76
+ log("error", message);
77
+ });
78
+ pending.set(directory, settled);
79
+ return settled;
80
+ };
81
+ void (async () => {
82
+ for await (const event of ctx.event.subscribe({ signal: controller.signal })) {
83
+ if (event.type !== "session.idle")
84
+ continue;
85
+ log("debug", "Session idle detected");
86
+ const directory = event.location?.directory ?? ctx.location.directory;
87
+ await run(directory, event.data.sessionID);
88
+ }
89
+ })();
90
+ return () => controller.abort();
91
+ },
92
+ });
93
+ export { GitAutocommitPlugin };
94
+ export default GitAutocommitPlugin;
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "@reachdevel/opencode-git-commit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.0.0",
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
+ }