diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 36bd408..d30fc59 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -13,11 +13,11 @@ "source": { "source": "npm", "package": "@ruddercode/rudder-plugin", - "version": "0.1.4", + "version": "0.1.5", "registry": "https://registry.npmjs.org" }, "description": "Generate focused unit tests from coding-session intent and worktree changes using your existing coding agent.", - "version": "0.1.4", + "version": "0.1.5", "author": { "name": "RudderCode", "email": "vivek@archermoney.com" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 60ff60e..b78b573 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "rudder", - "version": "0.1.4", + "version": "0.1.5", "description": "Generate focused unit tests from coding-session intent and worktree changes using your existing coding agent.", "author": { "name": "Vivek Yanamadula", @@ -17,6 +17,5 @@ "coverage" ], "skills": "./skills/", - "mcpServers": "./.claude-mcp.json", - "hooks": "./hooks/hooks.json" + "mcpServers": "./.claude-mcp.json" } diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 83bdc77..83c58e8 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "rudder", - "version": "0.1.4", + "version": "0.1.5", "description": "Generate focused unit tests from coding-session intent and worktree changes using your existing coding agent.", "author": { "name": "Vivek Yanamadula", diff --git a/package-lock.json b/package-lock.json index c4dbbdf..46ee85f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ruddercode/rudder-plugin", - "version": "0.1.4", + "version": "0.1.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ruddercode/rudder-plugin", - "version": "0.1.4", + "version": "0.1.5", "license": "Apache-2.0", "devDependencies": { "@modelcontextprotocol/ext-apps": "^1.7.5", diff --git a/package.json b/package.json index 532d560..dd57f2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ruddercode/rudder-plugin", - "version": "0.1.4", + "version": "0.1.5", "description": "Generate focused unit tests from coding-session intent and worktree changes.", "type": "module", "engines": { diff --git a/test/plugin-package.test.ts b/test/plugin-package.test.ts index 653c1aa..9475e43 100644 --- a/test/plugin-package.test.ts +++ b/test/plugin-package.test.ts @@ -1,6 +1,13 @@ import assert from 'node:assert/strict'; import { execFileSync } from 'node:child_process'; -import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { + existsSync, + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { after, before, test } from 'node:test'; @@ -64,7 +71,6 @@ test('ships matching Codex and Claude plugin metadata', () => { assert.equal(claude.mcpServers, './.claude-mcp.json'); assert.equal(codex.mcpServers, './.mcp.json'); assert.ok(codex.interface.capabilities.includes('Interactive')); - assert.equal(claude.hooks, './hooks/hooks.json'); assert.ok(codex.interface.shortDescription.length <= 30); assert.match(codex.interface.privacyPolicyURL, /^https:\/\//); assert.match(codex.interface.termsOfServiceURL, /^https:\/\//); @@ -268,6 +274,23 @@ test('keeps the release PostHog host explicit', () => { ); }); +test('leaves plugin hooks at the path both hosts discover on their own', () => { + const claude = JSON.parse( + readFileSync(join(pluginRoot, '.claude-plugin', 'plugin.json'), 'utf8') + ); + const codex = JSON.parse( + readFileSync(join(pluginRoot, '.codex-plugin', 'plugin.json'), 'utf8') + ); + + // Claude Code and Codex both load hooks/hooks.json from the plugin root without being + // told to. A manifest that names that same path registers the file a second time: + // Claude Code 2.0.60 and later fail the plugin with a duplicate hooks error, and earlier + // releases run every hook twice. Only non-default hook files belong in `hooks`. + assert.ok(existsSync(join(pluginRoot, 'hooks', 'hooks.json'))); + assert.equal(claude.hooks, undefined); + assert.equal(codex.hooks, undefined); +}); + test('registers prompt submission and stop hooks from the plugin root', () => { const config = JSON.parse( readFileSync(join(pluginRoot, 'hooks', 'hooks.json'), 'utf8')