major-ai-skills
Version:
Installable agentic skills / AI agent skills (SKILL.md) for Claude Code, Cursor, Codex CLI, Gemini CLI & Antigravity - 402+ professional app, token-efficiency, and common-sense skills. SEO/GEO ready.
211 lines (190 loc) • 7.76 kB
JavaScript
/**
* Lightweight multi-host installer inspired by agentic-awesome-skills style.
* Copies selected (or all) skills into the target agent's skills directory.
*/
const fs = require("fs");
const path = require("path");
const os = require("os");
const root = path.resolve(__dirname, "..", "..");
const skillsRoot = path.join(root, "skills");
const TARGETS = {
antigravity: path.join(os.homedir(), ".agents", "skills"),
cursor: path.join(os.homedir(), ".cursor", "skills"),
claude: path.join(os.homedir(), ".claude", "skills"),
codex: path.join(os.homedir(), ".codex", "skills"),
gemini: path.join(os.homedir(), ".gemini", "skills"),
agy: path.join(os.homedir(), ".gemini", "antigravity-cli", "skills"),
kiro: path.join(os.homedir(), ".kiro", "skills"),
opencode: path.join(process.cwd(), ".agents", "skills"),
};
function parseArgs(argv) {
const args = {
target: null,
path: null,
skills: null,
category: null,
dryRun: false,
all: false,
list: false,
help: false,
};
for (let i = 2; i < argv.length; i++) {
const a = argv[i];
if (["--search", "--limit"].includes(a) && (!argv[i + 1] || argv[i + 1].startsWith("--"))) {
throw new Error(`${a} requires a value`);
}
if (a === "--help" || a === "-h") args.help = true;
else if (a === "--dry-run") args.dryRun = true;
else if (a === "--all") args.all = true;
else if (a === "--list") args.list = true;
else if (a === "--antigravity") args.target = "antigravity";
else if (a === "--cursor") args.target = "cursor";
else if (a === "--claude") args.target = "claude";
else if (a === "--codex") args.target = "codex";
else if (a === "--gemini") args.target = "gemini";
else if (a === "--agy") args.target = "agy";
else if (a === "--kiro") args.target = "kiro";
else if (a === "--opencode") args.target = "opencode";
else if (a === "--path") args.path = argv[++i];
else if (a === "--skills") args.skills = argv[++i];
else if (a === "--category") args.category = argv[++i];
else if (a === "--search") args.search = argv[++i];
else if (a === "--json") args.json = true;
else if (a === "--limit") args.limit = Number(argv[++i]);
}
return args;
}
function loadIndex() {
const p = path.join(root, "skills_index.json");
if (!fs.existsSync(p)) {
console.error("Missing skills_index.json. Run: npm run index");
process.exit(1);
}
return JSON.parse(fs.readFileSync(p, "utf8"));
}
function resolveDest(args) {
if (args.path) return path.resolve(args.path);
if (args.target && TARGETS[args.target]) return TARGETS[args.target];
return TARGETS.antigravity;
}
function copyDir(src, dest, dryRun) {
if (dryRun) return;
fs.mkdirSync(dest, { recursive: true });
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
const from = path.join(src, entry.name);
const to = path.join(dest, entry.name);
if (entry.isDirectory()) copyDir(from, to, dryRun);
else fs.copyFileSync(from, to);
}
}
function printHelp() {
console.log(`Major AI Skills installer
Usage:
npx major-ai-skills --cursor --skills raycast,ffmpeg
npx major-ai-skills --antigravity --category macos --dry-run
npx major-ai-skills --claude --all
npx major-ai-skills --path ./my-skills --skills solidworks
npx major-ai-skills --list
Targets:
--antigravity ~/.agents/skills (default; prefer --skills to avoid overload)
--cursor ~/.cursor/skills
--claude ~/.claude/skills
--codex ~/.codex/skills
--gemini ~/.gemini/skills
--agy ~/.gemini/antigravity-cli/skills
--kiro ~/.kiro/skills
--opencode ./.agents/skills
--path <dir> custom destination
Filters:
--skills a,b,c exact skill IDs
--category x,y category filter (AND with --skills when both set)
--all install every skill (explicit consent)
--dry-run preview only
--list print catalog summary
--search "task" find relevant skills without installing anything
--json machine-readable search results
--limit 5 maximum search results (1-20)
`);
}
function main() {
const args = parseArgs(process.argv);
if (args.help) return printHelp();
const index = loadIndex();
if (args.search !== undefined) {
if (!args.search.trim()) throw new Error("Search query must not be empty");
const limit = args.limit === undefined ? 5 : args.limit;
if (!Number.isInteger(limit) || limit < 1 || limit > 20) throw new Error("--limit must be an integer from 1 to 20");
if (args.all || args.skills) throw new Error("Search cannot be combined with --all or --skills");
const results = require("./search").searchSkills(index, args.search, limit, args.category);
if (args.json) console.log(JSON.stringify({ schemaVersion: 1, query: args.search, results }, null, 2));
else console.log(results.length ? results.map(s => `${s.id}: ${s.description}\n${s.install}`).join("\n\n") : "No matching skills.");
return;
}
if (args.json || args.limit !== undefined) throw new Error("--json and --limit require --search");
if (args.list) {
const byCat = {};
for (const s of index) (byCat[s.category] ||= []).push(s.id);
for (const cat of Object.keys(byCat).sort()) {
console.log(`\n${cat} (${byCat[cat].length})`);
console.log(byCat[cat].sort().join(", "));
}
console.log(`\nTotal: ${index.length}`);
return;
}
let selected = index;
if (args.skills) {
const wanted = new Set(
args.skills
.split(",")
.map((s) => s.trim())
.filter(Boolean)
);
selected = selected.filter((s) => wanted.has(s.id));
const missing = [...wanted].filter((id) => !selected.find((s) => s.id === id));
if (missing.length) {
console.error(`Unknown skill IDs: ${missing.join(", ")}`);
process.exit(1);
}
}
if (args.category) {
const cats = new Set(
args.category
.split(",")
.map((s) => s.trim())
.filter(Boolean)
);
selected = selected.filter((s) => cats.has(s.category));
}
const destRoot = resolveDest(args);
const isAntigravityDefault =
!args.path && (!args.target || args.target === "antigravity");
if (isAntigravityDefault && !args.skills && !args.category && !args.all) {
console.error(
"Antigravity watches ~/.agents/skills. Refusing full install without --skills, --category, or --all."
);
console.error("Preview example: npx major-ai-skills --antigravity --skills raycast,ffmpeg --dry-run");
process.exit(1);
}
if (!args.skills && !args.category && !args.all) {
// Non-antigravity hosts: require explicit consent for full catalog
console.error("Provide --skills, --category, or --all.");
process.exit(1);
}
console.log(`Destination: ${destRoot}`);
console.log(`Skills to install: ${selected.length}`);
if (args.dryRun) console.log("(dry-run - no files written)");
for (const s of selected) {
const src = path.join(root, s.path);
const dest = path.join(destRoot, s.id);
console.log(`${args.dryRun ? "PLAN" : "COPY"} ${s.id} -> ${dest}`);
if (!fs.existsSync(path.join(src, "SKILL.md"))) {
console.error(`Missing SKILL.md for ${s.id}`);
process.exit(1);
}
copyDir(src, dest, args.dryRun);
}
console.log(args.dryRun ? "Dry-run complete." : "Install complete.");
console.log(`Try: @${selected[0]?.id || "skill-id"} help me with ...`);
}
try { main(); } catch (error) { console.error(error.message); process.exitCode = 1; }