Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -39,7 +38,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",
Expand All @@ -54,7 +52,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",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 1

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 7 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ jobs:
release:
runs-on: ubuntu-latest
strategy:
max-parallel: 1
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: 24

Expand All @@ -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@v7
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"
7 changes: 2 additions & 5 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@ jobs:
with:
node-version: 24

- 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
Expand Down
20 changes: 7 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<name>/package.json` — semantic-release writes this on merge to main
- `plugins/<name>/.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/<name>/.claude-plugin/plugin.json` — Per-plugin manifest (name, version, description, mcpServers, userConfig)
- `plugins/<name>/package.json` — npm package with `semantic-release-monorepo` config
- `plugins/<name>/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
Expand Down Expand Up @@ -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/<name>/.claude-plugin/plugin.json` and `plugins/<name>/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 <name>` to also scaffold `commands/` directory and `README.md`

## Commit convention

Expand Down
11 changes: 8 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion plugins/codeweaver/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
8 changes: 7 additions & 1 deletion plugins/codeweaver/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down Expand Up @@ -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"
]
Expand Down
2 changes: 1 addition & 1 deletion plugins/ctx/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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.",
Expand Down
8 changes: 7 additions & 1 deletion plugins/ctx/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down Expand Up @@ -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"
]
Expand Down
2 changes: 1 addition & 1 deletion plugins/strip-ansi/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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.",
Expand Down
8 changes: 7 additions & 1 deletion plugins/strip-ansi/package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand All @@ -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"
]
Expand Down
57 changes: 35 additions & 22 deletions scripts/generate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -114,25 +114,53 @@ const updatedReleaseYml = releaseYml.replace(
writeOrCheck(releaseYmlPath, updatedReleaseYml);

// ---------------------------------------------------------------------------
// 3. plugins/<name>/.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',
],
Comment on lines +122 to +131
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added @semantic-release/npm@^13.1.1 as an explicit devDependency in root package.json (both in devDependencies and catalogs.release) and updated the lockfile. Commit: d7a216f.

};

// ---------------------------------------------------------------------------
// 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');

if (!CHECK_MODE) {
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,
Expand All @@ -143,26 +171,11 @@ for (const plugin of plugins) {
};

writeOrCheck(pluginJsonPath, JSON.stringify(pluginJson, null, 2) + '\n');
}

// ---------------------------------------------------------------------------
// 4. plugins/<name>/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,
Expand All @@ -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) {
Expand Down
10 changes: 0 additions & 10 deletions scripts/validate-marketplace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading