claude-code-pack 1.0.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 +120 -0
- package/assets/statusline-command.sh +116 -0
- package/bin/claude-pack.mjs +48 -0
- package/claude-pack.config.json +37 -0
- package/package.json +31 -0
- package/skills/cloud-devops/SKILL.md +235 -0
- package/skills/fastapi/SKILL.md +436 -0
- package/skills/fastapi/references/dependencies.md +142 -0
- package/skills/fastapi/references/other-tools.md +76 -0
- package/skills/fastapi/references/streaming.md +105 -0
- package/skills/senior-ml-engineer/SKILL.md +304 -0
- package/skills/senior-ml-engineer/references/llm_integration_guide.md +317 -0
- package/skills/senior-ml-engineer/references/mlops_production_patterns.md +265 -0
- package/skills/senior-ml-engineer/references/rag_system_architecture.md +371 -0
- package/skills/senior-ml-engineer/scripts/ml_monitoring_suite.py +100 -0
- package/skills/senior-ml-engineer/scripts/model_deployment_pipeline.py +100 -0
- package/skills/senior-ml-engineer/scripts/rag_system_builder.py +100 -0
- package/skills/technical-writer/technical-writer/SKILL.md +351 -0
- package/src/install.mjs +391 -0
package/src/install.mjs
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync } from 'node:fs';
|
|
3
|
+
import { join, dirname, resolve } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const PACK_ROOT = resolve(__dirname, '..');
|
|
9
|
+
|
|
10
|
+
const CLAUDE_DIR = join(homedir(), '.claude');
|
|
11
|
+
const PLUGINS_DIR = join(CLAUDE_DIR, 'plugins');
|
|
12
|
+
const MARKETPLACES_DIR = join(PLUGINS_DIR, 'marketplaces');
|
|
13
|
+
const CACHE_DIR = join(PLUGINS_DIR, 'cache');
|
|
14
|
+
const SKILLS_DIR = join(CLAUDE_DIR, 'skills');
|
|
15
|
+
const SETTINGS_PATH = join(CLAUDE_DIR, 'settings.json');
|
|
16
|
+
const KNOWN_MARKETPLACES_PATH = join(PLUGINS_DIR, 'known_marketplaces.json');
|
|
17
|
+
const INSTALLED_PLUGINS_PATH = join(PLUGINS_DIR, 'installed_plugins.json');
|
|
18
|
+
|
|
19
|
+
function log(icon, msg) {
|
|
20
|
+
console.log(` ${icon} ${msg}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ensureDir(dir) {
|
|
24
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readJSON(path, fallback = {}) {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
30
|
+
} catch {
|
|
31
|
+
return fallback;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function writeJSON(path, data) {
|
|
36
|
+
ensureDir(dirname(path));
|
|
37
|
+
writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function loadConfig() {
|
|
41
|
+
const configPath = join(PACK_ROOT, 'claude-pack.config.json');
|
|
42
|
+
return JSON.parse(readFileSync(configPath, 'utf8'));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function gitClone(repo, dest) {
|
|
46
|
+
if (existsSync(dest)) {
|
|
47
|
+
// Pull latest
|
|
48
|
+
try {
|
|
49
|
+
execSync(`git -C "${dest}" pull --ff-only`, { stdio: 'pipe' });
|
|
50
|
+
return 'updated';
|
|
51
|
+
} catch {
|
|
52
|
+
// If pull fails, remove and re-clone
|
|
53
|
+
execSync(`rm -rf "${dest}"`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
execSync(`git clone --depth 1 "https://github.com/${repo}.git" "${dest}"`, {
|
|
57
|
+
stdio: 'pipe',
|
|
58
|
+
});
|
|
59
|
+
return 'cloned';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getPluginVersion(marketplaceDir, pluginName) {
|
|
63
|
+
// Try to read version from plugin's package.json or manifest
|
|
64
|
+
const candidates = [
|
|
65
|
+
join(marketplaceDir, 'plugin', 'package.json'),
|
|
66
|
+
join(marketplaceDir, 'package.json'),
|
|
67
|
+
];
|
|
68
|
+
for (const p of candidates) {
|
|
69
|
+
try {
|
|
70
|
+
const pkg = JSON.parse(readFileSync(p, 'utf8'));
|
|
71
|
+
if (pkg.version) return pkg.version;
|
|
72
|
+
} catch { /* continue */ }
|
|
73
|
+
}
|
|
74
|
+
return '0.0.0';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getGitCommitSha(dir) {
|
|
78
|
+
try {
|
|
79
|
+
return execSync(`git -C "${dir}" rev-parse HEAD`, { encoding: 'utf8' }).trim();
|
|
80
|
+
} catch {
|
|
81
|
+
return '';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function findPluginInstallPath(marketplaceDir, pluginName) {
|
|
86
|
+
// Look for a plugin/ subdirectory or the root
|
|
87
|
+
const pluginSubdir = join(marketplaceDir, 'plugin');
|
|
88
|
+
if (existsSync(pluginSubdir)) return pluginSubdir;
|
|
89
|
+
return marketplaceDir;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ─── Plugin Installation ───────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
function installPlugins(config, flags) {
|
|
95
|
+
console.log('\n📦 Plugins');
|
|
96
|
+
|
|
97
|
+
// 1. Install official marketplace first (needed for external plugins)
|
|
98
|
+
const officialDir = join(MARKETPLACES_DIR, 'claude-plugins-official');
|
|
99
|
+
if (!existsSync(officialDir)) {
|
|
100
|
+
if (flags.dryRun) {
|
|
101
|
+
log('○', 'Would clone official marketplace: anthropics/claude-plugins-official');
|
|
102
|
+
} else {
|
|
103
|
+
log('↓', 'Cloning official marketplace...');
|
|
104
|
+
gitClone('anthropics/claude-plugins-official', officialDir);
|
|
105
|
+
log('✓', 'Official marketplace installed');
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
if (flags.dryRun) {
|
|
109
|
+
log('●', 'Official marketplace: already installed');
|
|
110
|
+
} else {
|
|
111
|
+
log('↻', 'Updating official marketplace...');
|
|
112
|
+
gitClone('anthropics/claude-plugins-official', officialDir);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 2. Install third-party marketplaces
|
|
117
|
+
const knownMarketplaces = readJSON(KNOWN_MARKETPLACES_PATH);
|
|
118
|
+
|
|
119
|
+
for (const mp of config.marketplaces || []) {
|
|
120
|
+
const dest = join(MARKETPLACES_DIR, mp.name);
|
|
121
|
+
if (flags.dryRun) {
|
|
122
|
+
const exists = existsSync(dest);
|
|
123
|
+
log(exists ? '●' : '○', `Marketplace "${mp.name}": ${exists ? 'already installed' : `would clone ${mp.repo}`}`);
|
|
124
|
+
} else {
|
|
125
|
+
log('↓', `Installing marketplace "${mp.name}" from ${mp.repo}...`);
|
|
126
|
+
const result = gitClone(mp.repo, dest);
|
|
127
|
+
log('✓', `Marketplace "${mp.name}" ${result}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Register in known_marketplaces.json
|
|
131
|
+
if (!flags.dryRun) {
|
|
132
|
+
knownMarketplaces[mp.name] = {
|
|
133
|
+
source: { source: 'github', repo: mp.repo },
|
|
134
|
+
installLocation: dest,
|
|
135
|
+
lastUpdated: new Date().toISOString(),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!flags.dryRun) {
|
|
141
|
+
// Also register official marketplace
|
|
142
|
+
knownMarketplaces['claude-plugins-official'] = {
|
|
143
|
+
source: { source: 'github', repo: 'anthropics/claude-plugins-official' },
|
|
144
|
+
installLocation: officialDir,
|
|
145
|
+
lastUpdated: new Date().toISOString(),
|
|
146
|
+
};
|
|
147
|
+
writeJSON(KNOWN_MARKETPLACES_PATH, knownMarketplaces);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 3. Install and enable individual plugins
|
|
151
|
+
const installedPlugins = readJSON(INSTALLED_PLUGINS_PATH, { version: 2, plugins: {} });
|
|
152
|
+
if (!installedPlugins.version) installedPlugins.version = 2;
|
|
153
|
+
if (!installedPlugins.plugins) installedPlugins.plugins = {};
|
|
154
|
+
|
|
155
|
+
const settings = readJSON(SETTINGS_PATH);
|
|
156
|
+
if (!settings.enabledPlugins) settings.enabledPlugins = {};
|
|
157
|
+
if (!settings.extraKnownMarketplaces) settings.extraKnownMarketplaces = {};
|
|
158
|
+
|
|
159
|
+
for (const plugin of config.plugins || []) {
|
|
160
|
+
const pluginId = `${plugin.name}@${plugin.marketplace}`;
|
|
161
|
+
const marketplaceDir = join(MARKETPLACES_DIR, plugin.marketplace);
|
|
162
|
+
const alreadyInstalled = installedPlugins.plugins[pluginId];
|
|
163
|
+
|
|
164
|
+
if (alreadyInstalled && !flags.force) {
|
|
165
|
+
if (flags.dryRun) {
|
|
166
|
+
log('●', `Plugin "${pluginId}": already installed`);
|
|
167
|
+
} else {
|
|
168
|
+
log('●', `Plugin "${pluginId}": already installed (use --force to reinstall)`);
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
if (flags.dryRun) {
|
|
172
|
+
log('○', `Would install plugin "${pluginId}" from ${plugin.repo}`);
|
|
173
|
+
} else {
|
|
174
|
+
const version = getPluginVersion(marketplaceDir, plugin.name);
|
|
175
|
+
const sha = getGitCommitSha(marketplaceDir);
|
|
176
|
+
const pluginSrc = findPluginInstallPath(marketplaceDir, plugin.name);
|
|
177
|
+
|
|
178
|
+
// Cache the plugin
|
|
179
|
+
const cacheDir = join(CACHE_DIR, plugin.marketplace, plugin.name, version);
|
|
180
|
+
ensureDir(dirname(cacheDir));
|
|
181
|
+
if (existsSync(cacheDir) && flags.force) {
|
|
182
|
+
execSync(`rm -rf "${cacheDir}"`);
|
|
183
|
+
}
|
|
184
|
+
if (!existsSync(cacheDir)) {
|
|
185
|
+
cpSync(pluginSrc, cacheDir, { recursive: true });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
installedPlugins.plugins[pluginId] = [
|
|
189
|
+
{
|
|
190
|
+
scope: 'user',
|
|
191
|
+
installPath: cacheDir,
|
|
192
|
+
version,
|
|
193
|
+
installedAt: new Date().toISOString(),
|
|
194
|
+
lastUpdated: new Date().toISOString(),
|
|
195
|
+
gitCommitSha: sha,
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
log('✓', `Plugin "${pluginId}" installed (v${version})`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Enable plugin in settings
|
|
204
|
+
if (!flags.dryRun) {
|
|
205
|
+
if (plugin.enabled !== false) {
|
|
206
|
+
settings.enabledPlugins[pluginId] = true;
|
|
207
|
+
}
|
|
208
|
+
// Add marketplace to extraKnownMarketplaces in settings
|
|
209
|
+
const mp = (config.marketplaces || []).find((m) => m.name === plugin.marketplace);
|
|
210
|
+
if (mp) {
|
|
211
|
+
settings.extraKnownMarketplaces[mp.name] = {
|
|
212
|
+
source: { source: 'github', repo: mp.repo },
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Also register non-plugin marketplaces in settings
|
|
219
|
+
for (const mp of config.marketplaces || []) {
|
|
220
|
+
if (!flags.dryRun) {
|
|
221
|
+
settings.extraKnownMarketplaces[mp.name] = {
|
|
222
|
+
source: { source: 'github', repo: mp.repo },
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!flags.dryRun) {
|
|
228
|
+
writeJSON(INSTALLED_PLUGINS_PATH, installedPlugins);
|
|
229
|
+
writeJSON(SETTINGS_PATH, settings);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ─── Skill Installation ────────────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
function installSkills(config, flags) {
|
|
236
|
+
console.log('\n🛠 Skills');
|
|
237
|
+
|
|
238
|
+
for (const skill of config.skills || []) {
|
|
239
|
+
const dest = join(SKILLS_DIR, skill.name);
|
|
240
|
+
const exists = existsSync(dest);
|
|
241
|
+
|
|
242
|
+
if (skill.source === 'bundled') {
|
|
243
|
+
// Copy from package's bundled skills/
|
|
244
|
+
const src = join(PACK_ROOT, 'skills', skill.name);
|
|
245
|
+
if (!existsSync(src)) {
|
|
246
|
+
log('✗', `Skill "${skill.name}": bundled source not found at ${src}`);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (exists && !flags.force) {
|
|
251
|
+
if (flags.dryRun) {
|
|
252
|
+
log('●', `Skill "${skill.name}": already installed`);
|
|
253
|
+
} else {
|
|
254
|
+
log('●', `Skill "${skill.name}": already installed (use --force to overwrite)`);
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
if (flags.dryRun) {
|
|
258
|
+
log('○', `Would install bundled skill "${skill.name}"`);
|
|
259
|
+
} else {
|
|
260
|
+
ensureDir(SKILLS_DIR);
|
|
261
|
+
cpSync(src, dest, { recursive: true });
|
|
262
|
+
log('✓', `Skill "${skill.name}" installed (bundled)`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} else if (skill.source === 'github' && skill.repo) {
|
|
266
|
+
// Clone from GitHub
|
|
267
|
+
if (exists && !flags.force) {
|
|
268
|
+
if (flags.dryRun) {
|
|
269
|
+
log('●', `Skill "${skill.name}": already installed`);
|
|
270
|
+
} else {
|
|
271
|
+
log('↻', `Updating skill "${skill.name}" from ${skill.repo}...`);
|
|
272
|
+
gitClone(skill.repo, dest);
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
if (flags.dryRun) {
|
|
276
|
+
log('○', `Would clone skill "${skill.name}" from ${skill.repo}`);
|
|
277
|
+
} else {
|
|
278
|
+
log('↓', `Cloning skill "${skill.name}" from ${skill.repo}...`);
|
|
279
|
+
const result = gitClone(skill.repo, dest);
|
|
280
|
+
log('✓', `Skill "${skill.name}" ${result}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
log('✗', `Skill "${skill.name}": unknown source type "${skill.source}"`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ─── Settings Merge ────────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
function installSettings(config, flags) {
|
|
292
|
+
console.log('\n⚙ Settings');
|
|
293
|
+
|
|
294
|
+
const settings = readJSON(SETTINGS_PATH);
|
|
295
|
+
|
|
296
|
+
// Merge model preference
|
|
297
|
+
if (config.settings?.model) {
|
|
298
|
+
if (flags.dryRun) {
|
|
299
|
+
log('○', `Would set model to "${config.settings.model}"`);
|
|
300
|
+
} else {
|
|
301
|
+
settings.model = config.settings.model;
|
|
302
|
+
log('✓', `Model set to "${config.settings.model}"`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Merge statusline
|
|
307
|
+
if (config.settings?.statusLine) {
|
|
308
|
+
if (flags.dryRun) {
|
|
309
|
+
log('○', 'Would configure statusline');
|
|
310
|
+
} else {
|
|
311
|
+
// Copy statusline script
|
|
312
|
+
if (config.assets?.statusline) {
|
|
313
|
+
const src = join(PACK_ROOT, config.assets.statusline);
|
|
314
|
+
const dest = join(CLAUDE_DIR, 'statusline-command.sh');
|
|
315
|
+
if (existsSync(src)) {
|
|
316
|
+
const content = readFileSync(src, 'utf8');
|
|
317
|
+
writeFileSync(dest, content, { mode: 0o755 });
|
|
318
|
+
log('✓', 'Statusline script installed');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// Resolve $HOME in the command
|
|
322
|
+
const cmd = config.settings.statusLine.command.replace('$HOME', homedir());
|
|
323
|
+
settings.statusLine = {
|
|
324
|
+
...config.settings.statusLine,
|
|
325
|
+
command: cmd,
|
|
326
|
+
};
|
|
327
|
+
log('✓', 'Statusline configured');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Merge MCP servers
|
|
332
|
+
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
333
|
+
if (!settings.mcpServers) settings.mcpServers = {};
|
|
334
|
+
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
335
|
+
if (flags.dryRun) {
|
|
336
|
+
log('○', `Would configure MCP server "${name}"`);
|
|
337
|
+
} else {
|
|
338
|
+
settings.mcpServers[name] = serverConfig;
|
|
339
|
+
log('✓', `MCP server "${name}" configured`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
log('●', 'No MCP servers to configure (they come from plugins)');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (!flags.dryRun) {
|
|
347
|
+
writeJSON(SETTINGS_PATH, settings);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ─── Main Install ──────────────────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
export async function install(flags = {}) {
|
|
354
|
+
const config = loadConfig();
|
|
355
|
+
|
|
356
|
+
console.log('╔══════════════════════════════════════════╗');
|
|
357
|
+
console.log('║ claude-pack installer ║');
|
|
358
|
+
console.log('╚══════════════════════════════════════════╝');
|
|
359
|
+
|
|
360
|
+
if (flags.dryRun) {
|
|
361
|
+
console.log('\n (dry run — no changes will be made)\n');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Ensure base directories exist
|
|
365
|
+
if (!flags.dryRun) {
|
|
366
|
+
ensureDir(CLAUDE_DIR);
|
|
367
|
+
ensureDir(PLUGINS_DIR);
|
|
368
|
+
ensureDir(MARKETPLACES_DIR);
|
|
369
|
+
ensureDir(SKILLS_DIR);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Check git is available
|
|
373
|
+
try {
|
|
374
|
+
execSync('git --version', { stdio: 'pipe' });
|
|
375
|
+
} catch {
|
|
376
|
+
console.error('\n ✗ git is required but not found. Please install git first.');
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (!flags.skipPlugins) installPlugins(config, flags);
|
|
381
|
+
if (!flags.skipSkills) installSkills(config, flags);
|
|
382
|
+
if (!flags.skipSettings) installSettings(config, flags);
|
|
383
|
+
|
|
384
|
+
console.log('\n────────────────────────────────────────────');
|
|
385
|
+
if (flags.dryRun) {
|
|
386
|
+
console.log(' Dry run complete. Run without --dry-run to apply.');
|
|
387
|
+
} else {
|
|
388
|
+
console.log(' ✓ All done! Restart Claude Code to pick up changes.');
|
|
389
|
+
}
|
|
390
|
+
console.log('');
|
|
391
|
+
}
|