feat: multi-tool skill conversion (Cursor, Aider, Windsurf, etc.) (#326) (#327)

* 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:
Alireza Rezvani
2026-03-11 17:29:48 +01:00
committed by GitHub
parent 0368224219
commit 795f1846f7
4 changed files with 911 additions and 1 deletions

2
.gitignore vendored
View File

@@ -38,3 +38,5 @@ archive/
# MkDocs build output
site/
.playwright-mcp/
# Generated integration files (regenerate with ./scripts/convert.sh)
integrations/

View File

@@ -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
View 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 '![%s](https://img.shields.io/badge/Integration-%s-0A66C2)\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
View 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}"