feat: multi-tool skill conversion (Cursor, Aider, Windsurf, etc.) (#326)
* feat: add multi-tool conversion scripts (7 tools) - convert.sh: converts 156 skills to Antigravity, Cursor, Aider, Kilo Code, Windsurf, OpenCode, Augment formats - install.sh: installs converted skills to each tool's expected location - Pure bash/awk, no external deps, macOS + Linux compatible - Per-tool README with install/verify/update docs - integrations/ added to .gitignore (generated output) * docs: add multi-tool conversion to README - New section documenting Cursor, Aider, Kilo Code, Windsurf, OpenCode, Augment support - Install commands for each tool - Link to integrations/ for per-tool docs - Quick start: convert.sh + install.sh workflow --------- Co-authored-by: Leo <leo@openclaw.ai>
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -37,4 +37,6 @@ archive/
|
||||
|
||||
# MkDocs build output
|
||||
site/
|
||||
.playwright-mcp/
|
||||
.playwright-mcp/
|
||||
# Generated integration files (regenerate with ./scripts/convert.sh)
|
||||
integrations/
|
||||
|
||||
40
README.md
40
README.md
@@ -83,6 +83,46 @@ git clone https://github.com/alirezarezvani/claude-skills.git
|
||||
|
||||
---
|
||||
|
||||
## Multi-Tool Support (New)
|
||||
|
||||
**Convert all 156 skills to 7 AI coding tools** with a single script:
|
||||
|
||||
| Tool | Format | Install |
|
||||
|------|--------|---------|
|
||||
| **Cursor** | `.mdc` rules | `./scripts/install.sh --tool cursor --target .` |
|
||||
| **Aider** | `CONVENTIONS.md` | `./scripts/install.sh --tool aider --target .` |
|
||||
| **Kilo Code** | `.kilocode/rules/` | `./scripts/install.sh --tool kilocode --target .` |
|
||||
| **Windsurf** | `.windsurf/skills/` | `./scripts/install.sh --tool windsurf --target .` |
|
||||
| **OpenCode** | `.opencode/skills/` | `./scripts/install.sh --tool opencode --target .` |
|
||||
| **Augment** | `.augment/rules/` | `./scripts/install.sh --tool augment --target .` |
|
||||
| **Antigravity** | `~/.gemini/antigravity/skills/` | `./scripts/install.sh --tool antigravity` |
|
||||
|
||||
**How it works:**
|
||||
|
||||
```bash
|
||||
# 1. Convert all skills to all tools (takes ~15 seconds)
|
||||
./scripts/convert.sh --tool all
|
||||
|
||||
# 2. Install into your project (with confirmation)
|
||||
./scripts/install.sh --tool cursor --target /path/to/project
|
||||
|
||||
# Or use --force to skip confirmation:
|
||||
./scripts/install.sh --tool aider --target . --force
|
||||
|
||||
# 3. Verify
|
||||
find .cursor/rules -name "*.mdc" | wc -l # Should show 156
|
||||
```
|
||||
|
||||
**Each tool gets:**
|
||||
- ✅ All 156 skills converted to native format
|
||||
- ✅ Per-tool README with install/verify/update steps
|
||||
- ✅ Support for scripts, references, templates where applicable
|
||||
- ✅ Zero manual conversion work
|
||||
|
||||
See [integrations/](integrations/) for tool-specific documentation and pre-generated outputs.
|
||||
|
||||
---
|
||||
|
||||
## Skills Overview
|
||||
|
||||
**177 skills across 9 domains:**
|
||||
|
||||
593
scripts/convert.sh
Executable file
593
scripts/convert.sh
Executable file
@@ -0,0 +1,593 @@
|
||||
#!/usr/bin/env bash
|
||||
# Usage:
|
||||
# ./scripts/convert.sh [--tool <name>] [--out <dir>] [--help]
|
||||
#
|
||||
# Tools: antigravity, cursor, aider, kilocode, windsurf, opencode, augment, all
|
||||
# Default: all
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
TOOL="all"
|
||||
OUT_BASE="${REPO_ROOT}/integrations"
|
||||
TODAY="$(date +%F)"
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
ok() {
|
||||
echo -e "${GREEN}[OK]${NC} $*"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[!!]${NC} $*"
|
||||
}
|
||||
|
||||
err() {
|
||||
echo -e "${RED}[ERR]${NC} $*" >&2
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "${BLUE}[*]${NC} $*"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage:
|
||||
./scripts/convert.sh [--tool <name>] [--out <dir>] [--help]
|
||||
|
||||
Tools:
|
||||
antigravity, cursor, aider, kilocode, windsurf, opencode, augment, all
|
||||
|
||||
Defaults:
|
||||
--tool all
|
||||
--out <repo-root>/integrations
|
||||
USAGE
|
||||
}
|
||||
|
||||
is_valid_tool() {
|
||||
case "$1" in
|
||||
antigravity|cursor|aider|kilocode|windsurf|opencode|augment|all) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
yaml_unquote() {
|
||||
local value="$1"
|
||||
value="${value#\"}"
|
||||
value="${value%\"}"
|
||||
value="${value#\'}"
|
||||
value="${value%\'}"
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
yaml_quote() {
|
||||
local value="$1"
|
||||
value="${value//\\/\\\\}"
|
||||
value="${value//\"/\\\"}"
|
||||
printf '"%s"' "$value"
|
||||
}
|
||||
|
||||
init_count_vars() {
|
||||
converted_antigravity=0
|
||||
converted_cursor=0
|
||||
converted_aider=0
|
||||
converted_kilocode=0
|
||||
converted_windsurf=0
|
||||
converted_opencode=0
|
||||
converted_augment=0
|
||||
|
||||
skipped_antigravity=0
|
||||
skipped_cursor=0
|
||||
skipped_aider=0
|
||||
skipped_kilocode=0
|
||||
skipped_windsurf=0
|
||||
skipped_opencode=0
|
||||
skipped_augment=0
|
||||
}
|
||||
|
||||
inc_converted() {
|
||||
local t="$1"
|
||||
case "$t" in
|
||||
antigravity) converted_antigravity=$((converted_antigravity + 1)) ;;
|
||||
cursor) converted_cursor=$((converted_cursor + 1)) ;;
|
||||
aider) converted_aider=$((converted_aider + 1)) ;;
|
||||
kilocode) converted_kilocode=$((converted_kilocode + 1)) ;;
|
||||
windsurf) converted_windsurf=$((converted_windsurf + 1)) ;;
|
||||
opencode) converted_opencode=$((converted_opencode + 1)) ;;
|
||||
augment) converted_augment=$((converted_augment + 1)) ;;
|
||||
esac
|
||||
}
|
||||
|
||||
inc_skipped() {
|
||||
local t="$1"
|
||||
case "$t" in
|
||||
antigravity) skipped_antigravity=$((skipped_antigravity + 1)) ;;
|
||||
cursor) skipped_cursor=$((skipped_cursor + 1)) ;;
|
||||
aider) skipped_aider=$((skipped_aider + 1)) ;;
|
||||
kilocode) skipped_kilocode=$((skipped_kilocode + 1)) ;;
|
||||
windsurf) skipped_windsurf=$((skipped_windsurf + 1)) ;;
|
||||
opencode) skipped_opencode=$((skipped_opencode + 1)) ;;
|
||||
augment) skipped_augment=$((skipped_augment + 1)) ;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_converted() {
|
||||
local t="$1"
|
||||
case "$t" in
|
||||
antigravity) echo "$converted_antigravity" ;;
|
||||
cursor) echo "$converted_cursor" ;;
|
||||
aider) echo "$converted_aider" ;;
|
||||
kilocode) echo "$converted_kilocode" ;;
|
||||
windsurf) echo "$converted_windsurf" ;;
|
||||
opencode) echo "$converted_opencode" ;;
|
||||
augment) echo "$converted_augment" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_skipped() {
|
||||
local t="$1"
|
||||
case "$t" in
|
||||
antigravity) echo "$skipped_antigravity" ;;
|
||||
cursor) echo "$skipped_cursor" ;;
|
||||
aider) echo "$skipped_aider" ;;
|
||||
kilocode) echo "$skipped_kilocode" ;;
|
||||
windsurf) echo "$skipped_windsurf" ;;
|
||||
opencode) echo "$skipped_opencode" ;;
|
||||
augment) echo "$skipped_augment" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Prints frontmatter fields as: name<TAB>description
|
||||
extract_frontmatter() {
|
||||
local file="$1"
|
||||
awk '
|
||||
BEGIN {
|
||||
in_fm = 0
|
||||
name = ""
|
||||
desc = ""
|
||||
in_desc_block = 0
|
||||
}
|
||||
|
||||
function ltrim(s) {
|
||||
sub(/^[[:space:]]+/, "", s)
|
||||
return s
|
||||
}
|
||||
|
||||
function rtrim(s) {
|
||||
sub(/[[:space:]]+$/, "", s)
|
||||
return s
|
||||
}
|
||||
|
||||
function trim(s) {
|
||||
return rtrim(ltrim(s))
|
||||
}
|
||||
|
||||
function dequote(s, first, last) {
|
||||
s = trim(s)
|
||||
first = substr(s, 1, 1)
|
||||
last = substr(s, length(s), 1)
|
||||
if ((first == "\"" && last == "\"") || (first == "\047" && last == "\047")) {
|
||||
s = substr(s, 2, length(s) - 2)
|
||||
if (first == "\"") {
|
||||
gsub(/\\\\/, "\\", s)
|
||||
gsub(/\\"/, "\"", s)
|
||||
} else {
|
||||
gsub(/\047\047/, "\047", s)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
function append_desc(chunk) {
|
||||
chunk = trim(chunk)
|
||||
if (chunk == "") return
|
||||
if (desc == "") {
|
||||
desc = chunk
|
||||
} else {
|
||||
desc = desc " " chunk
|
||||
}
|
||||
}
|
||||
|
||||
NR == 1 {
|
||||
if ($0 == "---") {
|
||||
in_fm = 1
|
||||
next
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
in_fm == 1 {
|
||||
if ($0 == "---") {
|
||||
in_fm = 0
|
||||
next
|
||||
}
|
||||
|
||||
if (in_desc_block == 1) {
|
||||
if ($0 ~ /^[[:space:]]+/ || $0 == "") {
|
||||
line = $0
|
||||
sub(/^[[:space:]]+/, "", line)
|
||||
append_desc(line)
|
||||
next
|
||||
}
|
||||
in_desc_block = 0
|
||||
}
|
||||
|
||||
if ($0 ~ /^name:[[:space:]]*/) {
|
||||
line = $0
|
||||
sub(/^name:[[:space:]]*/, "", line)
|
||||
name = dequote(line)
|
||||
next
|
||||
}
|
||||
|
||||
if ($0 ~ /^description:[[:space:]]*/) {
|
||||
line = $0
|
||||
sub(/^description:[[:space:]]*/, "", line)
|
||||
line = trim(line)
|
||||
|
||||
if (line ~ /^>[+-]?$/ || line ~ /^\|[+-]?$/) {
|
||||
in_desc_block = 1
|
||||
next
|
||||
}
|
||||
|
||||
desc = dequote(line)
|
||||
next
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
|
||||
END {
|
||||
printf "%s\t%s\n", trim(name), trim(desc)
|
||||
}
|
||||
' "$file"
|
||||
}
|
||||
|
||||
extract_body() {
|
||||
local file="$1"
|
||||
awk '
|
||||
BEGIN { in_fm = 0 }
|
||||
|
||||
NR == 1 {
|
||||
if ($0 == "---") {
|
||||
in_fm = 1
|
||||
next
|
||||
}
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
in_fm == 1 {
|
||||
if ($0 == "---") {
|
||||
in_fm = 0
|
||||
next
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
{ print }
|
||||
' "$file"
|
||||
}
|
||||
|
||||
copy_supporting_dirs() {
|
||||
local src_dir="$1"
|
||||
local dst_dir="$2"
|
||||
local d
|
||||
for d in scripts references templates; do
|
||||
if [[ -d "${src_dir}/${d}" ]]; then
|
||||
cp -R "${src_dir}/${d}" "${dst_dir}/${d}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
append_aider_skill() {
|
||||
local name="$1"
|
||||
local description="$2"
|
||||
local body_file="$3"
|
||||
|
||||
{
|
||||
echo "---"
|
||||
echo
|
||||
echo "## ${name}"
|
||||
echo "> ${description}"
|
||||
echo
|
||||
cat "$body_file"
|
||||
echo
|
||||
} >> "${AIDER_FILE}"
|
||||
}
|
||||
|
||||
tool_title() {
|
||||
case "$1" in
|
||||
antigravity) echo "Antigravity" ;;
|
||||
cursor) echo "Cursor" ;;
|
||||
aider) echo "Aider" ;;
|
||||
kilocode) echo "Kilo Code" ;;
|
||||
windsurf) echo "Windsurf" ;;
|
||||
opencode) echo "OpenCode" ;;
|
||||
augment) echo "Augment" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
write_tool_readme() {
|
||||
local tool="$1"
|
||||
local count="$2"
|
||||
local out_dir="${OUT_BASE}/${tool}"
|
||||
|
||||
local format_line=""
|
||||
local manual_install=""
|
||||
local script_install="./scripts/install.sh --tool ${tool}"
|
||||
local verify_step=""
|
||||
local update_step=""
|
||||
|
||||
case "$tool" in
|
||||
antigravity)
|
||||
format_line='Directory skill bundles: `SKILL.md` with Antigravity frontmatter (`risk`, `source`, `date_added`) plus copied `scripts/`, `references/`, `templates/` when present.'
|
||||
manual_install='Copy each folder from `integrations/antigravity/<skill-name>/` to `~/.gemini/antigravity/skills/<skill-name>/`.'
|
||||
verify_step='Run `find ~/.gemini/antigravity/skills -name "SKILL.md" | wc -l` and confirm the count, then check your Gemini/Antigravity skill list.'
|
||||
update_step='Re-run `./scripts/convert.sh --tool antigravity` and then reinstall with `./scripts/install.sh --tool antigravity`.'
|
||||
;;
|
||||
cursor)
|
||||
format_line='Flat Cursor rules: `rules/<skill-name>.mdc` with Cursor-compatible frontmatter (`description`, `globs`, `alwaysApply`).'
|
||||
manual_install='Copy `integrations/cursor/rules/*.mdc` into your project `.cursor/rules/` directory.'
|
||||
verify_step='Open Cursor rules panel or run `find .cursor/rules -name "*.mdc" | wc -l` in your project.'
|
||||
update_step='Re-run `./scripts/convert.sh --tool cursor` and then reinstall with `./scripts/install.sh --tool cursor --target <project-dir>`.'
|
||||
;;
|
||||
aider)
|
||||
format_line='Single conventions file: `CONVENTIONS.md` concatenating all skills with separators and per-skill sections.'
|
||||
manual_install='Copy `integrations/aider/CONVENTIONS.md` into your project root.'
|
||||
verify_step='Run `aider --read CONVENTIONS.md` and confirm sections load (search for `## <skill-name>` entries).'
|
||||
update_step='Re-run `./scripts/convert.sh --tool aider` and reinstall with `./scripts/install.sh --tool aider --target <project-dir>`.'
|
||||
;;
|
||||
kilocode)
|
||||
format_line='Flat markdown rules: `rules/<skill-name>.md` with a title/description header and no frontmatter.'
|
||||
manual_install='Copy `integrations/kilocode/rules/*.md` into your project `.kilocode/rules/` directory.'
|
||||
verify_step='Run `find .kilocode/rules -name "*.md" | wc -l` in your project and confirm expected count.'
|
||||
update_step='Re-run `./scripts/convert.sh --tool kilocode` and reinstall with `./scripts/install.sh --tool kilocode --target <project-dir>`.'
|
||||
;;
|
||||
windsurf)
|
||||
format_line='Directory skill bundles: `skills/<skill-name>/SKILL.md` using Windsurf-compatible SKILL frontmatter (`name`, `description`) plus copied support folders.'
|
||||
manual_install='Copy each folder from `integrations/windsurf/skills/<skill-name>/` to `.windsurf/skills/<skill-name>/` in your project.'
|
||||
verify_step='Run `find .windsurf/skills -name "SKILL.md" | wc -l` and verify skills are listed in Windsurf.'
|
||||
update_step='Re-run `./scripts/convert.sh --tool windsurf` and reinstall with `./scripts/install.sh --tool windsurf --target <project-dir>`.'
|
||||
;;
|
||||
opencode)
|
||||
format_line='Directory skill bundles: `skills/<skill-name>/SKILL.md` with `compatibility: opencode` plus copied support folders.'
|
||||
manual_install='Copy each folder from `integrations/opencode/skills/<skill-name>/` to `.opencode/skills/<skill-name>/` in your project.'
|
||||
verify_step='Run `find .opencode/skills -name "SKILL.md" | wc -l` and confirm OpenCode shows installed skills.'
|
||||
update_step='Re-run `./scripts/convert.sh --tool opencode` and reinstall with `./scripts/install.sh --tool opencode --target <project-dir>`.'
|
||||
;;
|
||||
augment)
|
||||
format_line='Flat Augment rules: `rules/<skill-name>.md` with Augment frontmatter (`type: auto`, `description`).'
|
||||
manual_install='Copy `integrations/augment/rules/*.md` into your project `.augment/rules/` directory.'
|
||||
verify_step='Run `find .augment/rules -name "*.md" | wc -l` and check rules appear in Augment.'
|
||||
update_step='Re-run `./scripts/convert.sh --tool augment` and reinstall with `./scripts/install.sh --tool augment --target <project-dir>`.'
|
||||
;;
|
||||
esac
|
||||
|
||||
{
|
||||
printf '# %s Integration\n\n' "$(tool_title "$tool")"
|
||||
printf '\n\n' "$tool" "$tool"
|
||||
printf 'This directory contains converted Claude Skills for **%s**.\n\n' "$(tool_title "$tool")"
|
||||
printf '## Included Skills\n\n'
|
||||
printf -- '- **%s** skills generated from this repository.\n\n' "$count"
|
||||
printf '## Format\n\n'
|
||||
printf '%s\n\n' "$format_line"
|
||||
printf '## Install\n\n'
|
||||
printf '### Manual\n\n'
|
||||
printf '%s\n\n' "$manual_install"
|
||||
printf '### Script\n\n'
|
||||
printf '```bash\n'
|
||||
printf 'git clone https://github.com/alirezarezvani/claude-skills.git\n'
|
||||
printf 'cd claude-skills\n'
|
||||
printf '%s\n' "$script_install"
|
||||
printf '```\n\n'
|
||||
printf '## Verify\n\n'
|
||||
printf '%s\n\n' "$verify_step"
|
||||
printf '## Update\n\n'
|
||||
printf '%s\n\n' "$update_step"
|
||||
printf '## Source Repository\n\n'
|
||||
printf -- '- https://github.com/alirezarezvani/claude-skills\n'
|
||||
} > "${out_dir}/README.md"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--tool)
|
||||
TOOL="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--out)
|
||||
OUT_BASE="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
err "Unknown argument: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! is_valid_tool "$TOOL"; then
|
||||
err "Invalid --tool value: ${TOOL}"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TOOLS="antigravity cursor aider kilocode windsurf opencode augment"
|
||||
if [[ "$TOOL" != "all" ]]; then
|
||||
TOOLS="$TOOL"
|
||||
fi
|
||||
|
||||
SKILLS_TMP="$(mktemp)"
|
||||
(
|
||||
cd "$REPO_ROOT"
|
||||
find . -mindepth 3 -maxdepth 3 -type f -name 'SKILL.md' -not -path './.git/*' | sort
|
||||
) > "$SKILLS_TMP"
|
||||
|
||||
TOTAL_CANDIDATES="$(wc -l < "$SKILLS_TMP" | tr -d ' ')"
|
||||
if [[ "$TOTAL_CANDIDATES" -eq 0 ]]; then
|
||||
err "No skills found matching */*/SKILL.md"
|
||||
rm -f "$SKILLS_TMP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
info "Found ${TOTAL_CANDIDATES} candidate skills"
|
||||
|
||||
for t in $TOOLS; do
|
||||
rm -rf "${OUT_BASE}/${t}"
|
||||
mkdir -p "${OUT_BASE}/${t}"
|
||||
if [[ "$t" == "cursor" || "$t" == "kilocode" || "$t" == "augment" ]]; then
|
||||
mkdir -p "${OUT_BASE}/${t}/rules"
|
||||
fi
|
||||
if [[ "$t" == "windsurf" || "$t" == "opencode" ]]; then
|
||||
mkdir -p "${OUT_BASE}/${t}/skills"
|
||||
fi
|
||||
if [[ "$t" == "aider" ]]; then
|
||||
AIDER_FILE="${OUT_BASE}/aider/CONVENTIONS.md"
|
||||
{
|
||||
echo "# Claude Skills — Aider Conventions"
|
||||
echo "> Auto-generated from claude-skills. Do not edit manually."
|
||||
echo "> Generated: ${TODAY}"
|
||||
echo
|
||||
} > "$AIDER_FILE"
|
||||
fi
|
||||
ok "Prepared output directory: ${OUT_BASE}/${t}"
|
||||
done
|
||||
|
||||
init_count_vars
|
||||
|
||||
while IFS= read -r rel_path; do
|
||||
src="${REPO_ROOT}/${rel_path#./}"
|
||||
src_dir="$(dirname "$src")"
|
||||
|
||||
meta="$(extract_frontmatter "$src")"
|
||||
name="${meta%%$'\t'*}"
|
||||
description="${meta#*$'\t'}"
|
||||
|
||||
name="$(yaml_unquote "$name")"
|
||||
description="$(yaml_unquote "$description")"
|
||||
|
||||
if [[ -z "$name" || -z "$description" ]]; then
|
||||
for t in $TOOLS; do
|
||||
inc_skipped "$t"
|
||||
done
|
||||
warn "Skipping invalid frontmatter: ${rel_path}"
|
||||
continue
|
||||
fi
|
||||
|
||||
body_tmp="$(mktemp)"
|
||||
extract_body "$src" > "$body_tmp"
|
||||
|
||||
for t in $TOOLS; do
|
||||
case "$t" in
|
||||
antigravity)
|
||||
out_dir="${OUT_BASE}/antigravity/${name}"
|
||||
mkdir -p "$out_dir"
|
||||
{
|
||||
echo "---"
|
||||
echo "name: $(yaml_quote "$name")"
|
||||
echo "description: $(yaml_quote "$description")"
|
||||
echo "risk: low"
|
||||
echo "source: community"
|
||||
echo "date_added: '${TODAY}'"
|
||||
echo "---"
|
||||
cat "$body_tmp"
|
||||
} > "${out_dir}/SKILL.md"
|
||||
copy_supporting_dirs "$src_dir" "$out_dir"
|
||||
;;
|
||||
cursor)
|
||||
out_file="${OUT_BASE}/cursor/rules/${name}.mdc"
|
||||
{
|
||||
echo "---"
|
||||
echo "description: $(yaml_quote "$description")"
|
||||
echo "globs:"
|
||||
echo "alwaysApply: false"
|
||||
echo "---"
|
||||
cat "$body_tmp"
|
||||
} > "$out_file"
|
||||
;;
|
||||
aider)
|
||||
append_aider_skill "$name" "$description" "$body_tmp"
|
||||
;;
|
||||
kilocode)
|
||||
out_file="${OUT_BASE}/kilocode/rules/${name}.md"
|
||||
{
|
||||
echo "# ${name}"
|
||||
echo "> ${description}"
|
||||
echo
|
||||
cat "$body_tmp"
|
||||
} > "$out_file"
|
||||
;;
|
||||
windsurf)
|
||||
out_dir="${OUT_BASE}/windsurf/skills/${name}"
|
||||
mkdir -p "$out_dir"
|
||||
{
|
||||
echo "---"
|
||||
echo "name: $(yaml_quote "$name")"
|
||||
echo "description: $(yaml_quote "$description")"
|
||||
echo "---"
|
||||
cat "$body_tmp"
|
||||
} > "${out_dir}/SKILL.md"
|
||||
copy_supporting_dirs "$src_dir" "$out_dir"
|
||||
;;
|
||||
opencode)
|
||||
out_dir="${OUT_BASE}/opencode/skills/${name}"
|
||||
mkdir -p "$out_dir"
|
||||
{
|
||||
echo "---"
|
||||
echo "name: $(yaml_quote "$name")"
|
||||
echo "description: $(yaml_quote "$description")"
|
||||
echo "compatibility: opencode"
|
||||
echo "---"
|
||||
cat "$body_tmp"
|
||||
} > "${out_dir}/SKILL.md"
|
||||
copy_supporting_dirs "$src_dir" "$out_dir"
|
||||
;;
|
||||
augment)
|
||||
out_file="${OUT_BASE}/augment/rules/${name}.md"
|
||||
{
|
||||
echo "---"
|
||||
echo "type: auto"
|
||||
echo "description: $(yaml_quote "$description")"
|
||||
echo "---"
|
||||
cat "$body_tmp"
|
||||
} > "$out_file"
|
||||
;;
|
||||
*)
|
||||
err "Unhandled tool: ${t}"
|
||||
rm -f "$body_tmp" "$SKILLS_TMP"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
inc_converted "$t"
|
||||
done
|
||||
|
||||
rm -f "$body_tmp"
|
||||
ok "Converted ${name} (${rel_path})"
|
||||
done < "$SKILLS_TMP"
|
||||
|
||||
rm -f "$SKILLS_TMP"
|
||||
|
||||
for t in $TOOLS; do
|
||||
write_tool_readme "$t" "$(get_converted "$t")"
|
||||
done
|
||||
|
||||
echo
|
||||
info "Conversion summary"
|
||||
for t in $TOOLS; do
|
||||
echo " ${t}: $(get_converted "$t") converted, $(get_skipped "$t") skipped"
|
||||
done
|
||||
|
||||
echo
|
||||
ok "Done"
|
||||
275
scripts/install.sh
Executable file
275
scripts/install.sh
Executable file
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/env bash
|
||||
# Usage:
|
||||
# ./scripts/install.sh --tool <name> [--target <dir>] [--force] [--help]
|
||||
#
|
||||
# Installs converted skills into the appropriate location.
|
||||
# --target overrides the default install path.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
TOOL=""
|
||||
TARGET=""
|
||||
FORCE=false
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
ok() {
|
||||
echo -e "${GREEN}[OK]${NC} $*"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[!!]${NC} $*"
|
||||
}
|
||||
|
||||
err() {
|
||||
echo -e "${RED}[ERR]${NC} $*" >&2
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage:
|
||||
./scripts/install.sh --tool <name> [--target <dir>] [--force] [--help]
|
||||
|
||||
Tools:
|
||||
antigravity, cursor, aider, kilocode, windsurf, opencode, augment
|
||||
|
||||
Examples:
|
||||
./scripts/install.sh --tool cursor --target /path/to/project
|
||||
./scripts/install.sh --tool antigravity
|
||||
./scripts/install.sh --tool aider --force
|
||||
USAGE
|
||||
}
|
||||
|
||||
is_valid_tool() {
|
||||
case "$1" in
|
||||
antigravity|cursor|aider|kilocode|windsurf|opencode|augment) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
confirm_overwrite() {
|
||||
local prompt="$1"
|
||||
|
||||
if $FORCE; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "%s [y/N]: " "$prompt"
|
||||
read -r reply
|
||||
case "$reply" in
|
||||
y|Y|yes|YES) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
safe_copy_dir_contents() {
|
||||
local src_dir="$1"
|
||||
local dst_dir="$2"
|
||||
|
||||
mkdir -p "$dst_dir"
|
||||
cp -R "${src_dir}/." "$dst_dir/"
|
||||
}
|
||||
|
||||
install_antigravity() {
|
||||
local src_root="${REPO_ROOT}/integrations/antigravity"
|
||||
local dst_root
|
||||
|
||||
if [[ -n "$TARGET" ]]; then
|
||||
dst_root="$TARGET"
|
||||
else
|
||||
dst_root="${HOME}/.gemini/antigravity/skills"
|
||||
fi
|
||||
|
||||
if [[ ! -d "$src_root" ]]; then
|
||||
err "Missing source directory: $src_root"
|
||||
err "Run ./scripts/convert.sh --tool antigravity first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$dst_root"
|
||||
|
||||
local count=0
|
||||
local skill_dir
|
||||
for skill_dir in "$src_root"/*; do
|
||||
if [[ ! -d "$skill_dir" ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "$(basename "$skill_dir")" == "README.md" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
local skill_name
|
||||
skill_name="$(basename "$skill_dir")"
|
||||
local dst_skill="${dst_root}/${skill_name}"
|
||||
|
||||
if [[ -e "$dst_skill" ]]; then
|
||||
if ! confirm_overwrite "Overwrite existing ${dst_skill}?"; then
|
||||
warn "Skipped ${dst_skill}"
|
||||
continue
|
||||
fi
|
||||
rm -rf "$dst_skill"
|
||||
fi
|
||||
|
||||
cp -R "$skill_dir" "$dst_skill"
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
ok "Installed ${count} skill directories to ${dst_root}"
|
||||
}
|
||||
|
||||
install_flat_rules_tool() {
|
||||
local tool="$1"
|
||||
local subdir="$2"
|
||||
local ext="$3"
|
||||
|
||||
local src_dir="${REPO_ROOT}/integrations/${tool}/rules"
|
||||
local base_target="${TARGET:-$PWD}"
|
||||
local dst_dir="${base_target}/${subdir}"
|
||||
|
||||
if [[ ! -d "$src_dir" ]]; then
|
||||
err "Missing source directory: $src_dir"
|
||||
err "Run ./scripts/convert.sh --tool ${tool} first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -d "$dst_dir" ]] && [[ "$(find "$dst_dir" -maxdepth 1 -type f -name "*${ext}" | wc -l | tr -d ' ')" -gt 0 ]]; then
|
||||
if ! confirm_overwrite "${dst_dir} already contains ${ext} files. Overwrite contents?"; then
|
||||
warn "Install cancelled for ${tool}"
|
||||
return
|
||||
fi
|
||||
rm -rf "$dst_dir"
|
||||
fi
|
||||
|
||||
mkdir -p "$dst_dir"
|
||||
cp -R "${src_dir}/." "$dst_dir/"
|
||||
|
||||
local count
|
||||
count="$(find "$dst_dir" -maxdepth 1 -type f -name "*${ext}" | wc -l | tr -d ' ')"
|
||||
ok "Installed ${count} files to ${dst_dir}"
|
||||
}
|
||||
|
||||
install_aider() {
|
||||
local src_file="${REPO_ROOT}/integrations/aider/CONVENTIONS.md"
|
||||
local base_target="${TARGET:-$PWD}"
|
||||
local dst_file="${base_target}/CONVENTIONS.md"
|
||||
|
||||
if [[ ! -f "$src_file" ]]; then
|
||||
err "Missing source file: $src_file"
|
||||
err "Run ./scripts/convert.sh --tool aider first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$base_target"
|
||||
|
||||
if [[ -f "$dst_file" ]]; then
|
||||
if ! confirm_overwrite "Overwrite existing ${dst_file}?"; then
|
||||
warn "Skipped ${dst_file}"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
cp "$src_file" "$dst_file"
|
||||
ok "Installed ${dst_file}"
|
||||
}
|
||||
|
||||
install_skill_bundle_tool() {
|
||||
local tool="$1"
|
||||
local src_dir="${REPO_ROOT}/integrations/${tool}/skills"
|
||||
local base_target="${TARGET:-$PWD}"
|
||||
local dst_dir="${base_target}/.${tool}/skills"
|
||||
|
||||
if [[ ! -d "$src_dir" ]]; then
|
||||
err "Missing source directory: $src_dir"
|
||||
err "Run ./scripts/convert.sh --tool ${tool} first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -d "$dst_dir" ]] && [[ "$(find "$dst_dir" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')" -gt 0 ]]; then
|
||||
if ! confirm_overwrite "${dst_dir} already contains skills. Overwrite contents?"; then
|
||||
warn "Install cancelled for ${tool}"
|
||||
return
|
||||
fi
|
||||
rm -rf "$dst_dir"
|
||||
fi
|
||||
|
||||
mkdir -p "$dst_dir"
|
||||
cp -R "${src_dir}/." "$dst_dir/"
|
||||
|
||||
local count
|
||||
count="$(find "$dst_dir" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')"
|
||||
ok "Installed ${count} skill directories to ${dst_dir}"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--tool)
|
||||
TOOL="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--target)
|
||||
TARGET="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--force)
|
||||
FORCE=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
err "Unknown argument: $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$TOOL" ]]; then
|
||||
err "--tool is required"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! is_valid_tool "$TOOL"; then
|
||||
err "Invalid --tool value: ${TOOL}"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$TOOL" in
|
||||
antigravity)
|
||||
install_antigravity
|
||||
;;
|
||||
cursor)
|
||||
install_flat_rules_tool "cursor" ".cursor/rules" ".mdc"
|
||||
;;
|
||||
aider)
|
||||
install_aider
|
||||
;;
|
||||
kilocode)
|
||||
install_flat_rules_tool "kilocode" ".kilocode/rules" ".md"
|
||||
;;
|
||||
windsurf)
|
||||
install_skill_bundle_tool "windsurf"
|
||||
;;
|
||||
opencode)
|
||||
install_skill_bundle_tool "opencode"
|
||||
;;
|
||||
augment)
|
||||
install_flat_rules_tool "augment" ".augment/rules" ".md"
|
||||
;;
|
||||
*)
|
||||
err "Unhandled tool: ${TOOL}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
ok "Installation complete for ${TOOL}"
|
||||
Reference in New Issue
Block a user