From 62767e3926f2a8771b4d05fa8f1cf361d1233eeb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 18:13:39 +0000 Subject: [PATCH 1/5] fix: version sync and simplify generation system Root cause: release config was missing @semantic-release/npm, so package.json versions were never bumped by semantic-release. Changes: - Add @semantic-release/npm (npmPublish: false) to release config - Make package.json the sole version source of truth (remove version from marketplace.json) - Merge duplicate plugin.json + package.json generation loops - Centralize release config in generate.mjs - Simplify release.yml: remove marketplace.json sync, add max-parallel - Simplify validate.yml: use --check mode - Remove redundant version sync check from validate-marketplace.sh - Set correct versions: ctx 1.2.0, strip-ansi 1.0.0, codeweaver 1.0.1 Agent-Logs-Url: https://github.com/knitli/toolshed/sessions/a3a8df14-cb8f-4d06-83be-b5f3cfa31a6f Co-authored-by: bashandbone <89049923+bashandbone@users.noreply.github.com> --- .claude-plugin/marketplace.json | 3 - .github/workflows/release.yml | 14 ++--- .github/workflows/validate.yml | 7 +-- CLAUDE.md | 20 +++---- plugins/codeweaver/.claude-plugin/plugin.json | 2 +- plugins/codeweaver/package.json | 8 ++- plugins/ctx/.claude-plugin/plugin.json | 2 +- plugins/ctx/package.json | 8 ++- plugins/strip-ansi/.claude-plugin/plugin.json | 2 +- plugins/strip-ansi/package.json | 8 ++- scripts/generate.mjs | 57 ++++++++++++------- scripts/validate-marketplace.sh | 10 ---- 12 files changed, 73 insertions(+), 68 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 87608ab..a39ec7f 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -20,7 +20,6 @@ { "name": "ctx", "source": "./plugins/ctx", - "version": "0.1.0", "description": "Context hygiene for AI-assisted codebases. Finds stale, contradictory, and drifting AI context files (CLAUDE.md, AGENTS.md, Serena memories, Cursor rules, and 10+ more tool ecosystems).", "keywords": ["context", "memory", "staleness", "drift", "hygiene", "CLAUDE.md", "AGENTS.md", "multi-agent"], "category": "quality", @@ -30,7 +29,6 @@ { "name": "strip-ansi", "source": "./plugins/strip-ansi", - "version": "0.1.0", "description": "Prevents ANSI escape sequences from polluting agent context, commits, PRs, and file edits. Injects NO_COLOR into Bash tool calls and sanitizes any residual escapes from output.", "keywords": ["ansi", "sanitize", "hygiene", "context", "no-color"], "category": "quality", @@ -39,7 +37,6 @@ { "name": "codeweaver", "source": "./plugins/codeweaver", - "version": "0.1.0", "description": "Exquisite Context for AI Agents — Semantic code search MCP server with hybrid search, AST-level understanding, and intelligent chunking for 166+ languages.", "keywords": ["mcp", "search", "semantic", "code-search", "semantic-search", "AST", "indexing", "code-intelligence", "embeddings", "vector-search", "hybrid-search", "codebase-analysis"], "category": "search", diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3338af9..875e3a8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,7 @@ jobs: release: runs-on: ubuntu-latest strategy: + max-parallel: 1 matrix: plugin: [ctx, strip-ansi, codeweaver] steps: @@ -32,16 +33,11 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npx semantic-release - - name: Sync version to marketplace.json and regenerate - run: | - PKG_VERSION=$(jq -r '.version' plugins/${{ matrix.plugin }}/package.json) - jq --arg plugin "${{ matrix.plugin }}" --arg v "$PKG_VERSION" \ - '(.plugins[] | select(.name == $plugin) | .version) = $v' \ - .claude-plugin/marketplace.json > tmp.json && mv tmp.json .claude-plugin/marketplace.json - node scripts/generate.mjs + - name: Regenerate plugin.json from bumped package.json + run: node scripts/generate.mjs - name: Commit version sync uses: stefanzweifel/git-auto-commit-action@v5 with: - commit_message: "chore(${{ matrix.plugin }}): sync version to marketplace.json and regenerate files" - file_pattern: ".claude-plugin/marketplace.json plugins/${{ matrix.plugin }}/.claude-plugin/plugin.json plugins/${{ matrix.plugin }}/package.json" + commit_message: "chore(${{ matrix.plugin }}): sync plugin.json version [skip ci]" + file_pattern: "plugins/${{ matrix.plugin }}/.claude-plugin/plugin.json" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index bc711c6..8252ac4 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -24,11 +24,8 @@ jobs: with: node-version: 22 - - name: Regenerate and verify no drift - run: node scripts/generate.mjs - - - name: Fail if any generated file was modified - run: git diff --exit-code + - name: Check generated files for drift + run: node scripts/generate.mjs --check commitlint: runs-on: ubuntu-latest diff --git a/CLAUDE.md b/CLAUDE.md index 8834979..dfccad2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,19 +20,15 @@ All plugin logic is **markdown prompts** interpreted by Claude Code at runtime. ### Version sync -Each plugin's version lives in two files that must match: -- `plugins//package.json` — semantic-release writes this on merge to main -- `plugins//.claude-plugin/plugin.json` — Claude Code reads this at install time - -The release workflow auto-syncs `plugin.json` after semantic-release bumps `package.json`. The validation script (`scripts/validate-marketplace.sh`) catches mismatches. +`package.json` is the sole source of truth for each plugin's version — semantic-release bumps it on merge to main. The generate script reads the version from `package.json` and propagates it to `plugin.json`. The release workflow runs generate after each release to keep them in sync. ### Key files -- `.claude-plugin/marketplace.json` — Discovery manifest listing all plugins with source paths +- `.claude-plugin/marketplace.json` — Discovery manifest listing all plugins with metadata (description, keywords, etc. — but NOT version) - `plugins//.claude-plugin/plugin.json` — Per-plugin manifest (name, version, description, mcpServers, userConfig) -- `plugins//package.json` — npm package with `semantic-release-monorepo` config +- `plugins//package.json` — npm package; version source of truth, release config generated centrally - `.commitlintrc.json` — Enforces conventional commits; `scope-enum` must list all plugin names -- `.github/workflows/release.yml` — Per-plugin semantic-release via matrix; `matrix.plugin` must list all plugin names +- `.github/workflows/release.yml` — Per-plugin semantic-release via matrix (max-parallel: 1); `matrix.plugin` must list all plugin names - `.github/workflows/validate.yml` — PR gate: runs marketplace validation + commitlint ### Plugin component types @@ -87,11 +83,9 @@ The Stop event re-fires if the hook's output is treated as new model feedback. L ## Adding a new plugin -Four files must be updated in sync: -1. Create `plugins//.claude-plugin/plugin.json` and `plugins//package.json` with matching versions -2. Add entry to `.claude-plugin/marketplace.json` `plugins` array -3. Add plugin name to `scope-enum` in `.commitlintrc.json` -4. Add plugin name to `matrix.plugin` in `.github/workflows/release.yml` +1. Add the plugin entry to `.claude-plugin/marketplace.json` `plugins` array (name, source, description, keywords, etc.) +2. Run `npm run generate` — this creates all derived files (`plugin.json`, `package.json`, `.commitlintrc.json`, `release.yml`) +3. Optionally run `npm run generate -- --new ` to also scaffold `commands/` directory and `README.md` ## Commit convention diff --git a/plugins/codeweaver/.claude-plugin/plugin.json b/plugins/codeweaver/.claude-plugin/plugin.json index deb4c63..d9ee7ee 100644 --- a/plugins/codeweaver/.claude-plugin/plugin.json +++ b/plugins/codeweaver/.claude-plugin/plugin.json @@ -9,7 +9,7 @@ } }, "name": "codeweaver", - "version": "0.1.0", + "version": "1.0.1", "description": "Exquisite Context for AI Agents — Semantic code search MCP server with hybrid search, AST-level understanding, and intelligent chunking for 166+ languages.", "author": { "name": "Knitli Inc.", diff --git a/plugins/codeweaver/package.json b/plugins/codeweaver/package.json index f4b04b3..b1659f7 100644 --- a/plugins/codeweaver/package.json +++ b/plugins/codeweaver/package.json @@ -1,6 +1,6 @@ { "name": "@knitli/codeweaver", - "version": "0.1.0", + "version": "1.0.1", "private": true, "description": "Exquisite Context for AI Agents — Semantic code search MCP server with hybrid search, AST-level understanding, and intelligent chunking for 166+ languages.", "author": { @@ -33,6 +33,12 @@ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/changelog", + [ + "@semantic-release/npm", + { + "npmPublish": false + } + ], "@semantic-release/git", "@semantic-release/github" ] diff --git a/plugins/ctx/.claude-plugin/plugin.json b/plugins/ctx/.claude-plugin/plugin.json index ef731ef..f7348d2 100644 --- a/plugins/ctx/.claude-plugin/plugin.json +++ b/plugins/ctx/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "ctx", - "version": "0.1.0", + "version": "1.2.0", "description": "Context hygiene for AI-assisted codebases. Finds stale, contradictory, and drifting AI context files (CLAUDE.md, AGENTS.md, Serena memories, Cursor rules, and 10+ more tool ecosystems).", "author": { "name": "Knitli Inc.", diff --git a/plugins/ctx/package.json b/plugins/ctx/package.json index 461fcee..a8dd1a4 100644 --- a/plugins/ctx/package.json +++ b/plugins/ctx/package.json @@ -1,6 +1,6 @@ { "name": "@knitli/ctx", - "version": "0.1.0", + "version": "1.2.0", "private": true, "description": "Context hygiene for AI-assisted codebases. Finds stale, contradictory, and drifting AI context files (CLAUDE.md, AGENTS.md, Serena memories, Cursor rules, and 10+ more tool ecosystems).", "author": { @@ -29,6 +29,12 @@ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/changelog", + [ + "@semantic-release/npm", + { + "npmPublish": false + } + ], "@semantic-release/git", "@semantic-release/github" ] diff --git a/plugins/strip-ansi/.claude-plugin/plugin.json b/plugins/strip-ansi/.claude-plugin/plugin.json index c1edbc0..4a92146 100644 --- a/plugins/strip-ansi/.claude-plugin/plugin.json +++ b/plugins/strip-ansi/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "strip-ansi", - "version": "0.1.0", + "version": "1.0.0", "description": "Prevents ANSI escape sequences from polluting agent context, commits, PRs, and file edits. Injects NO_COLOR into Bash tool calls and sanitizes any residual escapes from output.", "author": { "name": "Knitli Inc.", diff --git a/plugins/strip-ansi/package.json b/plugins/strip-ansi/package.json index 35802b9..8f57087 100644 --- a/plugins/strip-ansi/package.json +++ b/plugins/strip-ansi/package.json @@ -1,6 +1,6 @@ { "name": "@knitli/strip-ansi", - "version": "0.1.0", + "version": "1.0.0", "private": true, "description": "Prevents ANSI escape sequences from polluting agent context, commits, PRs, and file edits. Injects NO_COLOR into Bash tool calls and sanitizes any residual escapes from output.", "author": { @@ -26,6 +26,12 @@ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/changelog", + [ + "@semantic-release/npm", + { + "npmPublish": false + } + ], "@semantic-release/git", "@semantic-release/github" ] diff --git a/scripts/generate.mjs b/scripts/generate.mjs index b4106ea..d73f41e 100644 --- a/scripts/generate.mjs +++ b/scripts/generate.mjs @@ -114,11 +114,34 @@ const updatedReleaseYml = releaseYml.replace( writeOrCheck(releaseYmlPath, updatedReleaseYml); // --------------------------------------------------------------------------- -// 3. plugins//.claude-plugin/plugin.json +// Shared release config — generated for every plugin's package.json. +// Includes @semantic-release/npm (npmPublish: false) so that semantic-release +// actually bumps the version in package.json on each release. +// --------------------------------------------------------------------------- + +const RELEASE_CONFIG = { + extends: 'semantic-release-monorepo', + plugins: [ + '@semantic-release/commit-analyzer', + '@semantic-release/release-notes-generator', + '@semantic-release/changelog', + ['@semantic-release/npm', { npmPublish: false }], + '@semantic-release/git', + '@semantic-release/github', + ], +}; + +// --------------------------------------------------------------------------- +// 3. Per-plugin: .claude-plugin/plugin.json + package.json +// +// Version source of truth: package.json (managed by semantic-release). +// The generate script reads the version from the existing package.json +// and propagates it to plugin.json. New plugins default to "0.0.0". // --------------------------------------------------------------------------- for (const plugin of plugins) { const pluginDir = join(ROOT, plugin.source.replace(/^\.\//, '')); + const pkgJsonPath = join(pluginDir, 'package.json'); const dotClaudePluginDir = join(pluginDir, '.claude-plugin'); const pluginJsonPath = join(dotClaudePluginDir, 'plugin.json'); @@ -126,13 +149,18 @@ for (const plugin of plugins) { mkdirSync(dotClaudePluginDir, { recursive: true }); } + // Read existing package.json — its version is the source of truth. + const existingPkg = existsSync(pkgJsonPath) ? readJSON(pkgJsonPath) : {}; + const version = existingPkg.version ?? '0.0.0'; + + // --- plugin.json --- const extensions = pluginManifestExtensions[plugin.name] ?? {}; // Spread extensions first so canonical fields always win if there's a conflict. const pluginJson = { ...extensions, name: plugin.name, - version: plugin.version, + version, description: plugin.description, author: shared.author, homepage: plugin.homepage ?? shared.homepage, @@ -143,26 +171,11 @@ for (const plugin of plugins) { }; writeOrCheck(pluginJsonPath, JSON.stringify(pluginJson, null, 2) + '\n'); -} - -// --------------------------------------------------------------------------- -// 4. plugins//package.json -// --------------------------------------------------------------------------- - -for (const plugin of plugins) { - const pluginDir = join(ROOT, plugin.source.replace(/^\.\//, '')); - const pkgJsonPath = join(pluginDir, 'package.json'); - - if (!CHECK_MODE) { - mkdirSync(pluginDir, { recursive: true }); - } - - // Preserve the release config block from the existing file if present. - const existing = existsSync(pkgJsonPath) ? readJSON(pkgJsonPath) : {}; + // --- package.json --- const pkgJson = { - name: existing.name ?? `@${shared.npmScope}/${plugin.name}`, - version: plugin.version, + name: existingPkg.name ?? `@${shared.npmScope}/${plugin.name}`, + version, private: shared.private ?? true, description: plugin.description, author: shared.author, @@ -173,14 +186,14 @@ for (const plugin of plugins) { type: 'git', url: shared.repository, }, - ...(existing.release !== undefined && { release: existing.release }), + release: RELEASE_CONFIG, }; writeOrCheck(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + '\n'); } // --------------------------------------------------------------------------- -// 5. --new scaffolding +// 4. --new scaffolding // --------------------------------------------------------------------------- if (NEW_PLUGIN) { diff --git a/scripts/validate-marketplace.sh b/scripts/validate-marketplace.sh index d7a1a2e..d2ab63e 100755 --- a/scripts/validate-marketplace.sh +++ b/scripts/validate-marketplace.sh @@ -51,16 +51,6 @@ for i in $(seq 0 $((PLUGIN_COUNT - 1))); do continue fi - # Version sync: package.json version == plugin.json version - if [ -f "$PLUGIN_DIR/package.json" ]; then - PKG_VERSION=$(jq -r '.version' "$PLUGIN_DIR/package.json") - PLUGIN_VERSION=$(jq -r '.version' "$PLUGIN_DIR/.claude-plugin/plugin.json") - if [ "$PKG_VERSION" != "$PLUGIN_VERSION" ]; then - echo " FAIL: version mismatch — package.json=$PKG_VERSION, plugin.json=$PLUGIN_VERSION" - ERRORS=$((ERRORS + 1)) - fi - fi - echo " OK" done From d7a216f2d145faf3201bf05967019fab62c76ab8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 18:27:55 +0000 Subject: [PATCH 2/5] fix: add @semantic-release/npm as explicit devDependency Agent-Logs-Url: https://github.com/knitli/toolshed/sessions/be6d01b5-8a1a-41eb-aa06-5b6d8bd4194a Co-authored-by: bashandbone <89049923+bashandbone@users.noreply.github.com> --- package-lock.json | 11 ++++++++--- package.json | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c643e8..0f9e1f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^12.0.6", + "@semantic-release/npm": "^13.1.1", "@semantic-release/release-notes-generator": "^14.1.0", "semantic-release": "^25.0.3", "semantic-release-monorepo": "^8.0.2" @@ -31,6 +32,7 @@ "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^12.0.6", + "@semantic-release/npm": "^13.1.1", "@semantic-release/release-notes-generator": "^14.1.0", "semantic-release": "^25.0.3", "semantic-release-monorepo": "^8.0.2" @@ -7633,15 +7635,18 @@ }, "plugins/codeweaver": { "name": "@knitli/codeweaver", - "version": "0.1.0" + "version": "1.0.1", + "license": "MIT OR Apache-2.0" }, "plugins/ctx": { "name": "@knitli/ctx", - "version": "0.1.0" + "version": "1.2.0", + "license": "MIT" }, "plugins/strip-ansi": { "name": "@knitli/strip-ansi", - "version": "0.1.0" + "version": "1.0.0", + "license": "MIT" } } } diff --git a/package.json b/package.json index 6b4ac15..fc84be2 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^12.0.6", + "@semantic-release/npm": "^13.1.1", "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/release-notes-generator": "^14.1.0", "@commitlint/cli": "^20.5.0", @@ -26,6 +27,7 @@ "@semantic-release/changelog": "^6.0.3", "@semantic-release/git": "^10.0.1", "@semantic-release/github": "^12.0.6", + "@semantic-release/npm": "^13.1.1", "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/release-notes-generator": "^14.1.0", "@commitlint/cli": "^20.5.0", From 2c0315ffc96b725daace2840be7a820761e4daca Mon Sep 17 00:00:00 2001 From: Adam Poulemanos <89049923+bashandbone@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:34:56 -0400 Subject: [PATCH 3/5] Update GitHub Actions checkout action version Signed-off-by: Adam Poulemanos <89049923+bashandbone@users.noreply.github.com> --- .github/workflows/claude-code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index b5e8cfd..5ae9a3f 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 1 From 1abaa0dbf8af593d50033d5b3b4bfec569ad814c Mon Sep 17 00:00:00 2001 From: Adam Poulemanos <89049923+bashandbone@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:39:02 -0400 Subject: [PATCH 4/5] Update actions/checkout version to v6 Signed-off-by: Adam Poulemanos <89049923+bashandbone@users.noreply.github.com> --- .github/workflows/claude.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index d300267..94dcce5 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -26,7 +26,7 @@ jobs: actions: read # Required for Claude to read CI results on PRs steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 1 From ca82888204787905cc059e90ae02e9370f50ec9d Mon Sep 17 00:00:00 2001 From: Adam Poulemanos <89049923+bashandbone@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:40:05 -0400 Subject: [PATCH 5/5] Upgrade GitHub Actions to v6 for checkout and setup-node Signed-off-by: Adam Poulemanos <89049923+bashandbone@users.noreply.github.com> --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 875e3a8..2e16875 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,11 +17,11 @@ jobs: matrix: plugin: [ctx, strip-ansi, codeweaver] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: 22