claudepluginhub 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/fetch.d.ts +20 -0
- package/dist/fetch.js +63 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +81 -0
- package/dist/install.d.ts +8 -0
- package/dist/install.js +41 -0
- package/dist/output.d.ts +11 -0
- package/dist/output.js +37 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# claudepluginhub
|
|
2
|
+
|
|
3
|
+
Install Claude Code plugins from [ClaudePluginHub](https://claudepluginhub.com) with a single command.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx claudepluginhub u/<userId>/<slug>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This will:
|
|
12
|
+
|
|
13
|
+
1. Fetch the plugin's marketplace JSON
|
|
14
|
+
2. Register the marketplace with Claude Code
|
|
15
|
+
3. Install all plugins from the marketplace
|
|
16
|
+
|
|
17
|
+
### Full URL
|
|
18
|
+
|
|
19
|
+
You can also pass the full marketplace JSON URL:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx claudepluginhub https://claudepluginhub.com/api/user-plugins/<userId>/<slug>/marketplace.json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Node.js 18+
|
|
28
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) installed and available in PATH
|
package/dist/fetch.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface MarketplacePlugin {
|
|
2
|
+
name: string;
|
|
3
|
+
source: {
|
|
4
|
+
source: string;
|
|
5
|
+
repo: string;
|
|
6
|
+
};
|
|
7
|
+
strict: boolean;
|
|
8
|
+
description?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface MarketplaceJSON {
|
|
12
|
+
name: string;
|
|
13
|
+
owner: {
|
|
14
|
+
name: string;
|
|
15
|
+
};
|
|
16
|
+
plugins: MarketplacePlugin[];
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveMarketplaceUrl(input: string): string | null;
|
|
19
|
+
export declare function fetchMarketplace(url: string): Promise<MarketplaceJSON | null>;
|
|
20
|
+
export {};
|
package/dist/fetch.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { printError } from './output.js';
|
|
2
|
+
const BASE_URL = 'https://claudepluginhub.com';
|
|
3
|
+
const ALLOWED_HOSTS = ['claudepluginhub.com', 'www.claudepluginhub.com'];
|
|
4
|
+
export function resolveMarketplaceUrl(input) {
|
|
5
|
+
// Full URL
|
|
6
|
+
if (input.startsWith('http://') || input.startsWith('https://')) {
|
|
7
|
+
try {
|
|
8
|
+
const parsed = new URL(input);
|
|
9
|
+
if (parsed.protocol !== 'https:') {
|
|
10
|
+
printError('Only HTTPS URLs are supported.');
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
if (!ALLOWED_HOSTS.includes(parsed.hostname)) {
|
|
14
|
+
printError(`Only claudepluginhub.com URLs are supported. Got: ${parsed.hostname}`);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return input;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
printError('Invalid URL format.');
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Short form: u/<userId>/<slug>
|
|
25
|
+
const match = input.match(/^u\/([^/]+)\/([^/]+)$/);
|
|
26
|
+
if (match) {
|
|
27
|
+
const [, userId, slug] = match;
|
|
28
|
+
return `${BASE_URL}/api/user-plugins/${userId}/${slug}/marketplace.json?source=cli`;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
export async function fetchMarketplace(url) {
|
|
33
|
+
try {
|
|
34
|
+
// Ensure ?source=cli is present for install tracking
|
|
35
|
+
const fetchUrl = url.includes('source=cli')
|
|
36
|
+
? url
|
|
37
|
+
: url.includes('?')
|
|
38
|
+
? `${url}&source=cli`
|
|
39
|
+
: `${url}?source=cli`;
|
|
40
|
+
const response = await fetch(fetchUrl, {
|
|
41
|
+
signal: AbortSignal.timeout(15_000),
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
if (response.status === 404) {
|
|
45
|
+
printError('Plugin not found. Check the identifier and try again.');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
printError(`HTTP ${response.status}: ${response.statusText}`);
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const data = (await response.json());
|
|
53
|
+
if (!data.name || !Array.isArray(data.plugins)) {
|
|
54
|
+
printError('Invalid marketplace JSON format.');
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return data;
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
printError(`Network error: ${err.message}`);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolveMarketplaceUrl, fetchMarketplace } from './fetch.js';
|
|
3
|
+
import { detectClaude, addMarketplace, installPlugin } from './install.js';
|
|
4
|
+
import { print, printBanner, printStep, printSuccess, printFail, printSummary, printError, } from './output.js';
|
|
5
|
+
function printUsage() {
|
|
6
|
+
print(`Usage: npx claudepluginhub <identifier>
|
|
7
|
+
|
|
8
|
+
Examples:
|
|
9
|
+
npx claudepluginhub u/<userId>/<slug>
|
|
10
|
+
npx claudepluginhub https://claudepluginhub.com/api/user-plugins/.../marketplace.json
|
|
11
|
+
`);
|
|
12
|
+
}
|
|
13
|
+
async function main() {
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
16
|
+
printBanner();
|
|
17
|
+
printUsage();
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
const input = args[0];
|
|
21
|
+
printBanner();
|
|
22
|
+
// Resolve to marketplace URL
|
|
23
|
+
const url = resolveMarketplaceUrl(input);
|
|
24
|
+
if (!url) {
|
|
25
|
+
printError(`Invalid input: ${input}`);
|
|
26
|
+
print('');
|
|
27
|
+
printUsage();
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
// Check claude CLI is available
|
|
31
|
+
printStep('Checking for Claude CLI...');
|
|
32
|
+
const claudePath = detectClaude();
|
|
33
|
+
if (!claudePath) {
|
|
34
|
+
printError('Claude CLI not found in PATH.\nInstall it from: https://docs.anthropic.com/en/docs/claude-code/overview');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
printSuccess('Claude CLI found');
|
|
38
|
+
// Fetch marketplace JSON
|
|
39
|
+
printStep('Fetching marketplace...');
|
|
40
|
+
const marketplace = await fetchMarketplace(url);
|
|
41
|
+
if (!marketplace) {
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
printSuccess(`"${marketplace.name}" - ${marketplace.plugins.length} plugin(s)`);
|
|
45
|
+
if (marketplace.plugins.length === 0) {
|
|
46
|
+
printError('No plugins found in this marketplace.');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
// Add marketplace
|
|
50
|
+
printStep('Adding marketplace...');
|
|
51
|
+
const addResult = addMarketplace(claudePath, url);
|
|
52
|
+
if (!addResult.ok) {
|
|
53
|
+
printError(`Failed to add marketplace: ${addResult.error}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
printSuccess('Marketplace registered');
|
|
57
|
+
// Install each plugin
|
|
58
|
+
print('');
|
|
59
|
+
printStep('Installing plugins...');
|
|
60
|
+
const results = [];
|
|
61
|
+
for (const plugin of marketplace.plugins) {
|
|
62
|
+
const installName = `${plugin.name}@${marketplace.name}`;
|
|
63
|
+
const result = installPlugin(claudePath, installName);
|
|
64
|
+
results.push({ name: plugin.name, ...result });
|
|
65
|
+
if (result.ok) {
|
|
66
|
+
printSuccess(plugin.name);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
printFail(plugin.name, result.error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
printSummary(results);
|
|
73
|
+
const failed = results.filter((r) => !r.ok).length;
|
|
74
|
+
if (failed > 0) {
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
main().catch((err) => {
|
|
79
|
+
printError(`Unexpected error: ${err.message}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface CommandResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
error?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function detectClaude(): string | null;
|
|
6
|
+
export declare function addMarketplace(claudePath: string, url: string): CommandResult;
|
|
7
|
+
export declare function installPlugin(claudePath: string, installName: string): CommandResult;
|
|
8
|
+
export {};
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
export function detectClaude() {
|
|
3
|
+
try {
|
|
4
|
+
execFileSync('claude', ['--version'], { stdio: 'pipe' });
|
|
5
|
+
return 'claude';
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function addMarketplace(claudePath, url) {
|
|
12
|
+
try {
|
|
13
|
+
execFileSync(claudePath, ['plugin', 'marketplace', 'add', url], {
|
|
14
|
+
stdio: 'pipe',
|
|
15
|
+
timeout: 30_000,
|
|
16
|
+
});
|
|
17
|
+
return { ok: true };
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const message = err.message;
|
|
21
|
+
// Marketplace already added is fine (idempotent)
|
|
22
|
+
if (message.includes('already') || message.includes('exists')) {
|
|
23
|
+
return { ok: true };
|
|
24
|
+
}
|
|
25
|
+
return { ok: false, error: message.split('\n')[0] };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function installPlugin(claudePath, installName) {
|
|
29
|
+
try {
|
|
30
|
+
execFileSync(claudePath, ['plugin', 'install', installName, '--scope', 'project'], { stdio: 'pipe', timeout: 60_000 });
|
|
31
|
+
return { ok: true };
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const message = err.message;
|
|
35
|
+
// Already installed is fine (idempotent)
|
|
36
|
+
if (message.includes('already installed')) {
|
|
37
|
+
return { ok: true };
|
|
38
|
+
}
|
|
39
|
+
return { ok: false, error: message.split('\n')[0] };
|
|
40
|
+
}
|
|
41
|
+
}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function print(message: string): void;
|
|
2
|
+
export declare function printError(message: string): void;
|
|
3
|
+
export declare function printBanner(): void;
|
|
4
|
+
export declare function printStep(label: string): void;
|
|
5
|
+
export declare function printSuccess(label: string): void;
|
|
6
|
+
export declare function printFail(label: string, error?: string): void;
|
|
7
|
+
export declare function printSummary(results: {
|
|
8
|
+
name: string;
|
|
9
|
+
ok: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
}[]): void;
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const RESET = '\x1b[0m';
|
|
2
|
+
const BOLD = '\x1b[1m';
|
|
3
|
+
const DIM = '\x1b[2m';
|
|
4
|
+
const RED = '\x1b[31m';
|
|
5
|
+
const GREEN = '\x1b[32m';
|
|
6
|
+
const YELLOW = '\x1b[33m';
|
|
7
|
+
const CYAN = '\x1b[36m';
|
|
8
|
+
export function print(message) {
|
|
9
|
+
console.log(message);
|
|
10
|
+
}
|
|
11
|
+
export function printError(message) {
|
|
12
|
+
console.error(`${RED}${BOLD}Error:${RESET} ${message}`);
|
|
13
|
+
}
|
|
14
|
+
export function printBanner() {
|
|
15
|
+
print(`\n${CYAN}${BOLD}ClaudePluginHub${RESET} ${DIM}Installer${RESET}\n`);
|
|
16
|
+
}
|
|
17
|
+
export function printStep(label) {
|
|
18
|
+
print(`${DIM}>${RESET} ${label}`);
|
|
19
|
+
}
|
|
20
|
+
export function printSuccess(label) {
|
|
21
|
+
print(` ${GREEN}ok${RESET} ${label}`);
|
|
22
|
+
}
|
|
23
|
+
export function printFail(label, error) {
|
|
24
|
+
print(` ${RED}fail${RESET} ${label}${error ? ` ${DIM}(${error})${RESET}` : ''}`);
|
|
25
|
+
}
|
|
26
|
+
export function printSummary(results) {
|
|
27
|
+
const succeeded = results.filter((r) => r.ok).length;
|
|
28
|
+
const failed = results.filter((r) => !r.ok).length;
|
|
29
|
+
print('');
|
|
30
|
+
if (failed === 0) {
|
|
31
|
+
print(`${GREEN}${BOLD}Done!${RESET} ${succeeded} plugin(s) installed successfully.`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
print(`${YELLOW}${BOLD}Done.${RESET} ${succeeded} succeeded, ${failed} failed.`);
|
|
35
|
+
}
|
|
36
|
+
print('');
|
|
37
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claudepluginhub",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Install Claude Code plugins from ClaudePluginHub",
|
|
5
|
+
"bin": {
|
|
6
|
+
"claudepluginhub": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"claude",
|
|
21
|
+
"claude-code",
|
|
22
|
+
"plugins",
|
|
23
|
+
"cli"
|
|
24
|
+
],
|
|
25
|
+
"author": "ClaudePluginHub",
|
|
26
|
+
"homepage": "https://claudepluginhub.com",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/heiberik/claudepluginhub.git",
|
|
30
|
+
"directory": "packages/cli"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
}
|
|
36
|
+
}
|