opencode-github-release 0.4.1 → 0.5.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/README.md CHANGED
@@ -83,6 +83,10 @@ Suggested bump: major -> v1.0.0
83
83
 
84
84
  You can invoke `suggest_bump` directly at any time ("Suggest a bump") — it analyzes commits since the latest tag and recommends a bump without creating anything.
85
85
 
86
+ The `suggest_bump` tool works exceptionally well if your commit messages follow the [conventional commits specification](https://www.conventionalcommits.org/en/v1.0.0/).
87
+
88
+ There are commit skills such as (https://github.com/PedroHBO/opencode-config-skills/tree/main/skills/git-commit) that makes it all automatic and seamless.
89
+
86
90
  ## Prerequisites
87
91
 
88
92
  - [Git](https://git-scm.com/)
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/index.ts
2
2
  import { tool } from "@opencode-ai/plugin";
3
3
  function bumpVersion(current, bump) {
4
+ const prefix = current.startsWith("v") ? "v" : "";
4
5
  const cleaned = current.replace(/^v/, "");
5
6
  const match = cleaned.match(/^(\d+)\.(\d+)\.(\d+)/);
6
7
  if (!match) throw new Error(`Cannot parse semver from "${current}"`);
@@ -19,7 +20,7 @@ function bumpVersion(current, bump) {
19
20
  if (bump === "patch") {
20
21
  patch++;
21
22
  }
22
- return `v${major}.${minor}.${patch}`;
23
+ return `${prefix}${major}.${minor}.${patch}`;
23
24
  }
24
25
  function classifyCommit(subject) {
25
26
  const breaking = /^BREAKING CHANGE:/im.test(subject) || /^\w+(\([^)]*\))?!:/m.test(subject);
@@ -35,7 +36,8 @@ var plugin = async ({ $ }) => {
35
36
  suggest_bump: tool({
36
37
  description: "Analyze git history since the latest tag and suggest a semantic version bump. Call this tool when the user asks to create a release but does NOT specify patch/minor/major or an explicit version string. After receiving the suggestion, present it to the user and ask for confirmation before calling create_release.",
37
38
  args: {},
38
- async execute(_args, _context) {
39
+ async execute(_args, context) {
40
+ context.metadata({ title: "Fetching tags\u2026" });
39
41
  await $`git fetch --tags --force 2>/dev/null || true`.quiet();
40
42
  const tagResult = await $`git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0"`.text();
41
43
  const latestTag = tagResult.trim();
@@ -88,11 +90,12 @@ var plugin = async ({ $ }) => {
88
90
  notes: tool.schema.string().optional(),
89
91
  force: tool.schema.boolean().optional()
90
92
  },
91
- async execute(args, _context) {
93
+ async execute(args, context) {
92
94
  const { bump, version, notes, force } = args;
93
95
  if (!bump && !version) {
94
96
  throw new Error("Provide either `bump` (patch/minor/major) or an explicit `version` string");
95
97
  }
98
+ context.metadata({ title: "Checking working tree\u2026" });
96
99
  const status = (await $`git status --porcelain`.text()).trim();
97
100
  if (status && !force) {
98
101
  const count = status.split("\n").length;
@@ -101,10 +104,33 @@ var plugin = async ({ $ }) => {
101
104
  output: `${count} uncommitted file(s) detected. Call create_release with force: true to proceed anyway, or commit/stash first.`
102
105
  };
103
106
  }
107
+ context.metadata({ title: "Fetching tags\u2026" });
104
108
  await $`git fetch --tags --force 2>/dev/null || true`.quiet();
105
109
  const tagResult = await $`git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0"`.text();
106
110
  const latestTag = tagResult.trim();
107
- const newTag = version ? version.startsWith("v") ? version : `v${version}` : bumpVersion(latestTag, bump);
111
+ const repoUsesV = latestTag.startsWith("v");
112
+ const hasExistingTags = parseInt((await $`git tag -l 2>/dev/null | wc -l`.text()).trim(), 10) > 0;
113
+ let newTag;
114
+ if (version) {
115
+ const versionHasV = version.startsWith("v");
116
+ if (hasExistingTags && versionHasV !== repoUsesV) {
117
+ const suggestion = versionHasV ? version.replace(/^v/, "") : `v${version}`;
118
+ return {
119
+ title: "Version prefix mismatch",
120
+ output: [
121
+ `Existing releases use ${repoUsesV ? 'the "v" prefix' : 'no "v" prefix'} (e.g. "${latestTag}"),`,
122
+ `but you provided "${version}" which ${versionHasV ? "has" : "does not have"} a "v" prefix.`,
123
+ "",
124
+ `Would you like to use "${suggestion}" instead?`,
125
+ "If so, call create_release again with the corrected version."
126
+ ].join("\n")
127
+ };
128
+ }
129
+ newTag = version;
130
+ } else {
131
+ newTag = bumpVersion(latestTag, bump);
132
+ }
133
+ context.metadata({ title: `Bumping to ${newTag}\u2026` });
108
134
  const branch = (await $`git rev-parse --abbrev-ref HEAD`.text()).trim();
109
135
  let unpushedBefore = [];
110
136
  if (branch !== "HEAD") {
@@ -124,11 +150,14 @@ var plugin = async ({ $ }) => {
124
150
  await $`git commit -m ${`chore(release): bump version to ${newTag}`}`.quiet();
125
151
  }
126
152
  if (branch !== "HEAD") {
153
+ context.metadata({ title: "Pushing commits\u2026" });
127
154
  await $`git push origin ${branch}`.quiet();
128
155
  }
156
+ context.metadata({ title: "Tagging release\u2026" });
129
157
  const message = notes || `Release ${newTag}`;
130
158
  await $`git tag -a ${newTag} -m ${message}`.quiet();
131
159
  await $`git push origin ${newTag}`.quiet();
160
+ context.metadata({ title: "Creating GitHub release\u2026" });
132
161
  if (notes) {
133
162
  await $`gh release create ${newTag} --title ${newTag} --notes ${notes}`.quiet();
134
163
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-github-release",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "OpenCode plugin to create git tags and publish GitHub releases with semantic versioning.",
5
5
  "repository": {
6
6
  "type": "git",