From 149b302b2c41367942265593a4ac819d46e40c59 Mon Sep 17 00:00:00 2001 From: gvgvgvijayan Date: Fri, 13 Feb 2026 20:35:41 +0530 Subject: [PATCH] Add Gemini CLI support for skillpacks Introduces 'gemini' and 'gemini-global' as valid targets for building and installing skills. This allows users to package skills specifically for the Gemini CLI, supporting both project-level (`.gemini/skills/`) and user-level (`~/.gemini/skills/`) installation. - Updated `skillpack-build.mjs` to include the gemini target and layout. - Updated `skillpack-install.mjs` to handle gemini and gemini-global paths. - Added Gemini installation instructions to `README.md` and `docs/packaging.md`. - Updated evaluation scenarios to include Gemini coverage. --- README.md | 13 +++++-- docs/packaging.md | 3 +- .../skillpack-build-and-install.json | 8 ++--- shared/scripts/skillpack-build.mjs | 10 +++--- shared/scripts/skillpack-install.mjs | 36 +++++++++++++------ 5 files changed, 49 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 13273a7..c0f79f9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Teach AI coding assistants how to build WordPress the right way.** -Agent Skills are portable bundles of instructions, checklists, and scripts that help AI assistants (Claude, Copilot, Codex, Cursor, etc.) understand WordPress development patterns, avoid common mistakes, and follow best practices. +Agent Skills are portable bundles of instructions, checklists, and scripts that help AI assistants (Claude, Copilot, Codex, Cursor, Gemini, etc.) understand WordPress development patterns, avoid common mistakes, and follow best practices. > **AI Authorship Disclosure:** These skills were generated using GPT-5.2 Codex (High Reasoning) from official Gutenberg and WordPress documentation, then reviewed and edited by WordPress contributors. We tested skills with AI assistants and iterated based on results. This is v1, and skills will improve as the community uses them and contributes fixes. See [docs/ai-authorship.md](docs/ai-authorship.md) for details. ([WordPress AI Guidelines](https://make.wordpress.org/ai/handbook/ai-guidelines/)) @@ -66,7 +66,7 @@ cd agent-skills node shared/scripts/skillpack-build.mjs --clean # Install into your WordPress project -node shared/scripts/skillpack-install.mjs --dest=../your-wp-project --targets=codex,vscode,claude,cursor +node shared/scripts/skillpack-install.mjs --dest=../your-wp-project --targets=codex,vscode,claude,cursor,gemini ``` This copies skills into: @@ -74,6 +74,7 @@ This copies skills into: - `.github/skills/` for VS Code / GitHub Copilot - `.claude/skills/` for Claude Code (project-level) - `.cursor/skills/` for Cursor (project-level) +- `.gemini/skills/` for Gemini CLI (project-level) ### Install globally for Cursor @@ -83,6 +84,14 @@ node shared/scripts/skillpack-install.mjs --targets=cursor-global This installs skills to `~/.cursor/skills/` where Cursor will discover them. +### Install globally for Gemini CLI + +```bash +node shared/scripts/skillpack-install.mjs --targets=gemini-global +``` + +This installs skills to `~/.gemini/skills/` where Gemini CLI will discover them. + ### Available options ```bash diff --git a/docs/packaging.md b/docs/packaging.md index 9a629af..f8e6f31 100644 --- a/docs/packaging.md +++ b/docs/packaging.md @@ -16,13 +16,14 @@ Outputs: - `dist/vscode/.github/skills/*` (VS Code / Copilot repo layout) - `dist/claude/.claude/skills/*` (Claude Code repo layout) - `dist/cursor/.cursor/skills/*` (Cursor repo layout) +- `dist/gemini/.gemini/skills/*` (Gemini CLI repo layout) ## Install into another repo 1. Build dist (above). 2. Install into a destination repo: -- `node shared/scripts/skillpack-install.mjs --dest=../some-repo --targets=codex,vscode,claude,cursor` +- `node shared/scripts/skillpack-install.mjs --dest=../some-repo --targets=codex,vscode,claude,cursor,gemini` By default, install mode is `replace` (it replaces only the skill directories it installs). diff --git a/eval/scenarios/skillpack-build-and-install.json b/eval/scenarios/skillpack-build-and-install.json index 653e1b2..ecde958 100644 --- a/eval/scenarios/skillpack-build-and-install.json +++ b/eval/scenarios/skillpack-build-and-install.json @@ -1,11 +1,11 @@ { "name": "Build and install skillpacks", "skills": [], - "query": "Package this repo's skills for Codex, VS Code, Claude, and Cursor and install them into another repository.", + "query": "Package this repo's skills for Codex, VS Code, Claude, Cursor, and Gemini and install them into another repository.", "expected_behavior": [ - "Step 1: Run build command: node shared/scripts/skillpack-build.mjs --clean --targets=codex,vscode,claude,cursor", + "Step 1: Run build command: node shared/scripts/skillpack-build.mjs --clean --targets=codex,vscode,claude,cursor,gemini", "Step 2: Verify build output in dist/ directory", - "Step 3: Run install command: node shared/scripts/skillpack-install.mjs --from=dist --dest= --targets=codex,vscode,claude,cursor", + "Step 3: Run install command: node shared/scripts/skillpack-install.mjs --from=dist --dest= --targets=codex,vscode,claude,cursor,gemini", "Step 4: Verify skills installed in target repository", "Step 5: Confirm no symlinks in installed skills (files are copied)" ], @@ -14,6 +14,6 @@ "Output created in dist/ directory", "Install command copies to target repository", "No symlinks in installed skills", - "Codex, vscode, claude, and cursor targets supported" + "Codex, vscode, claude, cursor, and gemini targets supported" ] } diff --git a/shared/scripts/skillpack-build.mjs b/shared/scripts/skillpack-build.mjs index a5841de..1ba162d 100644 --- a/shared/scripts/skillpack-build.mjs +++ b/shared/scripts/skillpack-build.mjs @@ -5,16 +5,17 @@ function usage() { process.stderr.write( [ "Usage:", - " node shared/scripts/skillpack-build.mjs [--out=dist] [--targets=codex,vscode,claude,cursor] [--skills=skill1,skill2] [--clean]", + " node shared/scripts/skillpack-build.mjs [--out=dist] [--targets=codex,vscode,claude,cursor,gemini] [--skills=skill1,skill2] [--clean]", "", "Outputs:", " - /codex/.codex/skills//SKILL.md", " - /vscode/.github/skills//SKILL.md", " - /claude/.claude/skills//SKILL.md", " - /cursor/.cursor/skills//SKILL.md", + " - /gemini/.gemini/skills//SKILL.md", "", "Options:", - " --targets Comma-separated list of targets (codex, vscode, claude, cursor). Default: codex,vscode,claude,cursor", + " --targets Comma-separated list of targets (codex, vscode, claude, cursor, gemini). Default: codex,vscode,claude,cursor,gemini", " --skills Comma-separated list of skill names to build. Default: all skills", " --clean Remove target directories before building", "", @@ -26,7 +27,7 @@ function usage() { } function parseArgs(argv) { - const args = { out: "dist", targets: ["codex", "vscode", "claude", "cursor"], skills: [], clean: false }; + const args = { out: "dist", targets: ["codex", "vscode", "claude", "cursor", "gemini"], skills: [], clean: false }; for (const a of argv) { if (a === "--help" || a === "-h") args.help = true; else if (a === "--clean") args.clean = true; @@ -101,6 +102,7 @@ function buildTarget({ repoRoot, outDir, target, skillDirs }) { vscode: path.join(outDir, "vscode", ".github", "skills"), claude: path.join(outDir, "claude", ".claude", "skills"), cursor: path.join(outDir, "cursor", ".cursor", "skills"), + gemini: path.join(outDir, "gemini", ".gemini", "skills"), }; const destSkillsRoot = rootByTarget[target]; assert(destSkillsRoot, `Unknown target: ${target}`); @@ -117,7 +119,7 @@ function buildTarget({ repoRoot, outDir, target, skillDirs }) { process.stdout.write(`OK: built ${target} skillpack at ${rel}\n`); } -const VALID_TARGETS = ["codex", "vscode", "claude", "cursor"]; +const VALID_TARGETS = ["codex", "vscode", "claude", "cursor", "gemini"]; function main() { const args = parseArgs(process.argv.slice(2)); diff --git a/shared/scripts/skillpack-install.mjs b/shared/scripts/skillpack-install.mjs index e9411aa..8e92cc2 100644 --- a/shared/scripts/skillpack-install.mjs +++ b/shared/scripts/skillpack-install.mjs @@ -11,7 +11,7 @@ function usage() { "Options:", " --dest= Destination repo root (required, unless using --global)", " --from= Source directory (default: dist)", - " --targets= Comma-separated targets: codex, vscode, claude, claude-global, cursor, cursor-global (default: codex,vscode)", + " --targets= Comma-separated targets: codex, vscode, claude, claude-global, cursor, cursor-global, gemini, gemini-global (default: codex,vscode)", " --skills= Comma-separated skill names to install (default: all)", " --mode= 'replace' (default) or 'merge'", " --global Shorthand for --targets=claude-global (installs to ~/.claude/skills)", @@ -25,11 +25,13 @@ function usage() { " claude-global Install to ~/.claude/skills/ (user-level, ignores --dest)", " cursor Install to /.cursor/skills/", " cursor-global Install to ~/.cursor/skills/ (user-level, ignores --dest)", + " gemini Install to /.gemini/skills/", + " gemini-global Install to ~/.gemini/skills/ (user-level, ignores --dest)", "", "Examples:", " # Build and install to a WordPress project", " node shared/scripts/skillpack-build.mjs --clean", - " node shared/scripts/skillpack-install.mjs --dest=../my-wp-repo --targets=codex,vscode,claude,cursor", + " node shared/scripts/skillpack-install.mjs --dest=../my-wp-repo --targets=codex,vscode,claude,cursor,gemini", "", " # Install globally for Claude Code (all skills)", " node shared/scripts/skillpack-install.mjs --global", @@ -37,6 +39,9 @@ function usage() { " # Install globally for Cursor (all skills)", " node shared/scripts/skillpack-install.mjs --targets=cursor-global", "", + " # Install globally for Gemini CLI (all skills)", + " node shared/scripts/skillpack-install.mjs --targets=gemini-global", + "", " # Install specific skills globally", " node shared/scripts/skillpack-install.mjs --global --skills=wp-playground,wp-block-development", "", @@ -133,31 +138,41 @@ 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", "cursor", "cursor-global", "gemini", "gemini-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 + // claude-global uses the same source as claude; cursor-global uses the same as cursor; gemini-global uses the same as gemini const sourceTarget = - target === "claude-global" ? "claude" : target === "cursor-global" ? "cursor" : target; + target === "claude-global" + ? "claude" + : target === "cursor-global" + ? "cursor" + : target === "gemini-global" + ? "gemini" + : target; const targetDirMap = { codex: path.join(fromDir, "codex", ".codex", "skills"), vscode: path.join(fromDir, "vscode", ".github", "skills"), claude: path.join(fromDir, "claude", ".claude", "skills"), cursor: path.join(fromDir, "cursor", ".cursor", "skills"), + gemini: path.join(fromDir, "gemini", ".gemini", "skills"), }; return targetDirMap[sourceTarget]; } // Map target to destination directory function getDestDir(destRepoRoot, target) { - // claude-global and cursor-global don't need destRepoRoot + // claude-global, cursor-global and gemini-global don't need destRepoRoot if (target === "claude-global") { return path.join(os.homedir(), ".claude", "skills"); } if (target === "cursor-global") { return path.join(os.homedir(), ".cursor", "skills"); } + if (target === "gemini-global") { + return path.join(os.homedir(), ".gemini", "skills"); + } // Other targets require destRepoRoot const destDirMap = { @@ -165,6 +180,7 @@ function getDestDir(destRepoRoot, target) { vscode: path.join(destRepoRoot, ".github", "skills"), claude: path.join(destRepoRoot, ".claude", "skills"), cursor: path.join(destRepoRoot, ".cursor", "skills"), + gemini: path.join(destRepoRoot, ".gemini", "skills"), }; return destDirMap[target]; } @@ -213,14 +229,14 @@ 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 === "cursor-global" || target === "gemini-global"; const location = isGlobal ? destSkillsRoot : path.relative(destRepoRoot, destSkillsRoot) || "."; process.stdout.write(`OK: installed ${skillDirs.length} skill(s) to ${location}\n`); } function listAvailableSkills(fromDir) { // Check all possible target sources - const sources = ["codex", "vscode", "claude", "cursor"] + const sources = ["codex", "vscode", "claude", "cursor", "gemini"] .map((t) => getSourceDir(fromDir, t)) .filter((p) => fs.existsSync(p)); @@ -258,8 +274,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 (claude-global, cursor-global, gemini-global) + const needsDest = targets.some((t) => t !== "claude-global" && t !== "cursor-global" && t !== "gemini-global"); if (needsDest && !args.dest) { process.stderr.write("Error: --dest is required for non-global targets.\n\n"); usage();