Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions shared/scripts/skillpack-install.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ function usage() {
" node shared/scripts/skillpack-install.mjs --dest=<repo-root> [options]",
"",
"Options:",
" --dest=<path> Destination repo root (required, unless using --global)",
" --dest=<path> Destination repo root (required, unless using global targets)",
" --from=<path> Source directory (default: dist)",
" --targets=<list> Comma-separated targets: codex, vscode, claude, claude-global, cursor, cursor-global (default: codex,vscode)",
" --targets=<list> Comma-separated targets: codex, codex-global, vscode, claude, claude-global, cursor, cursor-global (default: codex,vscode)",
" --skills=<list> Comma-separated skill names to install (default: all)",
" --mode=<mode> 'replace' (default) or 'merge'",
" --global Shorthand for --targets=claude-global (installs to ~/.claude/skills)",
Expand All @@ -20,6 +20,7 @@ function usage() {
"",
"Targets:",
" codex Install to <dest>/.codex/skills/",
" codex-global Install to ~/.codex/skills/ (user-level, ignores --dest)",
" vscode Install to <dest>/.github/skills/",
" claude Install to <dest>/.claude/skills/ (project-level)",
" claude-global Install to ~/.claude/skills/ (user-level, ignores --dest)",
Expand All @@ -34,6 +35,9 @@ function usage() {
" # Install globally for Claude Code (all skills)",
" node shared/scripts/skillpack-install.mjs --global",
"",
" # Install globally for Codex CLI (all skills)",
" node shared/scripts/skillpack-install.mjs --targets=codex-global",
"",
" # Install globally for Cursor (all skills)",
" node shared/scripts/skillpack-install.mjs --targets=cursor-global",
"",
Expand Down Expand Up @@ -133,13 +137,16 @@ function listSkillDirs(skillsRoot) {
.filter((d) => fs.existsSync(path.join(d, "SKILL.md")));
}

const VALID_TARGETS = ["codex", "vscode", "claude", "claude-global", "cursor", "cursor-global"];
const VALID_TARGETS = ["codex", "vscode", "claude", "claude-global", "codex-global", "cursor", "cursor-global"];

// Map target to source subdirectory in dist
function getSourceDir(fromDir, target) {
// claude-global uses the same source as claude; cursor-global uses the same as cursor
// *-global targets use the same source as their non-global counterparts
const sourceTarget =
target === "claude-global" ? "claude" : target === "cursor-global" ? "cursor" : target;
target === "claude-global" ? "claude" :
target === "codex-global" ? "codex" :
target === "cursor-global" ? "cursor" :
target;
const targetDirMap = {
codex: path.join(fromDir, "codex", ".codex", "skills"),
vscode: path.join(fromDir, "vscode", ".github", "skills"),
Expand All @@ -151,10 +158,13 @@ function getSourceDir(fromDir, target) {

// Map target to destination directory
function getDestDir(destRepoRoot, target) {
// claude-global and cursor-global don't need destRepoRoot
// Global targets don't need destRepoRoot
if (target === "claude-global") {
return path.join(os.homedir(), ".claude", "skills");
}
if (target === "codex-global") {
return path.join(os.homedir(), ".codex", "skills");
}
if (target === "cursor-global") {
return path.join(os.homedir(), ".cursor", "skills");
}
Expand Down Expand Up @@ -213,7 +223,7 @@ function installTarget({ fromDir, destRepoRoot, target, skillsFilter, mode, dryR
copyDir({ srcDir: srcSkillDir, destDir: destSkillDir });
}

const isGlobal = target === "claude-global" || target === "cursor-global";
const isGlobal = target === "claude-global" || target === "codex-global" || target === "cursor-global";
const location = isGlobal ? destSkillsRoot : path.relative(destRepoRoot, destSkillsRoot) || ".";
process.stdout.write(`OK: installed ${skillDirs.length} skill(s) to ${location}\n`);
}
Expand Down Expand Up @@ -258,8 +268,8 @@ function main() {
assert(VALID_TARGETS.includes(t), `Invalid target: ${t}. Valid targets: ${VALID_TARGETS.join(", ")}`);
}

// --dest is required unless only using global targets (claude-global, cursor-global)
const needsDest = targets.some((t) => t !== "claude-global" && t !== "cursor-global");
// --dest is required unless only using global targets
const needsDest = targets.some((t) => t !== "claude-global" && t !== "codex-global" && t !== "cursor-global");
if (needsDest && !args.dest) {
process.stderr.write("Error: --dest is required for non-global targets.\n\n");
usage();
Expand Down