opencode-gemiterm-skills 0.6.0 → 0.6.2
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/CHANGELOG.md +14 -0
- package/README.md +7 -3
- package/package.json +1 -1
- package/skills/debate-with-gemini/SKILL.md +1 -1
- package/src/installer.ts +33 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes to `opencode-gemiterm-skills` will be documented in this fil
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.6.2] - 2026-06-12
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- `install` no longer adds a duplicate plugin entry when `opencode-gemiterm-skills` is already present under a different form (e.g. `opencode-gemiterm-skills@latest` or different casing). Added a shared `normalizePluginName`/`isOurPluginEntry` helper that strips version tags (`@latest`, `@1.2.3`) and lowercases before matching, used by `addPluginToConfig`, `removePluginFromConfig`, and `isPluginInConfig`. Scoped packages (`@scope/pkg`) are handled correctly so the leading `@` isn't mistaken for a version separator.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Exported `normalizePluginName` and `isOurPluginEntry` from `src/installer.ts` for direct unit testing.
|
|
14
|
+
- 6 new tests in `tests/skills.test.ts` covering version-spec stripping, case-insensitivity, whitespace trimming, scoped-package handling, and matching/rejecting the right entries.
|
|
15
|
+
|
|
16
|
+
## [0.6.1] - 2026-06-12
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Fixed frontmatter for `debate-with-gemini` skill.
|
|
20
|
+
|
|
7
21
|
## [0.6.0] - 2026-06-10
|
|
8
22
|
|
|
9
23
|
### Changed
|
package/README.md
CHANGED
|
@@ -37,14 +37,15 @@ Both skills are loaded on demand. Metadata (name + description) is pre-loaded at
|
|
|
37
37
|
## Quick start
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
# Install
|
|
40
|
+
# Install via skills.sh
|
|
41
|
+
npx skills add expert-vision-software/opencode-gemiterm-skills [--skill debate-with-gemini --skill gemiterm]
|
|
42
|
+
|
|
43
|
+
# Or with bunx
|
|
41
44
|
bunx opencode-gemiterm-skills install [--scope global]
|
|
42
45
|
|
|
43
46
|
# Or with npx
|
|
44
47
|
npx opencode-gemiterm-skills install [--scope global]
|
|
45
48
|
|
|
46
|
-
# Or skills.sh
|
|
47
|
-
npx skills add expert-vision-software/opencode-gemiterm-skills --skill [gemiterm/debate-with-gemini]
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
That's it — skills are available immediately.
|
|
@@ -106,6 +107,9 @@ Gemini conceded on the replication point but raised WAL-mode mitigations.
|
|
|
106
107
|
### CLI install (any agent)
|
|
107
108
|
|
|
108
109
|
```bash
|
|
110
|
+
# Install via skills.sh
|
|
111
|
+
npx skills add expert-vision-software/opencode-gemiterm-skills [--skill debate-with-gemini --skill gemiterm]
|
|
112
|
+
|
|
109
113
|
# Via bunx
|
|
110
114
|
bunx opencode-gemiterm-skills install
|
|
111
115
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-gemiterm-skills",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"description": "AI agent skills for Google Gemini — list, search, export Gemini chats and run structured debates with Gemini. Works with OpenCode, Claude Code, and any skill-compatible AI agent.",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: debate-with-gemini
|
|
3
|
-
description: Conduct structured multi-turn technical debates with Gemini AI via gemiterm CLI. Delegates a subagent to argue a position (for
|
|
3
|
+
description: 'Conduct structured multi-turn technical debates with Gemini AI via the gemiterm CLI. Delegates a subagent to argue a position (for or against) autonomously for up to N turns. Use when the user says - debate gemini, argue with gemini, have gemini defend or attack X, continue debate, or wants a technical position stress-tested against Gemini. Requires the gemiterm CLI to be installed and authenticated.'
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: opencode, claude-code, and any skill-compatible agent
|
|
6
6
|
metadata:
|
package/src/installer.ts
CHANGED
|
@@ -30,6 +30,33 @@ export interface StatusResult {
|
|
|
30
30
|
const SKILL_NAMES = ["gemiterm", "debate-with-gemini"] as const;
|
|
31
31
|
const PACKAGE_NAME = "opencode-gemiterm-skills";
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Normalize a plugin entry to its bare, lowercased package name.
|
|
35
|
+
*
|
|
36
|
+
* Plugin entries may carry a version spec (e.g. "pkg@latest",
|
|
37
|
+
* "pkg@1.2.3") and arbitrary casing. This strips any trailing
|
|
38
|
+
* "@version" and lowercases the result so that "opencode-gemiterm-skills",
|
|
39
|
+
* "Opencode-Gemiterm-Skills", and "opencode-gemiterm-skills@latest" all
|
|
40
|
+
* resolve to the same canonical name.
|
|
41
|
+
*/
|
|
42
|
+
export function normalizePluginName(entry: string): string {
|
|
43
|
+
let name = entry.trim().toLowerCase();
|
|
44
|
+
const atIdx = name.indexOf("@");
|
|
45
|
+
if (atIdx === 0) {
|
|
46
|
+
// Scoped package ("@scope/pkg"): the version separator is the second "@".
|
|
47
|
+
const secondAt = name.indexOf("@", 1);
|
|
48
|
+
if (secondAt !== -1) name = name.slice(0, secondAt);
|
|
49
|
+
} else if (atIdx !== -1) {
|
|
50
|
+
// Unscoped package: everything after the first "@" is a version spec.
|
|
51
|
+
name = name.slice(0, atIdx);
|
|
52
|
+
}
|
|
53
|
+
return name;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isOurPluginEntry(entry: string): boolean {
|
|
57
|
+
return normalizePluginName(entry) === PACKAGE_NAME.toLowerCase();
|
|
58
|
+
}
|
|
59
|
+
|
|
33
60
|
function getPackageDir(): string {
|
|
34
61
|
return join(fileURLToPath(new URL("../", import.meta.url)));
|
|
35
62
|
}
|
|
@@ -100,7 +127,7 @@ async function addPluginToConfig(configPath: string): Promise<boolean> {
|
|
|
100
127
|
const config = await readJsonConfig(configPath);
|
|
101
128
|
if (!config.plugin) config.plugin = [];
|
|
102
129
|
const plugins = config.plugin as string[];
|
|
103
|
-
if (plugins.
|
|
130
|
+
if (plugins.some(isOurPluginEntry)) return false;
|
|
104
131
|
plugins.push(PACKAGE_NAME);
|
|
105
132
|
await mkdir(join(configPath, ".."), { recursive: true });
|
|
106
133
|
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
@@ -111,10 +138,10 @@ async function removePluginFromConfig(configPath: string): Promise<boolean> {
|
|
|
111
138
|
const config = await readJsonConfig(configPath);
|
|
112
139
|
if (!config.plugin) return false;
|
|
113
140
|
const plugins = config.plugin as string[];
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
141
|
+
const filtered = plugins.filter((p) => !isOurPluginEntry(p));
|
|
142
|
+
if (filtered.length === plugins.length) return false;
|
|
143
|
+
if (filtered.length === 0) delete config.plugin;
|
|
144
|
+
else config.plugin = filtered;
|
|
118
145
|
await mkdir(join(configPath, ".."), { recursive: true });
|
|
119
146
|
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
120
147
|
return true;
|
|
@@ -123,7 +150,7 @@ async function removePluginFromConfig(configPath: string): Promise<boolean> {
|
|
|
123
150
|
async function isPluginInConfig(configPath: string): Promise<boolean> {
|
|
124
151
|
const config = await readJsonConfig(configPath);
|
|
125
152
|
if (!config.plugin) return false;
|
|
126
|
-
return (config.plugin as string[]).
|
|
153
|
+
return (config.plugin as string[]).some(isOurPluginEntry);
|
|
127
154
|
}
|
|
128
155
|
|
|
129
156
|
async function checkMigrationNeeded(projectDir: string) {
|