@yibeichan/claude-skills 1.0.2
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/LICENSE +21 -0
- package/README.md +98 -0
- package/cli.js +272 -0
- package/install.py +240 -0
- package/package.json +44 -0
- package/skills/bidsapp-nidm-standards/SKILL.md +202 -0
- package/skills/bidsapp-nidm-standards/references/babs_config.md +20 -0
- package/skills/bidsapp-nidm-standards/references/cli_arguments.md +76 -0
- package/skills/bidsapp-nidm-standards/references/container_patterns.md +53 -0
- package/skills/bidsapp-nidm-standards/references/nidm_integration.md +403 -0
- package/skills/bidsapp-nidm-standards/references/repo_structure.md +121 -0
- package/skills/bidsapp-nidm-standards/references/testing_patterns.md +82 -0
- package/skills/dicom2fmriprep/SKILL.md +377 -0
- package/skills/dicom2fmriprep/evals/evals.json +26 -0
- package/skills/dicom2fmriprep/references/babs-details.md +407 -0
- package/skills/dicom2fmriprep/references/fmriprep-details.md +250 -0
- package/skills/dicom2fmriprep/references/heudiconv-details.md +243 -0
- package/skills/fmri-ssm/SKILL.md +317 -0
- package/skills/fmri-ssm/references/code_templates.md +1570 -0
- package/skills/fmri-ssm/references/downstream_analysis.md +680 -0
- package/skills/fmri-ssm/references/group_inference.md +608 -0
- package/skills/fmri-ssm/references/hrf_modeling.md +447 -0
- package/skills/fmri-ssm/references/model_catalog.md +436 -0
- package/skills/fmri-ssm/references/paradigm_guide.md +406 -0
- package/skills/fmri-ssm/references/preprocessing.md +614 -0
- package/skills/fmri-ssm.zip +0 -0
- package/skills/neuroimaging-qc/SKILL.md +203 -0
- package/skills/neuroimaging-qc/references/eeg_qc.md +400 -0
- package/skills/neuroimaging-qc/references/fmri_qc.md +343 -0
- package/skills/neuroimaging-qc/references/fnirs_qc.md +430 -0
- package/skills/neuroimaging-qc/references/structural_qc.md +454 -0
- package/skills/neuroimaging-qc/scripts/parse_fmriprep_confounds.py +153 -0
- package/skills/neuroimaging-qc/scripts/parse_mriqc.py +114 -0
- package/skills/neuroimaging-qc/scripts/qc_report.py +295 -0
- package/skills/scientific-writer/SKILL.md +202 -0
- package/skills/scientific-writer/references/citation_styles.md +163 -0
- package/skills/scientific-writer/references/field_conventions.md +245 -0
- package/skills/scientific-writer/references/figures_tables.md +225 -0
- package/skills/scientific-writer/references/reporting_guidelines.md +225 -0
- package/skills.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Yibei Chen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Claude Skills
|
|
2
|
+
|
|
3
|
+
A collection of reusable Claude skills for neuroimaging research workflows and scientific writing.
|
|
4
|
+
|
|
5
|
+
## Available Skills
|
|
6
|
+
|
|
7
|
+
| Skill | Install | Description |
|
|
8
|
+
|-------|---------|-------------|
|
|
9
|
+
| [dicom2fmriprep](skills/dicom2fmriprep/SKILL.md) | `npx @yibeichen/claude-skills install dicom2fmriprep` | Full DICOM→BIDS→fMRIPrep pipeline: heudiconv heuristics, BIDS validation fixes, fMRIPrep on SLURM via BABS. |
|
|
10
|
+
| [fmri-ssm](skills/fmri-ssm/SKILL.md) | `npx @yibeichen/claude-skills install fmri-ssm` | State-space models for fMRI: HMM, SLDS, rSLDS, SNLDS for resting-state, task, and naturalistic designs. |
|
|
11
|
+
| [neuroimaging-qc](skills/neuroimaging-qc/SKILL.md) | `npx @yibeichen/claude-skills install neuroimaging-qc` | Evidence-based QC decisions for fMRI, EEG, fNIRS using metrics from fMRIPrep, MRIQC, FreeSurfer. |
|
|
12
|
+
| [bidsapp-nidm-standards](skills/bidsapp-nidm-standards/SKILL.md) | `npx @yibeichen/claude-skills install bidsapp-nidm-standards` | Standards for creating NIDM-integrated BIDSapps that run through BABS. |
|
|
13
|
+
| [scientific-writer](skills/scientific-writer/SKILL.md) | `npx @yibeichen/claude-skills install scientific-writer` | Rigorous scientific manuscripts following IMRAD, CONSORT/STROBE/PRISMA guidelines. |
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Skills are installed to `.claude/skills/` in your current working directory by default.
|
|
18
|
+
|
|
19
|
+
### Quick Install (Recommended)
|
|
20
|
+
|
|
21
|
+
Install a specific skill using npx:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @yibeichen/claude-skills install bidsapp-nidm-standards
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Install all skills:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx @yibeichen/claude-skills install-all
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
List available skills:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx @yibeichen/claude-skills list
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Custom Target Directory
|
|
40
|
+
|
|
41
|
+
Install to a custom directory:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx @yibeichen/claude-skills install bidsapp-nidm-standards --target ./my-skills
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Overwrite Existing Skills
|
|
48
|
+
|
|
49
|
+
If a skill already exists, use `--overwrite` to replace it:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx @yibeichen/claude-skills install bidsapp-nidm-standards --overwrite
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Python Script (Alternative)
|
|
56
|
+
|
|
57
|
+
If you don't have npm/node installed, use the Python script:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# List skills
|
|
61
|
+
python install.py --list
|
|
62
|
+
|
|
63
|
+
# Install a skill
|
|
64
|
+
python install.py bidsapp-nidm-standards
|
|
65
|
+
|
|
66
|
+
# Install all skills
|
|
67
|
+
python install.py --all
|
|
68
|
+
|
|
69
|
+
# Custom target
|
|
70
|
+
python install.py scientific-writer --target ./skills
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Uninstall Skills
|
|
74
|
+
|
|
75
|
+
Remove installed skills:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Uninstall a specific skill
|
|
79
|
+
npx @yibeichen/claude-skills uninstall neuroimaging-qc
|
|
80
|
+
|
|
81
|
+
# Uninstall all skills
|
|
82
|
+
npx @yibeichen/claude-skills uninstall-all
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Or with Python:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
python install.py --uninstall neuroimaging-qc
|
|
89
|
+
python install.py --uninstall-all
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Development
|
|
93
|
+
|
|
94
|
+
See [CLAUDE.md](CLAUDE.md) for development guidelines or [MAINTAINERS.md](MAINTAINERS.md) for adding/updating skills.
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT
|
package/cli.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Claude Skills CLI
|
|
5
|
+
* Install and manage Claude skills from this repository
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { createRequire } from 'module';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
|
|
17
|
+
// Load package.json
|
|
18
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
19
|
+
|
|
20
|
+
// Load skills manifest
|
|
21
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, 'skills.json'), 'utf8'));
|
|
22
|
+
|
|
23
|
+
// Default Claude Code skills directory (relative to current working directory)
|
|
24
|
+
const DEFAULT_SKILLS_DIR = path.join(process.cwd(), '.claude', 'skills');
|
|
25
|
+
|
|
26
|
+
function printHeader() {
|
|
27
|
+
console.log(`\x1b[36mClaude Skills\x1b[0m v${packageJson.version}`);
|
|
28
|
+
console.log(`Repository: ${packageJson.repository.url}\n`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function listSkills() {
|
|
32
|
+
printHeader();
|
|
33
|
+
console.log('Available skills:\n');
|
|
34
|
+
|
|
35
|
+
for (const skill of manifest.skills) {
|
|
36
|
+
console.log(`\x1b[1;36m${skill.name}\x1b[0m`);
|
|
37
|
+
console.log(` Version: ${skill.version || '1.0.0'}`);
|
|
38
|
+
console.log(` Description: ${skill.description || 'No description'}`);
|
|
39
|
+
console.log(` Tags: ${skill.tags?.join(', ') || 'none'}`);
|
|
40
|
+
console.log('');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function copyDirectory(src, dest) {
|
|
45
|
+
// Create destination directory if it doesn't exist
|
|
46
|
+
if (!fs.existsSync(dest)) {
|
|
47
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Copy all files and subdirectories
|
|
51
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
52
|
+
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
const srcPath = path.join(src, entry.name);
|
|
55
|
+
const destPath = path.join(dest, entry.name);
|
|
56
|
+
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
copyDirectory(srcPath, destPath);
|
|
59
|
+
} else {
|
|
60
|
+
fs.copyFileSync(srcPath, destPath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function installSkill(skillName, options = {}) {
|
|
66
|
+
const targetDir = options.target || DEFAULT_SKILLS_DIR;
|
|
67
|
+
const overwrite = options.overwrite || false;
|
|
68
|
+
|
|
69
|
+
// Find the skill
|
|
70
|
+
const skill = manifest.skills.find(s => s.name === skillName);
|
|
71
|
+
|
|
72
|
+
if (!skill) {
|
|
73
|
+
console.error(`\x1b[31mError: Skill '${skillName}' not found.\x1b[0m`);
|
|
74
|
+
console.log("Run 'npx claude-skills list' to see available skills.");
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Source path
|
|
79
|
+
const sourcePath = path.join(__dirname, skill.path);
|
|
80
|
+
if (!fs.existsSync(sourcePath)) {
|
|
81
|
+
console.error(`\x1b[31mError: Skill directory not found at ${sourcePath}\x1b[0m`);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Target path
|
|
86
|
+
const targetPath = path.join(targetDir, skillName);
|
|
87
|
+
|
|
88
|
+
// Check if already exists
|
|
89
|
+
if (fs.existsSync(targetPath)) {
|
|
90
|
+
if (!overwrite) {
|
|
91
|
+
console.error(`\x1b[31mError: Target directory already exists: ${targetPath}\x1b[0m`);
|
|
92
|
+
console.log("Use --overwrite to replace the existing skill.");
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Create target directory
|
|
99
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
100
|
+
|
|
101
|
+
// Copy the skill
|
|
102
|
+
console.log(`Installing ${skillName}...`);
|
|
103
|
+
console.log(` Source: ${sourcePath}`);
|
|
104
|
+
console.log(` Target: ${targetPath}`);
|
|
105
|
+
|
|
106
|
+
copyDirectory(sourcePath, targetPath);
|
|
107
|
+
|
|
108
|
+
console.log(`\x1b[32m✓ Successfully installed ${skillName}\x1b[0m\n`);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function installAll(options = {}) {
|
|
113
|
+
printHeader();
|
|
114
|
+
console.log('Installing all skills...\n');
|
|
115
|
+
|
|
116
|
+
let success = true;
|
|
117
|
+
for (const skill of manifest.skills) {
|
|
118
|
+
if (!installSkill(skill.name, options)) {
|
|
119
|
+
success = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return success;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function uninstallSkill(skillName, options = {}) {
|
|
127
|
+
const targetDir = options.target || DEFAULT_SKILLS_DIR;
|
|
128
|
+
const targetPath = path.join(targetDir, skillName);
|
|
129
|
+
|
|
130
|
+
if (!fs.existsSync(targetPath)) {
|
|
131
|
+
console.error(`\x1b[31mError: Skill '${skillName}' is not installed at ${targetPath}\x1b[0m`);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(`Uninstalling ${skillName}...`);
|
|
136
|
+
console.log(` Removing: ${targetPath}`);
|
|
137
|
+
|
|
138
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
139
|
+
|
|
140
|
+
// Clean up empty parent directories
|
|
141
|
+
try {
|
|
142
|
+
fs.rmdirSync(targetDir);
|
|
143
|
+
fs.rmdirSync(path.dirname(targetDir));
|
|
144
|
+
} catch (e) {
|
|
145
|
+
// Ignore if directories are not empty
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log(`\x1b[32m✓ Successfully uninstalled ${skillName}\x1b[0m\n`);
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function uninstallAll(options = {}) {
|
|
153
|
+
printHeader();
|
|
154
|
+
console.log('Uninstalling all skills...\n');
|
|
155
|
+
|
|
156
|
+
const targetDir = options.target || DEFAULT_SKILLS_DIR;
|
|
157
|
+
|
|
158
|
+
if (!fs.existsSync(targetDir)) {
|
|
159
|
+
console.log('No skills installed.');
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const installedSkills = fs.readdirSync(targetDir, { withFileTypes: true })
|
|
164
|
+
.filter(d => d.isDirectory())
|
|
165
|
+
.map(d => d.name);
|
|
166
|
+
|
|
167
|
+
if (installedSkills.length === 0) {
|
|
168
|
+
console.log('No skills installed.');
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let success = true;
|
|
173
|
+
for (const skillName of installedSkills) {
|
|
174
|
+
if (!uninstallSkill(skillName, options)) {
|
|
175
|
+
success = false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return success;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function printHelp() {
|
|
183
|
+
printHeader();
|
|
184
|
+
console.log(`Usage: npx @yibeichan/claude-skills [command] [options]
|
|
185
|
+
|
|
186
|
+
Commands:
|
|
187
|
+
list, ls List all available skills
|
|
188
|
+
install <skill> Install a specific skill
|
|
189
|
+
install-all Install all skills
|
|
190
|
+
uninstall <skill> Uninstall a specific skill
|
|
191
|
+
uninstall-all Uninstall all skills
|
|
192
|
+
|
|
193
|
+
Options:
|
|
194
|
+
--target, -t <dir> Target directory for skills (default: ./.claude/skills)
|
|
195
|
+
--overwrite, -o Overwrite existing skill directory
|
|
196
|
+
--help, -h Show this help message
|
|
197
|
+
|
|
198
|
+
Examples:
|
|
199
|
+
npx @yibeichan/claude-skills list
|
|
200
|
+
npx @yibeichan/claude-skills install bidsapp-nidm-standards
|
|
201
|
+
npx @yibeichan/claude-skills install scientific-writer --target ./my-skills
|
|
202
|
+
npx @yibeichan/claude-skills install-all --overwrite
|
|
203
|
+
npx @yibeichan/claude-skills uninstall neuroimaging-qc
|
|
204
|
+
npx @yibeichan/claude-skills uninstall-all
|
|
205
|
+
`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Main CLI logic
|
|
209
|
+
function main() {
|
|
210
|
+
const args = process.argv.slice(2);
|
|
211
|
+
const command = args[0];
|
|
212
|
+
|
|
213
|
+
const options = {
|
|
214
|
+
target: null,
|
|
215
|
+
overwrite: false
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Parse options
|
|
219
|
+
for (let i = 1; i < args.length; i++) {
|
|
220
|
+
if (args[i] === '--target' || args[i] === '-t') {
|
|
221
|
+
options.target = args[++i];
|
|
222
|
+
} else if (args[i] === '--overwrite' || args[i] === '-o') {
|
|
223
|
+
options.overwrite = true;
|
|
224
|
+
} else if (args[i] === '--help' || args[i] === '-h') {
|
|
225
|
+
printHelp();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
switch (command) {
|
|
231
|
+
case 'list':
|
|
232
|
+
case 'ls':
|
|
233
|
+
listSkills();
|
|
234
|
+
break;
|
|
235
|
+
|
|
236
|
+
case 'install':
|
|
237
|
+
if (args[1] && !args[1].startsWith('-')) {
|
|
238
|
+
installSkill(args[1], options);
|
|
239
|
+
} else {
|
|
240
|
+
console.error('\x1b[31mError: Please specify a skill name.\x1b[0m');
|
|
241
|
+
console.log("Run 'npx @yibeichan/claude-skills list' to see available skills.");
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
|
|
245
|
+
case 'install-all':
|
|
246
|
+
installAll(options);
|
|
247
|
+
break;
|
|
248
|
+
|
|
249
|
+
case 'uninstall':
|
|
250
|
+
if (args[1] && !args[1].startsWith('-')) {
|
|
251
|
+
uninstallSkill(args[1], options);
|
|
252
|
+
} else {
|
|
253
|
+
console.error('\x1b[31mError: Please specify a skill name.\x1b[0m');
|
|
254
|
+
console.log("Run 'npx @yibeichan/claude-skills list' to see available skills.");
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
|
|
258
|
+
case 'uninstall-all':
|
|
259
|
+
uninstallAll(options);
|
|
260
|
+
break;
|
|
261
|
+
|
|
262
|
+
default:
|
|
263
|
+
if (!command || command.startsWith('-')) {
|
|
264
|
+
printHelp();
|
|
265
|
+
} else {
|
|
266
|
+
console.error(`\x1b[31mUnknown command: ${command}\x1b[0m`);
|
|
267
|
+
printHelp();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
main();
|
package/install.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Install Claude skills from this repository to a target directory.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python install.py --list # List available skills
|
|
7
|
+
python install.py bidsapp-nidm-standards # Install a skill (default: ./.claude/skills/)
|
|
8
|
+
python install.py scientific-writer --target ./skills
|
|
9
|
+
python install.py --all # Install all skills
|
|
10
|
+
python install.py --uninstall neuroimaging-qc # Uninstall a skill
|
|
11
|
+
python install.py --uninstall-all # Uninstall all skills
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import shutil
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Default Claude Code skills directory (relative to current working directory)
|
|
24
|
+
DEFAULT_SKILLS_DIR = Path.cwd() / ".claude" / "skills"
|
|
25
|
+
|
|
26
|
+
# Repository root
|
|
27
|
+
REPO_ROOT = Path(__file__).parent
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def load_manifest() -> dict:
|
|
31
|
+
"""Load the skills manifest."""
|
|
32
|
+
manifest_path = REPO_ROOT / "skills.json"
|
|
33
|
+
if not manifest_path.exists():
|
|
34
|
+
print(f"Error: skills.json not found at {manifest_path}")
|
|
35
|
+
sys.exit(1)
|
|
36
|
+
with open(manifest_path) as f:
|
|
37
|
+
return json.load(f)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def list_skills():
|
|
41
|
+
"""List all available skills."""
|
|
42
|
+
manifest = load_manifest()
|
|
43
|
+
print("Available skills:")
|
|
44
|
+
print()
|
|
45
|
+
for skill in manifest.get("skills", []):
|
|
46
|
+
print(f" {skill['name']}")
|
|
47
|
+
print(f" Version: {skill.get('version', '1.0.0')}")
|
|
48
|
+
print(f" Description: {skill.get('description', 'No description')}")
|
|
49
|
+
print(f" Tags: {', '.join(skill.get('tags', []))}")
|
|
50
|
+
print()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def install_skill(skill_name: str, target_dir: Path, overwrite: bool = False) -> bool:
|
|
54
|
+
"""Install a skill to the target directory."""
|
|
55
|
+
manifest = load_manifest()
|
|
56
|
+
|
|
57
|
+
# Find the skill
|
|
58
|
+
skill = next((s for s in manifest.get("skills", []) if s["name"] == skill_name), None)
|
|
59
|
+
if not skill:
|
|
60
|
+
print(f"Error: Skill '{skill_name}' not found.")
|
|
61
|
+
print("Run 'python install.py --list' to see available skills.")
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
# Source path
|
|
65
|
+
source_path = REPO_ROOT / skill["path"]
|
|
66
|
+
if not source_path.exists():
|
|
67
|
+
print(f"Error: Skill directory not found at {source_path}")
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
# Target path
|
|
71
|
+
target_path = target_dir / skill_name
|
|
72
|
+
|
|
73
|
+
# Check if already exists
|
|
74
|
+
if target_path.exists():
|
|
75
|
+
if not overwrite:
|
|
76
|
+
print(f"Error: Target directory already exists: {target_path}")
|
|
77
|
+
print("Use --overwrite to replace the existing skill.")
|
|
78
|
+
return False
|
|
79
|
+
shutil.rmtree(target_path)
|
|
80
|
+
|
|
81
|
+
# Create target directory
|
|
82
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
|
|
84
|
+
# Copy the skill
|
|
85
|
+
print(f"Installing {skill_name}...")
|
|
86
|
+
print(f" Source: {source_path}")
|
|
87
|
+
print(f" Target: {target_path}")
|
|
88
|
+
|
|
89
|
+
shutil.copytree(source_path, target_path)
|
|
90
|
+
|
|
91
|
+
print(f"✓ Successfully installed {skill_name}")
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def install_all_skills(target_dir: Path, overwrite: bool = False) -> bool:
|
|
96
|
+
"""Install all skills."""
|
|
97
|
+
manifest = load_manifest()
|
|
98
|
+
success = True
|
|
99
|
+
|
|
100
|
+
for skill in manifest.get("skills", []):
|
|
101
|
+
if not install_skill(skill["name"], target_dir, overwrite):
|
|
102
|
+
success = False
|
|
103
|
+
|
|
104
|
+
return success
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def uninstall_skill(skill_name: str, target_dir: Path) -> bool:
|
|
108
|
+
"""Uninstall a skill from the target directory."""
|
|
109
|
+
target_path = target_dir / skill_name
|
|
110
|
+
|
|
111
|
+
if not target_path.exists():
|
|
112
|
+
print(f"Error: Skill '{skill_name}' is not installed at {target_path}")
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
print(f"Uninstalling {skill_name}...")
|
|
116
|
+
print(f" Removing: {target_path}")
|
|
117
|
+
|
|
118
|
+
shutil.rmtree(target_path)
|
|
119
|
+
|
|
120
|
+
# Clean up empty parent directories
|
|
121
|
+
try:
|
|
122
|
+
target_dir.rmdir()
|
|
123
|
+
target_dir.parent.rmdir()
|
|
124
|
+
except OSError:
|
|
125
|
+
# Ignore if directories are not empty
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
print(f"✓ Successfully uninstalled {skill_name}")
|
|
129
|
+
return True
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def uninstall_all_skills(target_dir: Path) -> bool:
|
|
133
|
+
"""Uninstall all skills."""
|
|
134
|
+
if not target_dir.exists():
|
|
135
|
+
print("No skills installed.")
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
installed_skills = [d for d in target_dir.iterdir() if d.is_dir()]
|
|
139
|
+
|
|
140
|
+
if not installed_skills:
|
|
141
|
+
print("No skills installed.")
|
|
142
|
+
return True
|
|
143
|
+
|
|
144
|
+
success = True
|
|
145
|
+
for skill_dir in installed_skills:
|
|
146
|
+
if not uninstall_skill(skill_dir.name, target_dir):
|
|
147
|
+
success = False
|
|
148
|
+
|
|
149
|
+
return success
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def main():
|
|
153
|
+
parser = argparse.ArgumentParser(
|
|
154
|
+
description="Install Claude skills from this repository",
|
|
155
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
156
|
+
epilog="""
|
|
157
|
+
Examples:
|
|
158
|
+
python install.py --list
|
|
159
|
+
python install.py bidsapp-nidm-standards
|
|
160
|
+
python install.py scientific-writer --target ./my-skills
|
|
161
|
+
python install.py --all --overwrite
|
|
162
|
+
python install.py --uninstall neuroimaging-qc
|
|
163
|
+
python install.py --uninstall-all
|
|
164
|
+
"""
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
"skill",
|
|
169
|
+
nargs="?",
|
|
170
|
+
help="Name of the skill to install/uninstall (use --list to see available)"
|
|
171
|
+
)
|
|
172
|
+
parser.add_argument(
|
|
173
|
+
"--target", "-t",
|
|
174
|
+
type=Path,
|
|
175
|
+
default=DEFAULT_SKILLS_DIR,
|
|
176
|
+
help=f"Target directory for skills (default: ./.claude/skills)"
|
|
177
|
+
)
|
|
178
|
+
parser.add_argument(
|
|
179
|
+
"--list", "-l",
|
|
180
|
+
action="store_true",
|
|
181
|
+
help="List available skills"
|
|
182
|
+
)
|
|
183
|
+
parser.add_argument(
|
|
184
|
+
"--all",
|
|
185
|
+
action="store_true",
|
|
186
|
+
help="Install all available skills"
|
|
187
|
+
)
|
|
188
|
+
parser.add_argument(
|
|
189
|
+
"--overwrite", "-o",
|
|
190
|
+
action="store_true",
|
|
191
|
+
help="Overwrite existing skill directory"
|
|
192
|
+
)
|
|
193
|
+
parser.add_argument(
|
|
194
|
+
"--uninstall",
|
|
195
|
+
action="store_true",
|
|
196
|
+
help="Uninstall the specified skill"
|
|
197
|
+
)
|
|
198
|
+
parser.add_argument(
|
|
199
|
+
"--uninstall-all",
|
|
200
|
+
action="store_true",
|
|
201
|
+
help="Uninstall all skills"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
args = parser.parse_args()
|
|
205
|
+
|
|
206
|
+
# List mode
|
|
207
|
+
if args.list:
|
|
208
|
+
list_skills()
|
|
209
|
+
return 0
|
|
210
|
+
|
|
211
|
+
# Uninstall all mode
|
|
212
|
+
if args.uninstall_all:
|
|
213
|
+
success = uninstall_all_skills(args.target)
|
|
214
|
+
return 0 if success else 1
|
|
215
|
+
|
|
216
|
+
# Uninstall mode
|
|
217
|
+
if args.uninstall:
|
|
218
|
+
if not args.skill:
|
|
219
|
+
print("Error: Please specify a skill name to uninstall.")
|
|
220
|
+
print("Run 'python install.py --list' to see available skills.")
|
|
221
|
+
return 1
|
|
222
|
+
success = uninstall_skill(args.skill, args.target)
|
|
223
|
+
return 0 if success else 1
|
|
224
|
+
|
|
225
|
+
# Install all mode
|
|
226
|
+
if args.all:
|
|
227
|
+
success = install_all_skills(args.target, args.overwrite)
|
|
228
|
+
return 0 if success else 1
|
|
229
|
+
|
|
230
|
+
# Install specific skill
|
|
231
|
+
if not args.skill:
|
|
232
|
+
parser.print_help()
|
|
233
|
+
return 1
|
|
234
|
+
|
|
235
|
+
success = install_skill(args.skill, args.target, args.overwrite)
|
|
236
|
+
return 0 if success else 1
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
if __name__ == "__main__":
|
|
240
|
+
sys.exit(main())
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yibeichan/claude-skills",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Claude skills for neuroimaging research workflows and scientific writing",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claude-skills": "./cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"list": "node cli.js list",
|
|
11
|
+
"add": "node scripts/add-skill.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"claude",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"skills",
|
|
17
|
+
"neuroimaging",
|
|
18
|
+
"bids",
|
|
19
|
+
"nidm",
|
|
20
|
+
"scientific-writing"
|
|
21
|
+
],
|
|
22
|
+
"author": "Yibei Chen",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/yibeichen/claude-skills.git"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/yibeichen/claude-skills/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/yibeichen/claude-skills#readme",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18.0.0"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"cli.js",
|
|
40
|
+
"install.py",
|
|
41
|
+
"skills.json",
|
|
42
|
+
"skills/**/*"
|
|
43
|
+
]
|
|
44
|
+
}
|