mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-05-06 08:47:18 +00:00
chore(skills): centralize AI assistant config via symlinks (#9951)
Co-authored-by: Alan Buscaglia <gentlemanprogramming@gmail.com> Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
+50
-12
@@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Setup AI Skills for Prowler development
|
||||
# Configures AI coding assistants that follow agentskills.io standard:
|
||||
# - Claude Code: .claude/skills/ symlink + CLAUDE.md copies
|
||||
# - Gemini CLI: .gemini/skills/ symlink + GEMINI.md copies
|
||||
# - Claude Code: .claude/skills/ symlink + CLAUDE.md symlink
|
||||
# - Gemini CLI: .gemini/skills/ symlink + GEMINI.md symlink
|
||||
# - Codex (OpenAI): .codex/skills/ symlink + AGENTS.md (native)
|
||||
# - GitHub Copilot: .github/copilot-instructions.md copy
|
||||
# - GitHub Copilot: .github/copilot-instructions.md symlink
|
||||
#
|
||||
# Usage:
|
||||
# ./setup.sh # Interactive mode (select AI assistants)
|
||||
@@ -37,6 +37,28 @@ SETUP_COPILOT=false
|
||||
# HELPER FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
add_to_gitignore() {
|
||||
local pattern="$1"
|
||||
local gitignore_file="$REPO_ROOT/.gitignore"
|
||||
local header="# AI Coding assistants assets"
|
||||
|
||||
# Create .gitignore if it doesn't exist
|
||||
if [ ! -f "$gitignore_file" ]; then
|
||||
touch "$gitignore_file"
|
||||
fi
|
||||
|
||||
# Check if pattern exists (exact match or at end of file)
|
||||
if ! grep -qxF "$pattern" "$gitignore_file"; then
|
||||
# Check if header exists
|
||||
if ! grep -qxF "$header" "$gitignore_file"; then
|
||||
echo -e "\n\n$header" >> "$gitignore_file"
|
||||
fi
|
||||
|
||||
echo "$pattern" >> "$gitignore_file"
|
||||
echo -e "${GREEN} ✓ Added $pattern to .gitignore${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
show_help() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
@@ -109,6 +131,7 @@ setup_claude() {
|
||||
if [ ! -d "$REPO_ROOT/.claude" ]; then
|
||||
mkdir -p "$REPO_ROOT/.claude"
|
||||
fi
|
||||
add_to_gitignore ".claude/skills"
|
||||
|
||||
if [ -L "$target" ]; then
|
||||
rm "$target"
|
||||
@@ -119,8 +142,9 @@ setup_claude() {
|
||||
ln -s "$SKILLS_SOURCE" "$target"
|
||||
echo -e "${GREEN} ✓ .claude/skills -> skills/${NC}"
|
||||
|
||||
# Copy AGENTS.md to CLAUDE.md
|
||||
copy_agents_md "CLAUDE.md"
|
||||
# Link AGENTS.md to CLAUDE.md
|
||||
link_agents_md "CLAUDE.md"
|
||||
add_to_gitignore "CLAUDE.md"
|
||||
}
|
||||
|
||||
setup_gemini() {
|
||||
@@ -129,6 +153,7 @@ setup_gemini() {
|
||||
if [ ! -d "$REPO_ROOT/.gemini" ]; then
|
||||
mkdir -p "$REPO_ROOT/.gemini"
|
||||
fi
|
||||
add_to_gitignore ".gemini/skills"
|
||||
|
||||
if [ -L "$target" ]; then
|
||||
rm "$target"
|
||||
@@ -139,8 +164,9 @@ setup_gemini() {
|
||||
ln -s "$SKILLS_SOURCE" "$target"
|
||||
echo -e "${GREEN} ✓ .gemini/skills -> skills/${NC}"
|
||||
|
||||
# Copy AGENTS.md to GEMINI.md
|
||||
copy_agents_md "GEMINI.md"
|
||||
# Link AGENTS.md to GEMINI.md
|
||||
link_agents_md "GEMINI.md"
|
||||
add_to_gitignore "GEMINI.md"
|
||||
}
|
||||
|
||||
setup_codex() {
|
||||
@@ -149,6 +175,7 @@ setup_codex() {
|
||||
if [ ! -d "$REPO_ROOT/.codex" ]; then
|
||||
mkdir -p "$REPO_ROOT/.codex"
|
||||
fi
|
||||
add_to_gitignore ".codex/skills"
|
||||
|
||||
if [ -L "$target" ]; then
|
||||
rm "$target"
|
||||
@@ -164,12 +191,19 @@ setup_codex() {
|
||||
setup_copilot() {
|
||||
if [ -f "$REPO_ROOT/AGENTS.md" ]; then
|
||||
mkdir -p "$REPO_ROOT/.github"
|
||||
cp "$REPO_ROOT/AGENTS.md" "$REPO_ROOT/.github/copilot-instructions.md"
|
||||
|
||||
# Link AGENTS.md -> .github/copilot-instructions.md
|
||||
local target="$REPO_ROOT/.github/copilot-instructions.md"
|
||||
ln -sf "../AGENTS.md" "$target"
|
||||
|
||||
echo -e "${GREEN} ✓ AGENTS.md -> .github/copilot-instructions.md${NC}"
|
||||
|
||||
# Add specifically the file, NOT the .github folder
|
||||
add_to_gitignore ".github/copilot-instructions.md"
|
||||
fi
|
||||
}
|
||||
|
||||
copy_agents_md() {
|
||||
link_agents_md() {
|
||||
local target_name="$1"
|
||||
local agents_files
|
||||
local count=0
|
||||
@@ -179,11 +213,15 @@ copy_agents_md() {
|
||||
for agents_file in $agents_files; do
|
||||
local agents_dir
|
||||
agents_dir=$(dirname "$agents_file")
|
||||
cp "$agents_file" "$agents_dir/$target_name"
|
||||
|
||||
# Create relative symlink
|
||||
# Since files are in same dir, we can just link to basename
|
||||
(cd "$agents_dir" && ln -sf "$(basename "$agents_file")" "$target_name")
|
||||
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
echo -e "${GREEN} ✓ Copied $count AGENTS.md -> $target_name${NC}"
|
||||
echo -e "${GREEN} ✓ Linked $count AGENTS.md -> $target_name${NC}"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
@@ -302,4 +340,4 @@ echo "Configured:"
|
||||
[ "$SETUP_COPILOT" = true ] && echo " • GitHub Copilot: .github/copilot-instructions.md"
|
||||
echo ""
|
||||
echo -e "${BLUE}Note: Restart your AI assistant to load the skills.${NC}"
|
||||
echo -e "${BLUE} AGENTS.md is the source of truth - edit it, then re-run this script.${NC}"
|
||||
echo -e "${BLUE} AGENTS.md is the source of truth - changes are reflected automatically via symlinks.${NC}"
|
||||
|
||||
+17
-17
@@ -201,40 +201,40 @@ test_symlink_not_created_without_flag() {
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# TESTS: AGENTS.md COPYING
|
||||
# TESTS: AGENTS.md LINKING
|
||||
# =============================================================================
|
||||
|
||||
test_copy_claude_agents_md() {
|
||||
test_link_claude_agents_md() {
|
||||
run_setup --claude > /dev/null
|
||||
assert_file_exists "$TEST_DIR/CLAUDE.md" "Root CLAUDE.md should exist" && \
|
||||
assert_file_exists "$TEST_DIR/api/CLAUDE.md" "api/CLAUDE.md should exist" && \
|
||||
assert_file_exists "$TEST_DIR/ui/CLAUDE.md" "ui/CLAUDE.md should exist"
|
||||
assert_symlink_exists "$TEST_DIR/CLAUDE.md" "Root CLAUDE.md should be a symlink" && \
|
||||
assert_symlink_exists "$TEST_DIR/api/CLAUDE.md" "api/CLAUDE.md should be a symlink" && \
|
||||
assert_symlink_exists "$TEST_DIR/ui/CLAUDE.md" "ui/CLAUDE.md should be a symlink"
|
||||
}
|
||||
|
||||
test_copy_gemini_agents_md() {
|
||||
test_link_gemini_agents_md() {
|
||||
run_setup --gemini > /dev/null
|
||||
assert_file_exists "$TEST_DIR/GEMINI.md" "Root GEMINI.md should exist" && \
|
||||
assert_file_exists "$TEST_DIR/api/GEMINI.md" "api/GEMINI.md should exist" && \
|
||||
assert_file_exists "$TEST_DIR/ui/GEMINI.md" "ui/GEMINI.md should exist"
|
||||
assert_symlink_exists "$TEST_DIR/GEMINI.md" "Root GEMINI.md should be a symlink" && \
|
||||
assert_symlink_exists "$TEST_DIR/api/GEMINI.md" "api/GEMINI.md should be a symlink" && \
|
||||
assert_symlink_exists "$TEST_DIR/ui/GEMINI.md" "ui/GEMINI.md should be a symlink"
|
||||
}
|
||||
|
||||
test_copy_copilot_to_github() {
|
||||
test_link_copilot_to_github() {
|
||||
run_setup --copilot > /dev/null
|
||||
assert_file_exists "$TEST_DIR/.github/copilot-instructions.md" "Copilot instructions should exist"
|
||||
assert_symlink_exists "$TEST_DIR/.github/copilot-instructions.md" "Copilot instructions should be a symlink"
|
||||
}
|
||||
|
||||
test_copy_codex_no_extra_files() {
|
||||
test_link_codex_no_extra_files() {
|
||||
run_setup --codex > /dev/null
|
||||
assert_file_not_exists "$TEST_DIR/CODEX.md" "CODEX.md should not be created"
|
||||
}
|
||||
|
||||
test_copy_not_created_without_flag() {
|
||||
test_link_not_created_without_flag() {
|
||||
run_setup --codex > /dev/null
|
||||
assert_file_not_exists "$TEST_DIR/CLAUDE.md" "CLAUDE.md should not exist" && \
|
||||
assert_file_not_exists "$TEST_DIR/GEMINI.md" "GEMINI.md should not exist"
|
||||
assert_symlink_not_exists "$TEST_DIR/CLAUDE.md" "CLAUDE.md should not exist" && \
|
||||
assert_symlink_not_exists "$TEST_DIR/GEMINI.md" "GEMINI.md should not exist"
|
||||
}
|
||||
|
||||
test_copy_content_matches_source() {
|
||||
test_link_content_matches_source() {
|
||||
run_setup --claude > /dev/null
|
||||
local source_content target_content
|
||||
source_content=$(cat "$TEST_DIR/AGENTS.md")
|
||||
@@ -272,7 +272,7 @@ test_idempotent_multiple_runs() {
|
||||
run_setup --claude > /dev/null
|
||||
run_setup --claude > /dev/null
|
||||
assert_symlink_exists "$TEST_DIR/.claude/skills" "Symlink should still exist after second run" && \
|
||||
assert_file_exists "$TEST_DIR/CLAUDE.md" "CLAUDE.md should still exist after second run"
|
||||
assert_symlink_exists "$TEST_DIR/CLAUDE.md" "CLAUDE.md should still be a symlink after second run"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user