@robosoft/skillhub-cli 0.1.0 → 0.1.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/CHANGELOG.md +17 -0
- package/README.md +62 -5
- package/bin/skillhub.mjs +165 -15
- package/docs/skillhub-cli-logic.md +23 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
- Fixed `skillhub init --target cursor` when `skillhub.json` already exists.
|
|
6
|
+
- Existing projects now update `target`, `skillsDir`, and adapters instead of continuing to create `.ai/skills`.
|
|
7
|
+
- Added clearer init output showing the active target.
|
|
8
|
+
|
|
9
|
+
## 0.1.1
|
|
10
|
+
|
|
11
|
+
- Added configurable install targets: `ai`, `cursor`, and `claude`.
|
|
12
|
+
- Added `skillhub target` command.
|
|
13
|
+
- Added `--target` / `--to` support for `init`, `install`, and `sync`.
|
|
14
|
+
- Cursor skills now install under `.cursor/skills` and update `.cursor/rules/skillhub.mdc`.
|
|
15
|
+
- Claude skills now install under `.claude/skills` and update `CLAUDE.md` plus `.claude/skillhub.md`.
|
|
16
|
+
- Added `cloude` alias for `claude`.
|
|
17
|
+
|
|
18
|
+
# Changelog
|
|
19
|
+
|
|
3
20
|
## 0.1.0
|
|
4
21
|
|
|
5
22
|
Initial publishable release of SkillHub CLI.
|
package/README.md
CHANGED
|
@@ -72,18 +72,46 @@ node ./bin/skillhub.mjs --help
|
|
|
72
72
|
|
|
73
73
|
## Use in Any Project
|
|
74
74
|
|
|
75
|
-
Create SkillHub config inside your target project:
|
|
75
|
+
Create SkillHub config inside your target project. The CLI can now ask where to place skill files:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
78
|
skillhub init
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
Or pass the target directly:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
skillhub init --target cursor
|
|
85
|
+
skillhub init --target claude
|
|
86
|
+
skillhub init --target ai
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Targets:
|
|
90
|
+
|
|
91
|
+
```txt
|
|
92
|
+
ai -> .ai/skills + AGENTS.md
|
|
93
|
+
cursor -> .cursor/skills + .cursor/rules/skillhub.mdc
|
|
94
|
+
claude -> .claude/skills + CLAUDE.md + .claude/skillhub.md
|
|
95
|
+
```
|
|
96
|
+
|
|
81
97
|
Install a skill from a local registry:
|
|
82
98
|
|
|
83
99
|
```bash
|
|
84
100
|
skillhub install nextjs-clean-architecture --registry ./skillhub-registry
|
|
85
101
|
```
|
|
86
102
|
|
|
103
|
+
Install directly into Cursor skill location:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
skillhub install nextjs-clean-architecture --target cursor --registry ./skillhub-registry
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Install directly into Claude skill location:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
skillhub install nextjs-clean-architecture --target claude --registry ./skillhub-registry
|
|
113
|
+
```
|
|
114
|
+
|
|
87
115
|
Install a specific version:
|
|
88
116
|
|
|
89
117
|
```bash
|
|
@@ -96,6 +124,14 @@ List installed skills:
|
|
|
96
124
|
skillhub list
|
|
97
125
|
```
|
|
98
126
|
|
|
127
|
+
Change target later:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
skillhub target cursor
|
|
131
|
+
skillhub target claude
|
|
132
|
+
skillhub target ai
|
|
133
|
+
```
|
|
134
|
+
|
|
99
135
|
Sync all skills from `skillhub.json`:
|
|
100
136
|
|
|
101
137
|
```bash
|
|
@@ -149,12 +185,32 @@ npm run demo:validate
|
|
|
149
185
|
|
|
150
186
|
## Project Output
|
|
151
187
|
|
|
152
|
-
After installing skills, the target project gets files
|
|
188
|
+
After installing skills, the target project gets files based on the selected target.
|
|
189
|
+
|
|
190
|
+
For `--target cursor`:
|
|
191
|
+
|
|
192
|
+
```txt
|
|
193
|
+
.cursor/skills/<skill-name>/
|
|
194
|
+
.cursor/rules/skillhub.mdc
|
|
195
|
+
skillhub.json
|
|
196
|
+
skillhub.lock.json
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
For `--target claude`:
|
|
200
|
+
|
|
201
|
+
```txt
|
|
202
|
+
.claude/skills/<skill-name>/
|
|
203
|
+
.claude/skillhub.md
|
|
204
|
+
CLAUDE.md
|
|
205
|
+
skillhub.json
|
|
206
|
+
skillhub.lock.json
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
For `--target ai`:
|
|
153
210
|
|
|
154
211
|
```txt
|
|
155
212
|
.ai/skills/<skill-name>/
|
|
156
213
|
AGENTS.md
|
|
157
|
-
.cursor/rules/skillhub.mdc
|
|
158
214
|
skillhub.json
|
|
159
215
|
skillhub.lock.json
|
|
160
216
|
```
|
|
@@ -162,8 +218,9 @@ skillhub.lock.json
|
|
|
162
218
|
## Supported Commands
|
|
163
219
|
|
|
164
220
|
```txt
|
|
165
|
-
skillhub init
|
|
166
|
-
skillhub
|
|
221
|
+
skillhub init [--target ai|cursor|claude]
|
|
222
|
+
skillhub target [ai|cursor|claude]
|
|
223
|
+
skillhub install <skill[@version]> [--target ai|cursor|claude]
|
|
167
224
|
skillhub sync
|
|
168
225
|
skillhub list
|
|
169
226
|
skillhub remove <skill>
|
package/bin/skillhub.mjs
CHANGED
|
@@ -4,10 +4,29 @@ import fs from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import crypto from "node:crypto";
|
|
6
6
|
import process from "node:process";
|
|
7
|
+
import readline from "node:readline/promises";
|
|
7
8
|
|
|
8
9
|
const CONFIG_FILE = "skillhub.json";
|
|
9
10
|
const LOCK_FILE = "skillhub.lock.json";
|
|
10
|
-
const
|
|
11
|
+
const DEFAULT_TARGET = "ai";
|
|
12
|
+
const TARGETS = {
|
|
13
|
+
ai: {
|
|
14
|
+
label: ".ai/skills",
|
|
15
|
+
skillsDir: ".ai/skills",
|
|
16
|
+
adapters: { agentsMd: true, cursorRules: false, claude: false, githubCopilot: false },
|
|
17
|
+
},
|
|
18
|
+
cursor: {
|
|
19
|
+
label: ".cursor/skills",
|
|
20
|
+
skillsDir: ".cursor/skills",
|
|
21
|
+
adapters: { agentsMd: false, cursorRules: true, claude: false, githubCopilot: false },
|
|
22
|
+
},
|
|
23
|
+
claude: {
|
|
24
|
+
label: ".claude/skills",
|
|
25
|
+
skillsDir: ".claude/skills",
|
|
26
|
+
adapters: { agentsMd: false, cursorRules: false, claude: true, githubCopilot: false },
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
const DEFAULT_SKILLS_DIR = TARGETS[DEFAULT_TARGET].skillsDir;
|
|
11
30
|
const GENERATED_START = "<!-- skillhub:start -->";
|
|
12
31
|
const GENERATED_END = "<!-- skillhub:end -->";
|
|
13
32
|
|
|
@@ -114,6 +133,75 @@ function parseFlags(argv) {
|
|
|
114
133
|
return { positional, flags };
|
|
115
134
|
}
|
|
116
135
|
|
|
136
|
+
function normalizeTarget(value) {
|
|
137
|
+
if (!value) return null;
|
|
138
|
+
|
|
139
|
+
const normalized = String(value).trim().toLowerCase().replace(/^\./, "");
|
|
140
|
+
const aliases = {
|
|
141
|
+
ai: "ai",
|
|
142
|
+
cursor: "cursor",
|
|
143
|
+
cursorrules: "cursor",
|
|
144
|
+
".cursor": "cursor",
|
|
145
|
+
claude: "claude",
|
|
146
|
+
cloude: "claude",
|
|
147
|
+
".claude": "claude",
|
|
148
|
+
".cloude": "claude",
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const target = aliases[normalized] || normalized;
|
|
152
|
+
if (!TARGETS[target]) {
|
|
153
|
+
throw new Error(`Invalid target "${value}". Use: ai, cursor, or claude.`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return target;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function askTarget(defaultTarget = DEFAULT_TARGET) {
|
|
160
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
161
|
+
return defaultTarget;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
165
|
+
try {
|
|
166
|
+
console.log(paint("bold", "Where should SkillHub install skill files?"));
|
|
167
|
+
console.log(` 1. ${TARGETS.ai.skillsDir} ${paint("dim", "(generic AI folder + AGENTS.md)")}`);
|
|
168
|
+
console.log(` 2. ${TARGETS.cursor.skillsDir} ${paint("dim", "(Cursor rules)")}`);
|
|
169
|
+
console.log(` 3. ${TARGETS.claude.skillsDir} ${paint("dim", "(Claude project instructions)")}`);
|
|
170
|
+
|
|
171
|
+
const answer = await rl.question(`Choose target [1/2/3 or ai/cursor/claude] (${defaultTarget}): `);
|
|
172
|
+
const raw = answer.trim();
|
|
173
|
+
if (!raw) return defaultTarget;
|
|
174
|
+
if (raw === "1") return "ai";
|
|
175
|
+
if (raw === "2") return "cursor";
|
|
176
|
+
if (raw === "3") return "claude";
|
|
177
|
+
return normalizeTarget(raw);
|
|
178
|
+
} finally {
|
|
179
|
+
rl.close();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getConfigTarget(config) {
|
|
184
|
+
if (config?.target) return normalizeTarget(config.target);
|
|
185
|
+
|
|
186
|
+
const skillsDir = config?.skillsDir || DEFAULT_SKILLS_DIR;
|
|
187
|
+
if (skillsDir.startsWith(".cursor/")) return "cursor";
|
|
188
|
+
if (skillsDir.startsWith(".claude/") || skillsDir.startsWith(".cloude/")) return "claude";
|
|
189
|
+
return "ai";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function applyTargetToConfig(config, targetValue, { preserveExistingAdapters = false } = {}) {
|
|
193
|
+
const target = normalizeTarget(targetValue) || DEFAULT_TARGET;
|
|
194
|
+
const targetConfig = TARGETS[target];
|
|
195
|
+
|
|
196
|
+
config.target = target;
|
|
197
|
+
config.skillsDir = targetConfig.skillsDir;
|
|
198
|
+
config.adapters = preserveExistingAdapters
|
|
199
|
+
? { ...targetConfig.adapters, ...(config.adapters || {}) }
|
|
200
|
+
: { ...targetConfig.adapters, ...(target === "ai" ? { agentsMd: true } : {}) };
|
|
201
|
+
|
|
202
|
+
return config;
|
|
203
|
+
}
|
|
204
|
+
|
|
117
205
|
function parseSkillSpec(spec) {
|
|
118
206
|
if (!spec || spec.trim().length === 0) {
|
|
119
207
|
throw new Error("Skill name is required.");
|
|
@@ -188,20 +276,18 @@ function checksumFiles(files) {
|
|
|
188
276
|
return `sha256-${hash.digest("hex")}`;
|
|
189
277
|
}
|
|
190
278
|
|
|
191
|
-
function defaultConfig(projectRoot) {
|
|
192
|
-
|
|
279
|
+
function defaultConfig(projectRoot, target = DEFAULT_TARGET) {
|
|
280
|
+
const config = {
|
|
193
281
|
$schema: "https://skillhub.local/schema/skillhub.schema.json",
|
|
194
282
|
project: path.basename(projectRoot),
|
|
195
283
|
registry: "./skillhub-registry",
|
|
284
|
+
target: DEFAULT_TARGET,
|
|
196
285
|
skillsDir: DEFAULT_SKILLS_DIR,
|
|
197
286
|
skills: {},
|
|
198
|
-
adapters: {
|
|
199
|
-
agentsMd: true,
|
|
200
|
-
cursorRules: true,
|
|
201
|
-
claude: false,
|
|
202
|
-
githubCopilot: false,
|
|
203
|
-
},
|
|
287
|
+
adapters: {},
|
|
204
288
|
};
|
|
289
|
+
|
|
290
|
+
return applyTargetToConfig(config, target);
|
|
205
291
|
}
|
|
206
292
|
|
|
207
293
|
function defaultLock() {
|
|
@@ -218,6 +304,10 @@ async function readProjectConfig(projectRoot) {
|
|
|
218
304
|
if (!config) {
|
|
219
305
|
throw new Error(`No ${CONFIG_FILE} found. Run: skillhub init`);
|
|
220
306
|
}
|
|
307
|
+
|
|
308
|
+
config.target = getConfigTarget(config);
|
|
309
|
+
config.skillsDir = config.skillsDir || TARGETS[config.target].skillsDir;
|
|
310
|
+
config.adapters = config.adapters || TARGETS[config.target].adapters;
|
|
221
311
|
return config;
|
|
222
312
|
}
|
|
223
313
|
|
|
@@ -403,8 +493,9 @@ async function updateCursorRules(projectRoot, skillsDir, installed) {
|
|
|
403
493
|
}
|
|
404
494
|
|
|
405
495
|
async function updateClaudeMd(projectRoot, skillsDir, installed) {
|
|
406
|
-
const body = `# SkillHub Instructions\n\nFollow the installed skills below:\n\n${generatedSkillList(skillsDir, installed)}`;
|
|
496
|
+
const body = `# SkillHub Instructions\n\nFollow the installed skills below before generating or changing code:\n\n${generatedSkillList(skillsDir, installed)}`;
|
|
407
497
|
await replaceGeneratedBlock(path.join(projectRoot, "CLAUDE.md"), body, "# Claude Project Instructions\n");
|
|
498
|
+
await replaceGeneratedBlock(path.join(projectRoot, ".claude/skillhub.md"), body, "# Claude SkillHub Instructions\n");
|
|
408
499
|
}
|
|
409
500
|
|
|
410
501
|
async function updateCopilotInstructions(projectRoot, skillsDir, installed) {
|
|
@@ -415,11 +506,34 @@ async function updateCopilotInstructions(projectRoot, skillsDir, installed) {
|
|
|
415
506
|
async function commandInit(projectRoot, flags) {
|
|
416
507
|
const configPath = path.join(projectRoot, CONFIG_FILE);
|
|
417
508
|
const lockPath = path.join(projectRoot, LOCK_FILE);
|
|
509
|
+
const requestedTarget = normalizeTarget(flags.target || flags.to);
|
|
510
|
+
let config;
|
|
418
511
|
|
|
419
512
|
if (await exists(configPath)) {
|
|
513
|
+
config = await readProjectConfig(projectRoot);
|
|
420
514
|
warn(`${CONFIG_FILE} already exists.`);
|
|
515
|
+
|
|
516
|
+
if (requestedTarget) {
|
|
517
|
+
const previousTarget = getConfigTarget(config);
|
|
518
|
+
applyTargetToConfig(config, requestedTarget);
|
|
519
|
+
if (flags.registry) config.registry = flags.registry;
|
|
520
|
+
await writeJson(configPath, config);
|
|
521
|
+
if (previousTarget === requestedTarget) {
|
|
522
|
+
ok(`${CONFIG_FILE} already uses target ${requestedTarget}`);
|
|
523
|
+
} else {
|
|
524
|
+
ok(`Updated ${CONFIG_FILE} target: ${previousTarget} → ${requestedTarget}`);
|
|
525
|
+
}
|
|
526
|
+
} else if (flags.registry) {
|
|
527
|
+
config.registry = flags.registry;
|
|
528
|
+
await writeJson(configPath, config);
|
|
529
|
+
ok(`Updated ${CONFIG_FILE} registry`);
|
|
530
|
+
} else {
|
|
531
|
+
info(`Current target: ${getConfigTarget(config)}`);
|
|
532
|
+
info(`To change target: skillhub init --target cursor`);
|
|
533
|
+
}
|
|
421
534
|
} else {
|
|
422
|
-
const
|
|
535
|
+
const target = requestedTarget || (flags.yes ? DEFAULT_TARGET : await askTarget(DEFAULT_TARGET));
|
|
536
|
+
config = defaultConfig(projectRoot, target);
|
|
423
537
|
if (flags.registry) config.registry = flags.registry;
|
|
424
538
|
await writeJson(configPath, config);
|
|
425
539
|
ok(`Created ${CONFIG_FILE}`);
|
|
@@ -432,9 +546,10 @@ async function commandInit(projectRoot, flags) {
|
|
|
432
546
|
ok(`Created ${LOCK_FILE}`);
|
|
433
547
|
}
|
|
434
548
|
|
|
435
|
-
await ensureDir(path.join(projectRoot,
|
|
436
|
-
ok(`Created ${
|
|
549
|
+
await ensureDir(path.join(projectRoot, config.skillsDir));
|
|
550
|
+
ok(`Created ${config.skillsDir}`);
|
|
437
551
|
|
|
552
|
+
info(`Target: ${config.target}`);
|
|
438
553
|
info("Next: skillhub install nextjs-clean-architecture");
|
|
439
554
|
}
|
|
440
555
|
|
|
@@ -442,6 +557,9 @@ async function commandInstall(projectRoot, spec, flags = {}) {
|
|
|
442
557
|
const configPath = path.join(projectRoot, CONFIG_FILE);
|
|
443
558
|
const lockPath = path.join(projectRoot, LOCK_FILE);
|
|
444
559
|
const config = await readProjectConfig(projectRoot);
|
|
560
|
+
if (flags.target || flags.to) {
|
|
561
|
+
applyTargetToConfig(config, flags.target || flags.to);
|
|
562
|
+
}
|
|
445
563
|
const lock = (await readJson(lockPath, null)) || defaultLock();
|
|
446
564
|
const registry = resolveRegistry(projectRoot, config, flags);
|
|
447
565
|
const { name, version } = parseSkillSpec(spec);
|
|
@@ -476,7 +594,12 @@ async function commandInstall(projectRoot, spec, flags = {}) {
|
|
|
476
594
|
}
|
|
477
595
|
|
|
478
596
|
async function commandSync(projectRoot, flags = {}) {
|
|
597
|
+
const configPath = path.join(projectRoot, CONFIG_FILE);
|
|
479
598
|
const config = await readProjectConfig(projectRoot);
|
|
599
|
+
if (flags.target || flags.to) {
|
|
600
|
+
applyTargetToConfig(config, flags.target || flags.to);
|
|
601
|
+
await writeJson(configPath, config);
|
|
602
|
+
}
|
|
480
603
|
const skills = Object.entries(config.skills || {});
|
|
481
604
|
|
|
482
605
|
if (skills.length === 0) {
|
|
@@ -513,12 +636,13 @@ async function commandRemove(projectRoot, skillName) {
|
|
|
513
636
|
const config = await readProjectConfig(projectRoot);
|
|
514
637
|
const lock = (await readJson(lockPath, null)) || defaultLock();
|
|
515
638
|
const skillsDir = config.skillsDir || DEFAULT_SKILLS_DIR;
|
|
639
|
+
const installedPath = lock.skills?.[skillName]?.installTo || `${skillsDir}/${skillName}`;
|
|
516
640
|
|
|
517
641
|
delete config.skills?.[skillName];
|
|
518
642
|
delete lock.skills?.[skillName];
|
|
519
643
|
lock.updatedAt = now();
|
|
520
644
|
|
|
521
|
-
await fs.rm(path.join(projectRoot,
|
|
645
|
+
await fs.rm(path.join(projectRoot, installedPath), { recursive: true, force: true });
|
|
522
646
|
await writeJson(configPath, config);
|
|
523
647
|
await writeJson(lockPath, lock);
|
|
524
648
|
await updateAdapters(projectRoot, config, lock);
|
|
@@ -526,6 +650,26 @@ async function commandRemove(projectRoot, skillName) {
|
|
|
526
650
|
ok(`Removed ${skillName}`);
|
|
527
651
|
}
|
|
528
652
|
|
|
653
|
+
async function commandTarget(projectRoot, targetValue, flags = {}) {
|
|
654
|
+
const configPath = path.join(projectRoot, CONFIG_FILE);
|
|
655
|
+
const lockPath = path.join(projectRoot, LOCK_FILE);
|
|
656
|
+
const config = await readProjectConfig(projectRoot);
|
|
657
|
+
const target = normalizeTarget(targetValue || flags.target || flags.to) || await askTarget(getConfigTarget(config));
|
|
658
|
+
|
|
659
|
+
applyTargetToConfig(config, target);
|
|
660
|
+
await writeJson(configPath, config);
|
|
661
|
+
await ensureDir(path.join(projectRoot, config.skillsDir));
|
|
662
|
+
|
|
663
|
+
const lock = (await readJson(lockPath, null)) || defaultLock();
|
|
664
|
+
await updateAdapters(projectRoot, config, lock);
|
|
665
|
+
|
|
666
|
+
ok(`SkillHub target set to ${target}`);
|
|
667
|
+
info(`Skill files will be installed to ${config.skillsDir}`);
|
|
668
|
+
if (Object.keys(lock.skills || {}).length > 0) {
|
|
669
|
+
info("Run: skillhub sync");
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
529
673
|
async function commandCreate(projectRoot, name, flags = {}) {
|
|
530
674
|
if (!name) throw new Error("Skill name is required.");
|
|
531
675
|
|
|
@@ -684,7 +828,7 @@ async function commandGenerate(projectRoot, skillName, templateName, resourceNam
|
|
|
684
828
|
}
|
|
685
829
|
|
|
686
830
|
function printHelp() {
|
|
687
|
-
console.log(`\n${paint("bold", "SkillHub CLI")}\n\nInstall reusable AI/project skills into any codebase.\n\n${paint("bold", "Usage")}\n skillhub <command> [options]\n\n${paint("bold", "Commands")}\n init Create skillhub.json, lockfile, and
|
|
831
|
+
console.log(`\n${paint("bold", "SkillHub CLI")}\n\nInstall reusable AI/project skills into any codebase.\n\n${paint("bold", "Usage")}\n skillhub <command> [options]\n\n${paint("bold", "Commands")}\n init Create skillhub.json, lockfile, and chosen skills folder\n target [ai|cursor|claude] Change where skills are installed\n install <skill[@version]> Install a skill from the registry\n sync Reinstall all skills from skillhub.json\n list Show installed skills\n remove <skill> Remove an installed skill\n create <skill> Scaffold a new local skill package\n validate <skill|path> Validate skill.json and SKILL.md\n generate <skill> <template> <name> Generate files from a skill template\n\n${paint("bold", "Targets")}\n ai Install to .ai/skills and update AGENTS.md\n cursor Install to .cursor/skills and update .cursor/rules/skillhub.mdc\n claude Install to .claude/skills and update CLAUDE.md + .claude/skillhub.md\n\n${paint("bold", "Options")}\n --target <ai|cursor|claude> Install target. Alias: --to\n --registry <path|url> Registry location. Default: ./skillhub-registry\n --version <version> Version used by create. Default: 1.0.0\n --category <name> Category used by create. Default: general\n --yes Use default init target without prompting\n\n${paint("bold", "Examples")}\n skillhub init --target cursor\n skillhub init --target claude\n skillhub install nextjs-clean-architecture --target cursor\n skillhub target claude\n skillhub sync\n skillhub create api-security-rules --category security\n`);
|
|
688
832
|
}
|
|
689
833
|
|
|
690
834
|
async function main() {
|
|
@@ -705,6 +849,10 @@ async function main() {
|
|
|
705
849
|
case "add":
|
|
706
850
|
await commandInstall(projectRoot, positional[0], flags);
|
|
707
851
|
break;
|
|
852
|
+
case "target":
|
|
853
|
+
case "configure":
|
|
854
|
+
await commandTarget(projectRoot, positional[0], flags);
|
|
855
|
+
break;
|
|
708
856
|
case "sync":
|
|
709
857
|
await commandSync(projectRoot, flags);
|
|
710
858
|
break;
|
|
@@ -742,6 +890,8 @@ export {
|
|
|
742
890
|
sortVersions,
|
|
743
891
|
validateSkillManifest,
|
|
744
892
|
defaultConfig,
|
|
893
|
+
normalizeTarget,
|
|
894
|
+
applyTargetToConfig,
|
|
745
895
|
};
|
|
746
896
|
|
|
747
897
|
// Let people run this file through `node packages/skillhub-cli/bin/skillhub.mjs`.
|
|
@@ -143,3 +143,26 @@ The enabled adapters are controlled in `skillhub.json`:
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
```
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
## Target folders
|
|
149
|
+
|
|
150
|
+
SkillHub can install skill packages into different AI-tool folders instead of only `.ai/skills`.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
skillhub init --target cursor
|
|
154
|
+
skillhub init --target claude
|
|
155
|
+
skillhub install nextjs-clean-architecture --target cursor
|
|
156
|
+
skillhub target claude
|
|
157
|
+
skillhub sync
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Supported targets:
|
|
161
|
+
|
|
162
|
+
```txt
|
|
163
|
+
ai -> .ai/skills + AGENTS.md
|
|
164
|
+
cursor -> .cursor/skills + .cursor/rules/skillhub.mdc
|
|
165
|
+
claude -> .claude/skills + CLAUDE.md + .claude/skillhub.md
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The CLI also accepts `cloude` as an alias for `claude` to avoid failing on the common typo. Because apparently kindness can be implemented in 4 lines of code.
|