Skip to content

Commit dc23d47

Browse files
robertherberclaude
andcommitted
feat: add react plugin to marketplace and validate completeness
- Add missing react plugin to marketplace.json - Create scripts/validate-marketplace.ts to detect missing plugins - Update CI workflow to run marketplace validation before other checks - Add react to workflow paths and validation loop 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 791df6b commit dc23d47

3 files changed

Lines changed: 94 additions & 1 deletion

File tree

.claude-plugin/marketplace.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,18 @@
126126
"tags": ["bun", "setup", "codegen", "environment"],
127127
"license": "MIT"
128128
},
129+
{
130+
"name": "react",
131+
"source": "./react",
132+
"description": "React best practices: lean hooks, component patterns, and performance optimization.",
133+
"author": {
134+
"name": "Kingstinct",
135+
"email": "robert@kingstinct.com"
136+
},
137+
"category": "development",
138+
"tags": ["react", "hooks", "components", "performance", "best-practices"],
139+
"license": "MIT"
140+
},
129141
{
130142
"name": "ralph",
131143
"source": {

.github/workflows/validate-plugins.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ on:
99
- 'general/**'
1010
- 'kysely-sql/**'
1111
- 'planning/**'
12+
- 'react/**'
1213
- 'typescript/**'
14+
- 'scripts/**'
1315
pull_request:
1416
paths:
1517
- '.claude-plugin/**'
@@ -18,14 +20,22 @@ on:
1820
- 'general/**'
1921
- 'kysely-sql/**'
2022
- 'planning/**'
23+
- 'react/**'
2124
- 'typescript/**'
25+
- 'scripts/**'
2226

2327
jobs:
2428
validate:
2529
runs-on: ubuntu-latest
2630
steps:
2731
- uses: actions/checkout@v4
2832

33+
- name: Setup Bun
34+
uses: oven-sh/setup-bun@v2
35+
36+
- name: Validate marketplace completeness
37+
run: bun run scripts/validate-marketplace.ts
38+
2939
- name: Install Claude Code CLI
3040
run: npm install -g @anthropic-ai/claude-code
3141

@@ -34,7 +44,7 @@ jobs:
3444

3545
- name: Validate individual plugins
3646
run: |
37-
for plugin in biome bun general kysely-sql planning typescript; do
47+
for plugin in biome bun general kysely-sql planning react typescript; do
3848
echo "=== Validating $plugin ==="
3949
claude plugin validate "$plugin"
4050
done

scripts/validate-marketplace.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env bun
2+
/**
3+
* Validates that all local plugins with .claude-plugin directories
4+
* are listed in the marketplace.json file.
5+
*/
6+
7+
import { readdir, exists } from "node:fs/promises";
8+
import { join, dirname, resolve } from "node:path";
9+
10+
// Script is in .github/scripts/, so root is parent directory
11+
const ROOT_DIR = dirname(import.meta.dirname);
12+
const MARKETPLACE_PATH = join(ROOT_DIR, ".claude-plugin", "marketplace.json");
13+
14+
async function findLocalPlugins(): Promise<string[]> {
15+
const entries = await readdir(ROOT_DIR, { withFileTypes: true });
16+
const plugins: string[] = [];
17+
18+
for (const entry of entries) {
19+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
20+
21+
const pluginConfigPath = join(ROOT_DIR, entry.name, ".claude-plugin", "plugin.json");
22+
if (await exists(pluginConfigPath)) {
23+
plugins.push(entry.name);
24+
}
25+
}
26+
27+
return plugins.sort();
28+
}
29+
30+
async function getMarketplacePlugins(): Promise<string[]> {
31+
const marketplace = await Bun.file(MARKETPLACE_PATH).json();
32+
return marketplace.plugins
33+
.filter((p: { source: string | object }) => typeof p.source === "string" && p.source.startsWith("./"))
34+
.map((p: { source: string }) => p.source.replace("./", ""))
35+
.sort();
36+
}
37+
38+
async function main() {
39+
const localPlugins = await findLocalPlugins();
40+
const marketplacePlugins = await getMarketplacePlugins();
41+
42+
const missing = localPlugins.filter((p) => !marketplacePlugins.includes(p));
43+
const extra = marketplacePlugins.filter((p) => !localPlugins.includes(p));
44+
45+
let hasErrors = false;
46+
47+
if (missing.length > 0) {
48+
console.error("❌ Local plugins missing from marketplace.json:");
49+
for (const plugin of missing) {
50+
console.error(` - ${plugin}`);
51+
}
52+
hasErrors = true;
53+
}
54+
55+
if (extra.length > 0) {
56+
console.error("❌ Plugins in marketplace.json without local directory:");
57+
for (const plugin of extra) {
58+
console.error(` - ${plugin}`);
59+
}
60+
hasErrors = true;
61+
}
62+
63+
if (hasErrors) {
64+
console.error("\nPlease update .claude-plugin/marketplace.json to match local plugins.");
65+
process.exit(1);
66+
}
67+
68+
console.log(`✅ All ${localPlugins.length} local plugins are in marketplace.json`);
69+
}
70+
71+
main();

0 commit comments

Comments
 (0)