Add AI skills intaller

This commit is contained in:
DariusIII
2026-08-05 10:50:00 +02:00
parent eef34bdbb0
commit 3ea36bc93c
6 changed files with 1295 additions and 0 deletions
+5
View File
@@ -70,3 +70,8 @@ composer.lock
package-lock.json package-lock.json
yarn.lock yarn.lock
pnpm-lock.yaml pnpm-lock.yaml
# Agent skills are installed globally by scripts/install-global-skills.sh
/.agents/skills/
/.claude/skills/
/.cursor/skills/
+27
View File
@@ -437,6 +437,33 @@ npm run build
npm run lint npm run lint
``` ```
### Agent Skills
The repository keeps the shared agent skill baseline in `skills-lock.json`. The
generated Linux installer installs those skills globally under `~/.agents/skills`
and bridges them into Codex under `~/.codex/skills`.
Prerequisites: Bash, Node.js/npm (`npx`), and network access to the skill
repositories.
```bash
# Regenerate the installer after changing skills-lock.json
./scripts/generate-global-skills-installer.sh
# Preview global installs and Codex bridge changes
./scripts/install-global-skills.sh --dry-run
# Install globally and create Codex symlinks
./scripts/install-global-skills.sh
# Copy skills into Codex instead of creating symlinks
./scripts/install-global-skills.sh --copy
```
Set `SKILLS_GLOBAL_DIR` or `CODEX_HOME` when using non-default global
directories. The installer never removes or overwrites unmanaged existing
skill paths; resolve reported conflicts manually and rerun it.
## Troubleshooting ## Troubleshooting
### Common Issues ### Common Issues
+222
View File
@@ -0,0 +1,222 @@
#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
LOCK_FILE="${1:-${PROJECT_ROOT}/skills-lock.json}"
OUTPUT_FILE="${2:-${SCRIPT_DIR}/install-global-skills.sh}"
die() {
printf 'Error: %s\n' "$1" >&2
exit 1
}
for command_name in awk chmod mkdir mktemp mv rm sort; do
if ! command -v "${command_name}" >/dev/null 2>&1; then
die "Required command not found: ${command_name}"
fi
done
[[ -f "${LOCK_FILE}" ]] || die "Skills lockfile not found: ${LOCK_FILE}"
mapfile -t records < <(
awk '
/^ "[^"]+": \{$/ {
skill = $1
gsub(/[":]/, "", skill)
next
}
$1 == "\"source\":" {
source = $2
gsub(/[",]/, "", source)
print source "\t" skill
skill = ""
}
' "${LOCK_FILE}" | LC_ALL=C sort -t $'\t' -k1,1 -k2,2
)
(( ${#records[@]} > 0 )) || die "No skills found in lockfile: ${LOCK_FILE}"
temporary_file="$(mktemp "${OUTPUT_FILE}.tmp.XXXXXX")"
cleanup() {
rm -f -- "${temporary_file}"
}
trap cleanup EXIT
{
printf '%s\n' '#!/usr/bin/env bash'
printf '\n'
printf '%s\n' '# This file is generated by scripts/generate-global-skills-installer.sh.'
printf '%s\n' '# Do not edit manually; regenerate it after changing skills-lock.json.'
printf '%s\n' '# shellcheck shell=bash'
printf '\n'
printf '%s\n' 'set -Eeuo pipefail'
printf '\n'
printf 'SKILL_COUNT=%q\n' "${#records[@]}"
printf 'DRY_RUN=0\n'
printf 'COPY_MODE=0\n'
printf 'SHARED_SKILLS_DIR="${SKILLS_GLOBAL_DIR:-${HOME:?HOME must be set}/.agents/skills}"\n'
printf 'CODEX_HOME_DIR="${CODEX_HOME:-${HOME:?HOME must be set}/.codex}"\n'
printf 'CODEX_SKILLS_DIR="${CODEX_HOME_DIR}/skills"\n'
printf '\n'
printf '%s\n' 'usage() {'
printf '%s\n' ' cat <<'"'"'USAGE'"'"''
printf '%s\n' 'Usage: scripts/install-global-skills.sh [options]'
printf '\n'
printf '%s\n' 'Install the repository skill baseline globally and bridge it into Codex.'
printf '\n'
printf '%s\n' 'Options:'
printf '%s\n' ' --dry-run Show commands and filesystem changes without applying them.'
printf '%s\n' ' --copy Copy skills into Codex instead of creating symlinks.'
printf '%s\n' ' --help Show this help text.'
printf '\n'
printf '%s\n' 'Environment:'
printf '%s\n' ' SKILLS_GLOBAL_DIR Shared global skills directory (default: ~/.agents/skills).'
printf '%s\n' ' CODEX_HOME Codex home directory (default: ~/.codex).'
printf '%s\n' 'USAGE'
printf '%s\n' '}'
printf '\n'
printf '%s\n' 'die() {'
printf '%s\n' ' printf '"'"'Error: %s\n'"'"' "$1" >&2'
printf '%s\n' ' exit 1'
printf '%s\n' '}'
printf '\n'
printf '%s\n' 'print_command() {'
printf '%s\n' ' printf '"'"'+'"'"''
printf '%s\n' ' printf '"'"' %q'"'"' "$@"'
printf '%s\n' ' printf '"'"'\n'"'"''
printf '%s\n' '}'
printf '\n'
printf '%s\n' 'run_command() {'
printf '%s\n' ' if (( DRY_RUN )); then'
printf '%s\n' ' print_command "$@"'
printf '%s\n' ' return 0'
printf '%s\n' ' fi'
printf '%s\n' ' "$@"'
printf '%s\n' '}'
printf '\n'
printf '%s\n' 'require_command() {'
printf '%s\n' ' command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"'
printf '%s\n' '}'
printf '\n'
printf '%s\n' 'while (( $# > 0 )); do'
printf '%s\n' ' case "$1" in'
printf '%s\n' ' --dry-run)'
printf '%s\n' ' DRY_RUN=1'
printf '%s\n' ' ;;'
printf '%s\n' ' --copy)'
printf '%s\n' ' COPY_MODE=1'
printf '%s\n' ' ;;'
printf '%s\n' ' --help|-h)'
printf '%s\n' ' usage'
printf '%s\n' ' exit 0'
printf '%s\n' ' ;;'
printf '%s\n' ' *)'
printf '%s\n' ' die "Unknown option: $1 (use --help for usage)"'
printf '%s\n' ' ;;'
printf '%s\n' ' esac'
printf '%s\n' ' shift'
printf '%s\n' 'done'
printf '\n'
printf '%s\n' 'require_command bash'
printf '%s\n' 'require_command npx'
printf '%s\n' 'require_command mkdir'
printf '%s\n' 'require_command ln'
printf '%s\n' 'require_command readlink'
printf '%s\n' 'if (( COPY_MODE )); then'
printf '%s\n' ' require_command cp'
printf '%s\n' ' require_command diff'
printf '%s\n' 'fi'
printf '\n'
printf '%s\n' 'printf '"'"'Installing %d skills globally...\n'"'"' "$SKILL_COUNT"'
printf '\n'
current_source=""
source_skill_args=()
for record in "${records[@]}"; do
source="${record%%$'\t'*}"
skill="${record#*$'\t'}"
if [[ -n "${current_source}" && "${source}" != "${current_source}" ]]; then
printf 'run_command npx --yes skills add %q --global --yes' "${current_source}"
for skill_arg in "${source_skill_args[@]}"; do
printf ' --skill %q' "${skill_arg}"
done
printf '\n\n'
source_skill_args=()
fi
current_source="${source}"
source_skill_args+=("${skill}")
done
printf 'run_command npx --yes skills add %q --global --yes' "${current_source}"
for skill_arg in "${source_skill_args[@]}"; do
printf ' --skill %q' "${skill_arg}"
done
printf '\n\n'
printf '%s\n' 'SKILLS=('
for record in "${records[@]}"; do
skill="${record#*$'\t'}"
printf ' %q\n' "${skill}"
done
printf '%s\n' ')'
printf '\n'
printf '%s\n' 'if (( DRY_RUN )); then'
printf '%s\n' ' printf '"'"'Would bridge skills from %s to %s\n'"'"' "$SHARED_SKILLS_DIR" "$CODEX_SKILLS_DIR"'
printf '%s\n' ' for skill in "${SKILLS[@]}"; do'
printf '%s\n' ' if (( COPY_MODE )); then'
printf '%s\n' ' printf '"'"'+ copy %s -> %s/%s\n'"'"' "$SHARED_SKILLS_DIR/$skill" "$CODEX_SKILLS_DIR" "$skill"'
printf '%s\n' ' else'
printf '%s\n' ' printf '"'"'+ ln -s %s %s/%s\n'"'"' "$SHARED_SKILLS_DIR/$skill" "$CODEX_SKILLS_DIR" "$skill"'
printf '%s\n' ' fi'
printf '%s\n' ' done'
printf '%s\n' ' exit 0'
printf '%s\n' 'fi'
printf '\n'
printf '%s\n' 'run_command mkdir -p "$CODEX_SKILLS_DIR"'
printf '%s\n' 'conflict_count=0'
printf '%s\n' 'for skill in "${SKILLS[@]}"; do'
printf '%s\n' ' source_path="$SHARED_SKILLS_DIR/$skill"'
printf '%s\n' ' target_path="$CODEX_SKILLS_DIR/$skill"'
printf '%s\n' ' [[ -d "$source_path" ]] || { printf '"'"'Error: Installed skill is missing: %s\n'"'"' "$source_path" >&2; conflict_count=$((conflict_count + 1)); continue; }'
printf '%s\n' ' if [[ -L "$target_path" ]]; then'
printf '%s\n' ' if [[ "$(readlink "$target_path")" == "$source_path" ]]; then'
printf '%s\n' ' printf '"'"'Already linked: %s\n'"'"' "$skill"'
printf '%s\n' ' continue'
printf '%s\n' ' fi'
printf '%s\n' ' printf '"'"'Error: Refusing to replace unmanaged symlink: %s\n'"'"' "$target_path" >&2'
printf '%s\n' ' conflict_count=$((conflict_count + 1))'
printf '%s\n' ' continue'
printf '%s\n' ' fi'
printf '%s\n' ' if [[ -e "$target_path" ]]; then'
printf '%s\n' ' if (( COPY_MODE )) && [[ -d "$target_path" ]] && diff -qr -- "$source_path" "$target_path" >/dev/null; then'
printf '%s\n' ' printf '"'"'Already copied: %s\n'"'"' "$skill"'
printf '%s\n' ' continue'
printf '%s\n' ' fi'
printf '%s\n' ' printf '"'"'Error: Refusing to overwrite existing path: %s\n'"'"' "$target_path" >&2'
printf '%s\n' ' conflict_count=$((conflict_count + 1))'
printf '%s\n' ' continue'
printf '%s\n' ' fi'
printf '%s\n' ' if (( COPY_MODE )); then'
printf '%s\n' ' run_command cp -R -- "$source_path" "$target_path"'
printf '%s\n' ' printf '"'"'Copied: %s\n'"'"' "$skill"'
printf '%s\n' ' else'
printf '%s\n' ' run_command ln -s -- "$source_path" "$target_path"'
printf '%s\n' ' printf '"'"'Linked: %s\n'"'"' "$skill"'
printf '%s\n' ' fi'
printf '%s\n' 'done'
printf '\n'
printf '%s\n' '(( conflict_count == 0 )) || die "${conflict_count} skill bridge conflict(s) require manual resolution"'
printf '%s\n' 'printf '"'"'Global skills installation complete.\n'"'"''
} >"${temporary_file}"
chmod 755 "${temporary_file}"
mkdir -p -- "$(dirname -- "${OUTPUT_FILE}")"
mv -- "${temporary_file}" "${OUTPUT_FILE}"
trap - EXIT
printf 'Generated %s from %s (%d skills).\n' "${OUTPUT_FILE}" "${LOCK_FILE}" "${#records[@]}"
+265
View File
@@ -0,0 +1,265 @@
#!/usr/bin/env bash
# This file is generated by scripts/generate-global-skills-installer.sh.
# Do not edit manually; regenerate it after changing skills-lock.json.
# shellcheck shell=bash
set -Eeuo pipefail
SKILL_COUNT=119
DRY_RUN=0
COPY_MODE=0
SHARED_SKILLS_DIR="${SKILLS_GLOBAL_DIR:-${HOME:?HOME must be set}/.agents/skills}"
CODEX_HOME_DIR="${CODEX_HOME:-${HOME:?HOME must be set}/.codex}"
CODEX_SKILLS_DIR="${CODEX_HOME_DIR}/skills"
usage() {
cat <<'USAGE'
Usage: scripts/install-global-skills.sh [options]
Install the repository skill baseline globally and bridge it into Codex.
Options:
--dry-run Show commands and filesystem changes without applying them.
--copy Copy skills into Codex instead of creating symlinks.
--help Show this help text.
Environment:
SKILLS_GLOBAL_DIR Shared global skills directory (default: ~/.agents/skills).
CODEX_HOME Codex home directory (default: ~/.codex).
USAGE
}
die() {
printf 'Error: %s\n' "$1" >&2
exit 1
}
print_command() {
printf '+'
printf ' %q' "$@"
printf '\n'
}
run_command() {
if (( DRY_RUN )); then
print_command "$@"
return 0
fi
"$@"
}
require_command() {
command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
}
while (( $# > 0 )); do
case "$1" in
--dry-run)
DRY_RUN=1
;;
--copy)
COPY_MODE=1
;;
--help|-h)
usage
exit 0
;;
*)
die "Unknown option: $1 (use --help for usage)"
;;
esac
shift
done
require_command bash
require_command npx
require_command mkdir
require_command ln
require_command readlink
if (( COPY_MODE )); then
require_command cp
require_command diff
fi
printf 'Installing %d skills globally...\n' "$SKILL_COUNT"
run_command npx --yes skills add asyrafhussin/agent-skills --global --yes --skill api-design-patterns --skill clean-code-principles --skill code-slop --skill e2e-playwright-testing --skill git-workflow --skill laravel-ai-sdk --skill laravel-best-practices --skill laravel-database-optimization --skill laravel-inertia-react --skill laravel-mcp --skill laravel-owasp-security --skill laravel-queues --skill laravel-testing --skill php-best-practices --skill prd-writing --skill project-docs --skill react-vite-best-practices --skill seo-best-practices --skill state-management --skill tailwind-best-practices --skill technical-debt --skill testing-best-practices --skill typescript-react-patterns --skill web-design-guidelines
run_command npx --yes skills add iserter/laravel-claude-agents --global --yes --skill eloquent-best-practices --skill laravel-api-resource-patterns --skill laravel-authorization-patterns --skill laravel-blade-component-patterns --skill laravel-brainstorming --skill laravel-caching-strategies --skill laravel-event-driven-architecture --skill laravel-feature-flags --skill laravel-middleware-patterns --skill laravel-notification-patterns --skill laravel-queue-patterns --skill laravel-systematic-debugging --skill laravel-task-scheduling --skill laravel-tdd --skill laravel-validation-patterns
run_command npx --yes skills add jeffallan/claude-skills --global --yes --skill angular-architect --skill api-designer --skill architecture-designer --skill atlassian-mcp --skill chaos-engineer --skill cli-developer --skill cloud-architect --skill code-documenter --skill code-reviewer --skill cpp-pro --skill csharp-developer --skill database-optimizer --skill debugging-wizard --skill devops-engineer --skill django-expert --skill dotnet-core-expert --skill embedded-systems --skill fastapi-expert --skill feature-forge --skill fine-tuning-expert --skill flutter-expert --skill fullstack-guardian --skill game-developer --skill golang-pro --skill graphql-architect --skill java-architect --skill javascript-pro --skill kotlin-specialist --skill kubernetes-specialist --skill laravel-specialist --skill legacy-modernizer --skill mcp-developer --skill microservices-architect --skill ml-pipeline --skill monitoring-expert --skill nestjs-expert --skill nextjs-developer --skill pandas-pro --skill php-pro --skill playwright-expert --skill postgres-pro --skill prompt-engineer --skill python-pro --skill rag-architect --skill rails-expert --skill react-expert --skill react-native-expert --skill rust-engineer --skill salesforce-developer --skill secure-code-guardian --skill security-reviewer --skill shopify-expert --skill spark-engineer --skill spec-miner --skill spring-boot-engineer --skill sql-pro --skill sre-engineer --skill swift-expert --skill terraform-engineer --skill test-master --skill the-fool --skill typescript-pro --skill vue-expert --skill vue-expert-js --skill websocket-engineer --skill wordpress-pro
run_command npx --yes skills add leeovery/claude-laravel --global --yes --skill laravel-quality
run_command npx --yes skills add thienanblog/awesome-ai-agent-skills --global --yes --skill agents-md-generator --skill ask-questions-if-underspecified --skill design-system-generator --skill docker-local-dev --skill documentation-guidelines --skill find-scene --skill laravel-11-12-app-guidelines --skill noizai-voice-workflow --skill office-web-ui-system --skill project-development-mindset --skill symcli-skill --skill ui-mockup-visualizer --skill vps-docker-traefik-deploy
SKILLS=(
api-design-patterns
clean-code-principles
code-slop
e2e-playwright-testing
git-workflow
laravel-ai-sdk
laravel-best-practices
laravel-database-optimization
laravel-inertia-react
laravel-mcp
laravel-owasp-security
laravel-queues
laravel-testing
php-best-practices
prd-writing
project-docs
react-vite-best-practices
seo-best-practices
state-management
tailwind-best-practices
technical-debt
testing-best-practices
typescript-react-patterns
web-design-guidelines
eloquent-best-practices
laravel-api-resource-patterns
laravel-authorization-patterns
laravel-blade-component-patterns
laravel-brainstorming
laravel-caching-strategies
laravel-event-driven-architecture
laravel-feature-flags
laravel-middleware-patterns
laravel-notification-patterns
laravel-queue-patterns
laravel-systematic-debugging
laravel-task-scheduling
laravel-tdd
laravel-validation-patterns
angular-architect
api-designer
architecture-designer
atlassian-mcp
chaos-engineer
cli-developer
cloud-architect
code-documenter
code-reviewer
cpp-pro
csharp-developer
database-optimizer
debugging-wizard
devops-engineer
django-expert
dotnet-core-expert
embedded-systems
fastapi-expert
feature-forge
fine-tuning-expert
flutter-expert
fullstack-guardian
game-developer
golang-pro
graphql-architect
java-architect
javascript-pro
kotlin-specialist
kubernetes-specialist
laravel-specialist
legacy-modernizer
mcp-developer
microservices-architect
ml-pipeline
monitoring-expert
nestjs-expert
nextjs-developer
pandas-pro
php-pro
playwright-expert
postgres-pro
prompt-engineer
python-pro
rag-architect
rails-expert
react-expert
react-native-expert
rust-engineer
salesforce-developer
secure-code-guardian
security-reviewer
shopify-expert
spark-engineer
spec-miner
spring-boot-engineer
sql-pro
sre-engineer
swift-expert
terraform-engineer
test-master
the-fool
typescript-pro
vue-expert
vue-expert-js
websocket-engineer
wordpress-pro
laravel-quality
agents-md-generator
ask-questions-if-underspecified
design-system-generator
docker-local-dev
documentation-guidelines
find-scene
laravel-11-12-app-guidelines
noizai-voice-workflow
office-web-ui-system
project-development-mindset
symcli-skill
ui-mockup-visualizer
vps-docker-traefik-deploy
)
if (( DRY_RUN )); then
printf 'Would bridge skills from %s to %s\n' "$SHARED_SKILLS_DIR" "$CODEX_SKILLS_DIR"
for skill in "${SKILLS[@]}"; do
if (( COPY_MODE )); then
printf '+ copy %s -> %s/%s\n' "$SHARED_SKILLS_DIR/$skill" "$CODEX_SKILLS_DIR" "$skill"
else
printf '+ ln -s %s %s/%s\n' "$SHARED_SKILLS_DIR/$skill" "$CODEX_SKILLS_DIR" "$skill"
fi
done
exit 0
fi
run_command mkdir -p "$CODEX_SKILLS_DIR"
conflict_count=0
for skill in "${SKILLS[@]}"; do
source_path="$SHARED_SKILLS_DIR/$skill"
target_path="$CODEX_SKILLS_DIR/$skill"
[[ -d "$source_path" ]] || { printf 'Error: Installed skill is missing: %s\n' "$source_path" >&2; conflict_count=$((conflict_count + 1)); continue; }
if [[ -L "$target_path" ]]; then
if [[ "$(readlink "$target_path")" == "$source_path" ]]; then
printf 'Already linked: %s\n' "$skill"
continue
fi
printf 'Error: Refusing to replace unmanaged symlink: %s\n' "$target_path" >&2
conflict_count=$((conflict_count + 1))
continue
fi
if [[ -e "$target_path" ]]; then
if (( COPY_MODE )) && [[ -d "$target_path" ]] && diff -qr -- "$source_path" "$target_path" >/dev/null; then
printf 'Already copied: %s\n' "$skill"
continue
fi
printf 'Error: Refusing to overwrite existing path: %s\n' "$target_path" >&2
conflict_count=$((conflict_count + 1))
continue
fi
if (( COPY_MODE )); then
run_command cp -R -- "$source_path" "$target_path"
printf 'Copied: %s\n' "$skill"
else
run_command ln -s -- "$source_path" "$target_path"
printf 'Linked: %s\n' "$skill"
fi
done
(( conflict_count == 0 )) || die "${conflict_count} skill bridge conflict(s) require manual resolution"
printf 'Global skills installation complete.\n'
+176
View File
@@ -0,0 +1,176 @@
#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
INSTALLER="${SCRIPT_DIR}/install-global-skills.sh"
GENERATOR="${SCRIPT_DIR}/generate-global-skills-installer.sh"
LOCK_FILE="${PROJECT_ROOT}/skills-lock.json"
TEMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/global-skills-test.XXXXXX")"
cleanup() {
rm -rf -- "${TEMP_ROOT}"
}
trap cleanup EXIT INT TERM
fail() {
printf 'Test failure: %s\n' "$1" >&2
exit 1
}
assert_contains() {
local needle="$1"
local haystack="$2"
[[ "${haystack}" == *"${needle}"* ]] || fail "Expected output to contain: ${needle}"
}
assert_file_exists() {
[[ -e "$1" ]] || fail "Expected file to exist: $1"
}
assert_equals() {
local expected="$1"
local actual="$2"
local description="$3"
[[ "${expected}" == "${actual}" ]] || fail "${description}: expected ${expected}, got ${actual}"
}
expected_skill_count="$(awk '
/^SKILLS=\(/ { inside = 1; next }
inside && /^\)/ { inside = 0 }
inside && /^ / { count++ }
END { print count + 0 }
' "${INSTALLER}")"
assert_equals 119 "${expected_skill_count}" "generated skill count"
generated_installer="${TEMP_ROOT}/generated-installer.sh"
"${GENERATOR}" "${LOCK_FILE}" "${generated_installer}" >/dev/null
bash -n "${generated_installer}"
generated_count="$(awk '
/^SKILLS=\(/ { inside = 1; next }
inside && /^\)/ { inside = 0 }
inside && /^ / { count++ }
END { print count + 0 }
' "${generated_installer}")"
assert_equals "${expected_skill_count}" "${generated_count}" "regenerated skill count"
assert_equals 5 "$(rg -c '^run_command npx --yes skills add' "${generated_installer}")" "source command count"
if ! diff -u \
<(awk '/^ "[^"]+": \{$/ { skill = $1; gsub(/[":]/, "", skill); print skill }' "${LOCK_FILE}" | LC_ALL=C sort) \
<(awk '
/^SKILLS=\(/ { inside = 1; next }
inside && /^\)/ { inside = 0 }
inside && /^ / { print $1 }
' "${generated_installer}" | LC_ALL=C sort); then
fail 'generated installer skill names differ from skills-lock.json'
fi
mock_bin="${TEMP_ROOT}/bin"
mkdir -p -- "${mock_bin}"
cat >"${mock_bin}/npx" <<'MOCK_NPX'
#!/usr/bin/env bash
set -Eeuo pipefail
printf '%s\n' "$*" >>"${MOCK_NPX_LOG}"
[[ "${1:-}" == "--yes" ]] || exit 2
shift
[[ "${1:-}" == "skills" ]] || exit 2
shift
[[ "${1:-}" == "add" ]] || exit 2
shift
shift
while (( $# > 0 )); do
case "$1" in
--global|--yes)
shift
;;
--skill)
skill="$2"
mkdir -p -- "${SKILLS_GLOBAL_DIR}/${skill}"
printf 'mock skill: %s\n' "${skill}" >"${SKILLS_GLOBAL_DIR}/${skill}/SKILL.md"
shift 2
;;
*)
shift
;;
esac
done
MOCK_NPX
chmod 755 "${mock_bin}/npx"
run_installer() {
local home_dir="$1"
local shared_dir="$2"
local codex_home="$3"
shift 3
HOME="${home_dir}" \
SKILLS_GLOBAL_DIR="${shared_dir}" \
CODEX_HOME="${codex_home}" \
MOCK_NPX_LOG="${TEMP_ROOT}/npx.log" \
PATH="${mock_bin}:${PATH}" \
"${INSTALLER}" "$@"
}
home_dir="${TEMP_ROOT}/home"
shared_dir="${TEMP_ROOT}/shared"
codex_home="${TEMP_ROOT}/codex"
mkdir -p -- "${home_dir}" "${codex_home}/skills/.system"
printf 'preserve me\n' >"${codex_home}/skills/.system/SKILL.md"
: >"${TEMP_ROOT}/npx.log"
run_installer "${home_dir}" "${shared_dir}" "${codex_home}" >"${TEMP_ROOT}/install.log" 2>&1 \
|| { cat "${TEMP_ROOT}/install.log" >&2; fail 'initial installation failed'; }
assert_equals "${expected_skill_count}" "$(find "${codex_home}/skills" -mindepth 1 -maxdepth 1 -type l | wc -l | tr -d ' ')" "symlink count"
assert_file_exists "${codex_home}/skills/.system/SKILL.md"
system_contents="$(<"${codex_home}/skills/.system/SKILL.md")"
assert_contains 'preserve me' "${system_contents}"
npx_call_count="$(wc -l < "${TEMP_ROOT}/npx.log" | tr -d ' ')"
assert_equals 5 "${npx_call_count}" "global source install count"
npx_log_contents="$(<"${TEMP_ROOT}/npx.log")"
assert_contains '--global' "${npx_log_contents}"
run_installer "${home_dir}" "${shared_dir}" "${codex_home}" >"${TEMP_ROOT}/rerun.log" 2>&1 \
|| { cat "${TEMP_ROOT}/rerun.log" >&2; fail 'idempotent symlink rerun failed'; }
assert_equals "${expected_skill_count}" "$(rg -c '^Already linked:' "${TEMP_ROOT}/rerun.log")" "idempotent link count"
dry_home="${TEMP_ROOT}/dry-home"
dry_shared="${TEMP_ROOT}/dry-shared"
dry_codex="${TEMP_ROOT}/dry-codex"
dry_output="$(run_installer "${dry_home}" "${dry_shared}" "${dry_codex}" --dry-run 2>&1)"
assert_contains 'Would bridge skills' "${dry_output}"
[[ ! -e "${dry_shared}" ]] || fail 'dry run created the shared skill directory'
[[ ! -e "${dry_codex}" ]] || fail 'dry run created the Codex directory'
copy_home="${TEMP_ROOT}/copy-home"
copy_shared="${TEMP_ROOT}/copy-shared"
copy_codex="${TEMP_ROOT}/copy-codex"
mkdir -p -- "${copy_home}" "${copy_codex}/skills/.system"
run_installer "${copy_home}" "${copy_shared}" "${copy_codex}" --copy >"${TEMP_ROOT}/copy.log" 2>&1 \
|| { cat "${TEMP_ROOT}/copy.log" >&2; fail 'copy installation failed'; }
assert_equals "${expected_skill_count}" "$(find "${copy_codex}/skills" -mindepth 1 -maxdepth 1 -type d ! -name .system | wc -l | tr -d ' ')" "copy count"
run_installer "${copy_home}" "${copy_shared}" "${copy_codex}" --copy >"${TEMP_ROOT}/copy-rerun.log" 2>&1 \
|| { cat "${TEMP_ROOT}/copy-rerun.log" >&2; fail 'idempotent copy rerun failed'; }
assert_equals "${expected_skill_count}" "$(rg -c '^Already copied:' "${TEMP_ROOT}/copy-rerun.log")" "idempotent copy count"
conflict_home="${TEMP_ROOT}/conflict-home"
conflict_shared="${TEMP_ROOT}/conflict-shared"
conflict_codex="${TEMP_ROOT}/conflict-codex"
first_skill="$(awk '
/^SKILLS=\(/ { inside = 1; next }
inside && /^ / { print $1; exit }
' "${INSTALLER}")"
mkdir -p -- "${conflict_home}" "${conflict_codex}/skills/${first_skill}"
printf 'unmanaged\n' >"${conflict_codex}/skills/${first_skill}/marker.txt"
if run_installer "${conflict_home}" "${conflict_shared}" "${conflict_codex}" >"${TEMP_ROOT}/conflict.log" 2>&1; then
cat "${TEMP_ROOT}/conflict.log" >&2
fail 'unmanaged conflict was not rejected'
fi
conflict_log_contents="$(<"${TEMP_ROOT}/conflict.log")"
assert_contains 'Refusing to overwrite existing path' "${conflict_log_contents}"
printf 'Global skills installer tests passed (%d skills).\n' "${expected_skill_count}"
+600
View File
@@ -0,0 +1,600 @@
{
"version": 1,
"skills": {
"agents-md-generator": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "b5b5d0dde3127aa10f6d7db1208d49919fdcbaf1374062b30ddc68dabb83b001"
},
"angular-architect": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "d93b695494c81c076a7c837007762f6e04b204ee19eccd7ff77969ff76835dfd"
},
"api-design-patterns": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "3f15c74774287c26486439158e2f7454907b6751fbd2e7842f034555e47df796"
},
"api-designer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "06e7906fe1ca4d1db195ca244ff7cc05e87e2c8b634662abeedd501c4657e88e"
},
"architecture-designer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "ecb729710037e01080fd2c342f37d2c25b3597bec70b8d529f9604abd8ccc142"
},
"ask-questions-if-underspecified": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "79458ea62d3f88fccd2c9936ebfcb025ce9a43d3302417bbee9d0e2edec30109"
},
"atlassian-mcp": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "4513b7e0d937979a80b774ef9220b0074c5c4230ea5cbb39cca4f1c402529491"
},
"chaos-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "bc0cdbf36e1860a66d4f8d967c78ee31b1daed917c1ab9d7a015237ff071e718"
},
"clean-code-principles": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "4c22dca6110d58f1454523dc3f192ef20d91f538233da9132b407edd041a4762"
},
"cli-developer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "8ac5b5dc1cc784f255f4ebde0c65d0185da4a7d6b02d8d38838be178b62cfd7a"
},
"cloud-architect": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "9082e59c3dc8f05378bc52dc043724e22da525eea38916becb8778e2d320ac92"
},
"code-documenter": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "49709db09641a9ab4196459835bfcbe565eb0581c23cb4eeb50ff80d2af88272"
},
"code-reviewer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "57ab3eba6fa596bc52d73ca7312afc2f389d9622714d25b2ab3d4bbf03dc293a"
},
"code-slop": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "a4362f272da2f81751108d0bbfb8b5128e5067329619645d24b100c95dc3edd9"
},
"cpp-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "a5632d28a479fe433c7fd9ab98903b9cd010926b3ddd73785ff0325d0277dbe6"
},
"csharp-developer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "90bd8e86b9c139379d225f4692b4e9deb588db680055439dc3b463833ef6adfb"
},
"database-optimizer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "2feb5b0765151417dab90bf014df41514386e4bdb25aad9ab38498c0bd18a1d8"
},
"debugging-wizard": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "9f39c248ae9588423cd12ea5197adf1530d7308eaf51a6d018ad08d52658a2ed"
},
"design-system-generator": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "402854c08c678a14b302a66bf48e43ea803bca8247fb1f6735fe9cabfe662535"
},
"devops-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "6de5e562151fe9c227234e81bdc1e647066d6b1822463eadf296a5344b47e35a"
},
"django-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "f64e47ac397e308dde77144592cfc2fc2b60815c7ee653ef8bdc8cf860de3c27"
},
"docker-local-dev": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "2d81a4cea16c22c5404fef6f932aa8b22e2fe5cddc0b7ba4650f26b812afa585"
},
"documentation-guidelines": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "af9ad9dc6e7e18ab6896fad8bf23e19037671d2d06461452d7a6052119237334"
},
"dotnet-core-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "ceb0245bce4c20ad0760d7f589311e3ac2cdf110636a1581fa6d3018c7b27890"
},
"e2e-playwright-testing": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "ee097dfeaa4d731a35ab47bd930859f17d7d0d073d0df3720435b02d97d12210"
},
"eloquent-best-practices": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "370e1502efc9d179f5df7470a45a10302de81de9cf378641403e4ade9eb7d67f"
},
"embedded-systems": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "1631345781a5ed3ba2a780a710b8c23cb1b0abaaf576164c40d15b20683d2f6e"
},
"fastapi-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "eb70cfcb3a06badfbdf51262ecb31b868a983963b9d6654e02856752828b312f"
},
"feature-forge": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "a6ed33cfbfc0259c39f42ad69cc55ace984da805c4f4197df437770cf7c1b671"
},
"find-scene": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "2a5ebaf950698a1751270337b9eb80dd37c3ebd459be3920d787506cc2455f84"
},
"fine-tuning-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "8e70137cf3de9a6edba12d3f523c087c0407002f1206b0981b767323e0f5a54e"
},
"flutter-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "c7849f39cf34d91999f573b8ad39315dd68c7717bcd38c42b5e49cd83785d9c1"
},
"fullstack-guardian": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "2d3533f91951f2c8626eaeb7ffb33e40727bca85545a8e63f97e35939957e1a4"
},
"game-developer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "5310da9a7910ae7e9d119abb7377b6213b1dc7767e4aacd818b8fdb60abab36f"
},
"git-workflow": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "e6a28779208771a57940a14e1f9e463ab65c1f8e660fcc52d05b9ec05de3fab1"
},
"golang-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "12b4cac8f65228f82cbc26f0da9232aba87ffa45ccd01afa3998fb8ec61dc288"
},
"graphql-architect": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "78c6dd863af986f7f409e26ba25dc14f76985f892a0340383dc56a7b8439ec43"
},
"java-architect": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "02a7bcc716c9338aedcb0727be38a86d6addbadb1c17b485d019f654d2b04af6"
},
"javascript-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "d5d4526309a674a2ddfd99aa9fb31fe061af225a7b3750eed52a2d38c8ce6041"
},
"kotlin-specialist": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "7119693c0ce1ebf7de447ccafef33cd319b9a8bfc057b6dfcb137caac2fea274"
},
"kubernetes-specialist": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "9c5eb68fc9d553ee29e745258bc6198563158c4b2c57436d63647e61010b5f0a"
},
"laravel-11-12-app-guidelines": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "9505833bc35846abbfbb5ab93e1c45a7da179e7cccc3122223a4881afefab7cd"
},
"laravel-ai-sdk": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "6e7fb4360bcbe6eea2a634d7b96a46b41887f60eca806f54b7400f3efa508ce0"
},
"laravel-api-resource-patterns": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "b140eaa32c967c9bbde994a47dc56491132efd97f06a534920e13dbbc36db887"
},
"laravel-authorization-patterns": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "57418ddc3eea7b850c96dbcd431070e75b2abff5a4bd010f759133e225c5e216"
},
"laravel-best-practices": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "6eb357774a2416b3e632cf4f7dec1ff9558af2d70c4b8ade802f5a65ce118fb5"
},
"laravel-blade-component-patterns": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "4fe35eb514db82613a195b4c3d790618adb88e70e68a84ff9c50d086fa288759"
},
"laravel-brainstorming": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "ff3aa9135e3e2573bb7edb664b611baefeef893c9425f52ccda87a56618e9089"
},
"laravel-caching-strategies": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "7b5d23808f7355be3de5328f8c0f5ca80ab194b252aeed697c3ee505fc90b270"
},
"laravel-database-optimization": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "564cbba91a281420e077178d7b367ef1c367576ff6f050f9ee6eaa99d30d2a1c"
},
"laravel-event-driven-architecture": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "5b43a13e97cc0b9dcd5df9bf819a15b254f1dd016ccadb94bb5e6d3c77710de9"
},
"laravel-feature-flags": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "46214cd3e065d109cfcba59dce63cb75563b97efc200deac8b6c1333bda48f75"
},
"laravel-inertia-react": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "b05144f7f9df5fd3cd03ce13cba822ac61970449fdf7c4c4208684258e8b1724"
},
"laravel-mcp": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "31de735b826f5c8e7b64ca133b10f2fc6b4a1789bb6b144aecccc948fe8ef689"
},
"laravel-middleware-patterns": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "44414ad9a3dbe8753d0861673b8fdf10d9bec931564c0e552c9accecdefbedf3"
},
"laravel-notification-patterns": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "658eaf3f166438e7d5e5403c3bd0b9e2670a2858a45d70da68289fa18c01ce60"
},
"laravel-owasp-security": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "35f74b7fb37c9b0cb1a274e7e25f7425e793dacd73620c3507c7e9462e4c9c9d"
},
"laravel-quality": {
"source": "leeovery/claude-laravel",
"sourceType": "github",
"computedHash": "bffa603f3f1128d2d3b1c2dcbbbffdec1bd239d4a799c74419deb627fa64a518"
},
"laravel-queue-patterns": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "4bfe5b51ed89506df59a32de0f4be4ab2cc106356b98149953441a109bd2f951"
},
"laravel-queues": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "75391b4d5635e0db3ef8a02bd512a2974b98bae97375fdfd9a18fe7ad936dbf8"
},
"laravel-specialist": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "d58e2ed3488398cb94a039f25b9d270e55dc434e16102abff9035608bec3db90"
},
"laravel-systematic-debugging": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "82db3f7d4e3cfc3b3d1605a96c06612f93121dd3c879208d9dba23f30495518d"
},
"laravel-task-scheduling": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "833b36a2ae73833137431c6b7b5d9d393adfe93b3ab4d135e87acead31a7cc20"
},
"laravel-tdd": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "f895a09949a45fb610d8f033c168fc7ed70ef33323c38ef176add410ddd58e8e"
},
"laravel-testing": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "9627c977341aa7a9a11e5f500838409c1e39e1ca0714c6a6038fba4d3a5203c9"
},
"laravel-validation-patterns": {
"source": "iserter/laravel-claude-agents",
"sourceType": "github",
"computedHash": "66fb92309a38bba54c8730a0efccc0f3092cf7dbfb71b294ff33c1becae91285"
},
"legacy-modernizer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "703bc6b068b60f1ca32c2892f3aa3875c986fa17b65676f612810eb52c21b5eb"
},
"mcp-developer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "e94458c5a3ca95938decbcf071e1fa3d1bb44a8b878c5a7cab7b48b7612dc5db"
},
"microservices-architect": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "674b7d4c18eb009295213cbb49ccdc112db1d2f60bb65848ceb8771bdc69a25f"
},
"ml-pipeline": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "5e80b6260cabb7e8705b04627f1ec1a70341ecf2e77cbc5df1b6fcb5242e18e5"
},
"monitoring-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "928f8e068a5c3c85e4a8d515241f21e5cf6a38b39c597cf54b681197fa107853"
},
"nestjs-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "0775f4ba14e4f372b894cc86feb0bf58a51b2038bc98b3ff18f2021565cea494"
},
"nextjs-developer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "e53bdeaf766e6d5d29d1328f424777a8236f80f489134f1661bbda6c8c4149e9"
},
"noizai-voice-workflow": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "1bf4fba1bd0c3bd4c453a9aec4f981409bc64b17775361db55c2cba7863cb881"
},
"office-web-ui-system": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "574a097be28bab7f80d5bd998da1323501eb0adf755671c4020def825e5313d1"
},
"pandas-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "386d7588ba685df487079293be0f6d026467db579df9337c798aeb20e2d82a59"
},
"php-best-practices": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "1b25a60572ddd8b206eaf68b067cde70a9d002df572de8fefda703532cecfdad"
},
"php-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "c163f6d5d26ddbf4a4d79e8dc95d3b41e447f1659486cf0aae8c070d950a9d86"
},
"playwright-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "2715b8412ae840555d77d3d2c566e6f75ca42900637363d7574356af409ae10c"
},
"postgres-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "b5385f29408d4bd80666e608dbd7da3d4bed9153afa53b71bda94c6bf7ba8d51"
},
"prd-writing": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "4c3141b2fdc77aac6a528bd9cee5e07d8122d75a89295f4020ceab112c1d44a9"
},
"project-development-mindset": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "b8bf39985a51545efb84b396bf9c262bdd99f7b412be6d70a6f2ce28933324e3"
},
"project-docs": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "d2da7474d61edf37883e91b31dec49efbcb5bc6979e8d132dceaa26c3fad1b61"
},
"prompt-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "c2574f1d0c4120117727dbfe79da8bf087b50635df30920ae1d2bc9b256ce7c6"
},
"python-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "fae3b07d03d2eb9f9112ceab0ef498ebafab33f1e095dff8c5a36de55ef68445"
},
"rag-architect": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "650ce80b998ce2c3004c5276a1eabaa2ad4131d4256832eed9a5e06cdc98454d"
},
"rails-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "e67a8886ff2a11da8133188a0bc0b48b9df213c6fc4bac4cbdd1ddacabb628c1"
},
"react-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "498e8247c701522e0a63496a439680a409dafe67c3d8d7d5e4902db0e6222252"
},
"react-native-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "f617f83be990728fef1ea2192c9b8ae85ef2c362e97f18782438f8bab357b02a"
},
"react-vite-best-practices": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "82994934665a490b227fabdfec3d148b1fa291d77d82ccf978e761b9e1ff6e4c"
},
"rust-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "f8093c6253afe24bbac3c3328c3ca07e9ef089758896ba10221ed0f2078cb2a0"
},
"salesforce-developer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "a4b5ae7acb98027b4aeb6a3eb31f301af20e30c6e0701b1d237b5714f159a479"
},
"secure-code-guardian": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "36e0b6460df5ea86c0a6aeeb1d33ad6dfa8f8a404ba1dc47feb3cf93341cb2c6"
},
"security-reviewer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "7a1ed4f4ca78a50b97e3e8c5725e494dc8aae0895bd1efde681a75ab22ff89d3"
},
"seo-best-practices": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "4bc88f564290e835fd5f486bdacce1c3a867afaafa118363c9e80f6e6466a8d7"
},
"shopify-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "b44431e08c55a0439ee41ed8893e6f6698f5e75e216b0a8f6c756cb5d461c016"
},
"spark-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "4c66f4104d2780f8075ca8e38084d009dfe7d2f8c594424f570611e50d4943b6"
},
"spec-miner": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "91822456789ffa6f5641d4f586df7533757fbc02629b76cb6e68cce5e369b13a"
},
"spring-boot-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "ed065f5db45afe9dc2739cff713927d0b6b6ad3e667bdf7b40340d1a5543bb0f"
},
"sql-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "4bbeda009afc2d98d07cdcd5ad318ad7b8921073f74a3118b452b9c01947db0d"
},
"sre-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "f3bcde0381f316487e2475f01ed6951d6e1d5636c46c807c29f5efc5a802381e"
},
"state-management": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "0bad07f74366ae633503a6f256eae7d7335c5ba9277110b4d788263d56a7a07a"
},
"swift-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "3486707c8581c84d35d3da765ad9afd3744f93e9d208505bcb91c093e70d95c1"
},
"symcli-skill": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "7e565fc813d15787deb8341c56e2bd964ce669ce8fbaad065b47841d635e28b6"
},
"tailwind-best-practices": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "39145b4b9d12b154d9ff9261805d225557a1dcb5c881b1da8dcc027004aefd44"
},
"technical-debt": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "a127f457928fb6dc439751ec0cdb6edef14c78e90b6fd6e26ff31eb18b2e90c3"
},
"terraform-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "04e717ca5d883a2839863f371f8ebba8b3f89cde3b2292cf90a3186c30b2d24a"
},
"test-master": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "176d5be77c9fb4e64335a4ad9e38dfb82ccb029dc302c417db99a11e2d880180"
},
"testing-best-practices": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "590eb4064d9fa41854f7871991a3c1ef566553593338632c6cf88d9984194d61"
},
"the-fool": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "41aa64b8ccd474bd3c693454f4e0fc01a190512e01264817285a0527362d9756"
},
"typescript-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "b53b57340243eab4b75acee9096f0444319bb1b9e8530fdab4d1d75e1568fa6c"
},
"typescript-react-patterns": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "650048e53310097d1e9029a668c0b39e5738ce70fbe3b23f35cd875fc05b48e5"
},
"ui-mockup-visualizer": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "42248697304475f800e799899431c03845fb9f8841edb3f7b5fb9f8ba8fd1a10"
},
"vps-docker-traefik-deploy": {
"source": "thienanblog/awesome-ai-agent-skills",
"sourceType": "github",
"computedHash": "fdb0cfdbebd039ddec2d8f3a2ee5734188c05e6b4bc85be377114f821f790895"
},
"vue-expert": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "7276996f25e0e22316d480d12a51ea8f7441cc9855e79590ed0f5e2a25da4714"
},
"vue-expert-js": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "f9480e6f932c64f0f87a609691bbf587978f21017f9104b4010c4890b03dea16"
},
"web-design-guidelines": {
"source": "asyrafhussin/agent-skills",
"sourceType": "github",
"computedHash": "e27800d4c6a4cf98c50776af70d0b0ea1ebf5b81a03992b2019309f0ea9e7012"
},
"websocket-engineer": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "8e5e1a8313d92d2d9631b163672291f7edd4cfdc4a7ede545fbcd863c0ab7f7b"
},
"wordpress-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"computedHash": "ef569ff0920fa0948aca9a464118e13b9a9d655f40fd5b61526b7e53a80f451c"
}
}
}