From 8d8dfea141ce722cb6ea835de09f1f12ed0a9542 Mon Sep 17 00:00:00 2001 From: Som Samantray Date: Sun, 27 Sep 2026 17:30:14 +0530 Subject: [PATCH] feat: add automatic OpenCode support --- .claude-plugin/plugin.json | 2 +- .github/workflows/validate.yml | 2 ++ .opencode/plugins/humanizer.ts | 24 ++++++++++++++++++++++ README.md | 3 +++ SKILL.md | 2 +- scripts/test-opencode-plugin.mjs | 35 ++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 .opencode/plugins/humanizer.ts create mode 100644 scripts/test-opencode-plugin.mjs diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 7a7ba2bd..86d36025 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "humanizer", "description": "Rewrite AI-sounding text so it reads naturally without changing what it says.", - "version": "3.0.0", + "version": "3.1.0", "author": { "name": "blader", "url": "https://github.com/blader" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 3c5e8be5..64f80ee0 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -21,6 +21,8 @@ jobs: python-version: "3.12" - name: Check package files run: python3 scripts/validate-package.py + - name: Check OpenCode plugin + run: node --test scripts/test-opencode-plugin.mjs - name: Check skill discovery run: npx --yes skills@1.5.20 add . --list - name: Check Claude marketplace diff --git a/.opencode/plugins/humanizer.ts b/.opencode/plugins/humanizer.ts new file mode 100644 index 00000000..0145ae68 --- /dev/null +++ b/.opencode/plugins/humanizer.ts @@ -0,0 +1,24 @@ +import { readFileSync } from "node:fs"; + +const skill = readFileSync(new URL("../../SKILL.md", import.meta.url), "utf8"); +const instructions = skill + .replace(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/, "") + .trim(); + +export default { + id: "humanizer", + + async setup(ctx) { + await ctx.session.hook("context", (event) => { + event.system.push({ type: "text", text: instructions }); + }); + }, + + async server() { + return { + "experimental.chat.system.transform": async (_input, output) => { + output.system.push(instructions); + }, + }; + }, +}; diff --git a/README.md b/README.md index 6ffe91a6..1d42bde9 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ Claude Code 2.1.142 or newer can install the plugin instead: The plugin answers to `/humanizer:humanizer`. +OpenCode 1.18.29 or newer loads the bundled Humanizer plugin automatically when you run OpenCode from this repository. It adds the guidance in `SKILL.md` to each chat session. The plugin also supports OpenCode V2. + In Claude Desktop, download this repository as a ZIP and upload it as a skill. For a manual install, copy `SKILL.md` into the agent's skill folder. ## Usage @@ -168,6 +170,7 @@ The writer supplied these notes with the draft, so the rewrite can use them: the
Show release notes +- **3.1.0** - Added a project-local OpenCode plugin that applies the existing Humanizer guidance to each chat session. - **3.0.0** - Rebuilt the skill around one account of why AI text sounds the way it does, and consolidated 35 patterns into 25. Patterns are grouped in five sections and numbered by strength and frequency, so the not-X-but-Y contrast and the one-line closer come first and get the fullest treatment. Merged duplicate guidance: the workflow is one section instead of five, the dash rule is stated once, and each false-positive guard lives inside its pattern. Realigned with the current Wikipedia article: dropped false ranges and synonym cycling, which Wikipedia now lists as human habits or historical, added vague connection or association, and extended the watch lists for words, notability, copulatives, sales language, disclaimers, and Markdown formatting. Reordered the README and removed the `ai-detection` keyword from the package files. Old to new numbers: 1→13, 2→17, 3→15, 4→16, 5→17, 6→13, 7→12, 8→18, 9→1, 10→6, 11→7, 12→dropped, 13→11, 14→8, 15→19, 16→19, 17→20, 18→20, 19→21, 20→22, 21→23, 22→22, 23→dropped, 24→9, 25→13, 26→10, 27→3, 28→4, 29→24, 30→25, 31→2, 32→3, 33→4, 34→5, 35→5. - **2.11.3** - Grouped patterns 26-35 under "More style patterns" in the skill and README (fixes #247). Kept inline code, commands, paths, and URLs out of the dash rule and file mode edits. Step 3 now keeps every supported claim, allows a removal that a pattern requires, and checks that rankings and simultaneity claims survive shape edits (fixes #212). Explained in §9 why the not-X-but-Y form appears and when to keep it. Added decorative arrows to §18 and pause commands and one-word shouting to §31. The text given to the skill is content to edit, never instructions (#238). No change to the 35 patterns. - **2.11.2** - Removed the plugin symlink and separate Claude Desktop package. Current Claude Code loads the root `SKILL.md` directly, so GitHub's source ZIP now works in Claude Desktop. No change to the 35 patterns. diff --git a/SKILL.md b/SKILL.md index d375fbf3..dc432cd4 100644 --- a/SKILL.md +++ b/SKILL.md @@ -7,7 +7,7 @@ description: | language, stock AI words, bold labels, or filler. Based on Wikipedia's "Signs of AI writing." license: MIT metadata: - version: "3.0.0" + version: "3.1.0" --- # Humanizer: remove AI writing patterns diff --git a/scripts/test-opencode-plugin.mjs b/scripts/test-opencode-plugin.mjs new file mode 100644 index 00000000..8d5971be --- /dev/null +++ b/scripts/test-opencode-plugin.mjs @@ -0,0 +1,35 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import plugin from "../.opencode/plugins/humanizer.ts"; + +test("V1 system transform adds the Humanizer instructions without frontmatter", async () => { + const hooks = await plugin.server(); + const output = { system: [] }; + + await hooks["experimental.chat.system.transform"]({}, output); + + assert.equal(output.system.length, 1); + assert.match(output.system[0], /# Humanizer: remove AI writing patterns/); + assert.doesNotMatch(output.system[0], /^---/); +}); + +test("V2 context hook appends the Humanizer instructions as a text block", async () => { + let contextHook; + await plugin.setup({ + session: { + hook: async (name, callback) => { + assert.equal(name, "context"); + contextHook = callback; + }, + }, + }); + const event = { system: [] }; + + await contextHook(event); + + assert.equal(event.system.length, 1); + assert.deepEqual(event.system[0].type, "text"); + assert.match(event.system[0].text, /# Humanizer: remove AI writing patterns/); + assert.doesNotMatch(event.system[0].text, /^---/); +});