skills/bin/deploy-skill.sh
dan 84d2de1683 feat(deployment): Add support for Gemini and Codex
- RFC-MULTI-AGENT-DEPLOYMENT.md: Design for unified deployment
- modules/ai-skills.nix: Added geminiSkills option
- bin/use-skills.sh: Added GEMINI_HOME support
- bin/deploy-skill.sh: Inject configs for Codex and Gemini
2026-01-19 15:31:32 -08:00

254 lines
6.9 KiB
Bash
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Deploy a skill from this repo to dotfiles for system-wide availability
set -euo pipefail
SKILLS_REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DOTFILES_REPO="$HOME/proj/dotfiles"
SKILL_NAME="${1:-}"
usage() {
cat <<EOF
Usage: $0 <skill-name>
Deploy a skill from ~/proj/skills to ~/proj/dotfiles for system-wide deployment.
Arguments:
skill-name Name of skill directory in skills/
Examples:
$0 screenshot-latest
$0 niri-window-capture
This script:
1. Copies skill to dotfiles/claude/skills/
2. Shows you the Nix config to add
3. Reminds you to rebuild
You must manually:
- Edit home/claude.nix
- Edit home/opencode.nix
- Run: sudo nixos-rebuild switch --flake .#delpad
- Restart AI agents
Available skills:
EOF
ls -1 "$SKILLS_REPO/skills" | grep -v template | sed 's/^/ /'
exit 1
}
# Function to inject config into Nix file
inject_nix_config() {
local target_file="$1"
local config_block="$2"
local marker="$3" # Unique string to check if already deployed
if [[ ! -f "$target_file" ]]; then
echo "⚠️ File not found: $target_file (skipping)"
return
fi
if grep -q "$marker" "$target_file"; then
echo " Config already present in $(basename "$target_file")"
else
echo "Injecting config into $(basename "$target_file")..."
# Create a secure temporary file
local temp_file
temp_file=$(mktemp "${target_file}.XXXXXX")
# Ensure cleanup on exit or error
trap 'rm -f "$temp_file"' EXIT
# Insert before the last line (assuming it is '}')
if ! head -n -1 "$target_file" > "$temp_file"; then
echo "Error: failed to read $target_file" >&2
return 1
fi
echo "$config_block" >> "$temp_file"
if ! tail -n 1 "$target_file" >> "$temp_file"; then
echo "Error: failed to append to $temp_file" >&2
return 1
fi
# Validate: temp file should be larger than original (since we're adding)
local orig_size
orig_size=$(stat -c%s "$target_file")
local new_size
new_size=$(stat -c%s "$temp_file")
if [[ $new_size -le $orig_size ]]; then
echo "Error: Validation failed, new file is not larger than original" >&2
return 1
fi
# Atomic move
if ! mv "$temp_file" "$target_file"; then
echo "Error: Failed to replace $target_file" >&2
return 1
fi
# Clear trap after successful move
trap - EXIT
echo "✓ Updated $(basename "$target_file")"
fi
}
# Helper to inject a home.file entry into a Nix config
# Usage: inject_home_file <target_nix_file> <dest_path_in_home> <source_relative_to_config> <extra_props> <comment>
inject_home_file() {
local target_file="$1"
local home_path="$2"
local source_path="$3"
local extra_props="$4"
local comment="$5"
local config_block="
# Skill: $comment
home.file.\"$home_path\" = {
source = $source_path;
$extra_props
};"
inject_nix_config "$target_file" "$config_block" "$home_path"
}
if [[ -z "$SKILL_NAME" ]]; then
usage
fi
SKILL_SOURCE="$SKILLS_REPO/skills/$SKILL_NAME"
SKILL_DEST="$DOTFILES_REPO/claude/skills/$SKILL_NAME"
# Validate skill exists
if [[ ! -d "$SKILL_SOURCE" ]]; then
echo "Error: Skill not found: $SKILL_SOURCE" >&2
echo "" >&2
usage
fi
# Validate dotfiles repo exists
if [[ ! -d "$DOTFILES_REPO" ]]; then
echo "Error: Dotfiles repo not found: $DOTFILES_REPO" >&2
exit 1
fi
# Check if skill has SKILL.md
if [[ ! -f "$SKILL_SOURCE/SKILL.md" ]]; then
echo "Error: $SKILL_NAME missing SKILL.md" >&2
exit 1
fi
# Check if already deployed
if [[ -d "$SKILL_DEST" ]]; then
echo "⚠️ Skill already deployed: $SKILL_DEST"
read -p "Overwrite? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled"
exit 1
fi
rm -rf "$SKILL_DEST"
fi
# Check for security docs
SECURITY_WARNING=""
if [[ -f "$SKILL_SOURCE/SECURITY.md" ]]; then
SECURITY_WARNING="
⚠️ ⚠️ ⚠️ SECURITY WARNING ⚠️ ⚠️ ⚠️
This skill has security documentation.
READ BEFORE DEPLOYING: $SKILL_DEST/SECURITY.md
Security-sensitive skills should only be deployed after:
1. Reviewing security documentation
2. Understanding risks and mitigations
3. Configuring protection mechanisms
"
fi
echo "Deploying skill: $SKILL_NAME"
echo ""
echo "Source: $SKILL_SOURCE"
echo "Dest: $SKILL_DEST"
echo ""
# Copy skill
mkdir -p "$(dirname "$SKILL_DEST")"
cp -r "$SKILL_SOURCE" "$SKILL_DEST"
echo "✓ Skill copied to dotfiles"
echo ""
if [[ -n "$SECURITY_WARNING" ]]; then
echo "$SECURITY_WARNING"
fi
echo "Configuring system..."
echo ""
# 1. Claude Code Config
inject_home_file "$DOTFILES_REPO/home/claude.nix" \
".claude/skills/$SKILL_NAME" \
"../claude/skills/$SKILL_NAME" \
"recursive = true;" \
"$SKILL_NAME"
# 2. OpenCode Config
inject_home_file "$DOTFILES_REPO/home/opencode.nix" \
".config/opencode/skills/$SKILL_NAME" \
"../claude/skills/$SKILL_NAME" \
"recursive = true;" \
"$SKILL_NAME"
# 3. Codex Config (if home/codex.nix exists)
if [[ -f "$DOTFILES_REPO/home/codex.nix" ]]; then
inject_home_file "$DOTFILES_REPO/home/codex.nix" \
".codex/skills/$SKILL_NAME" \
"../claude/skills/$SKILL_NAME" \
"recursive = true;" \
"$SKILL_NAME"
fi
# 4. Gemini Config (if home/gemini.nix exists)
if [[ -f "$DOTFILES_REPO/home/gemini.nix" ]]; then
inject_home_file "$DOTFILES_REPO/home/gemini.nix" \
".gemini/skills/$SKILL_NAME" \
"../claude/skills/$SKILL_NAME" \
"recursive = true;" \
"$SKILL_NAME"
fi
# 5. Antigravity / Global Config
# Check if antigravity.nix exists, otherwise warn
ANTIGRAVITY_NIX="$DOTFILES_REPO/home/antigravity.nix"
if [[ -f "$ANTIGRAVITY_NIX" ]]; then
# For global scripts, we need to find executable scripts in the skill
if [[ -d "$SKILL_SOURCE/scripts" ]]; then
SCRIPTS=$(find "$SKILL_SOURCE/scripts" -name "*.sh" -type f)
for script in $SCRIPTS; do
SCRIPT_NAME=$(basename "$script")
SCRIPT_NO_EXT="${SCRIPT_NAME%.*}"
LINK_NAME="$SCRIPT_NO_EXT"
inject_home_file "$ANTIGRAVITY_NIX" \
".local/bin/$LINK_NAME" \
"../claude/skills/$SKILL_NAME/scripts/$SCRIPT_NAME" \
"executable = true;" \
"$SKILL_NAME ($SCRIPT_NAME)"
done
fi
else
echo "⚠️ $ANTIGRAVITY_NIX not found. Skipping global binary configuration."
echo " To enable global binaries, create home/antigravity.nix and add it to your flake."
fi
echo ""
echo "Deployment configured."
echo "Run the following to apply changes:"
echo ""
echo " cd $DOTFILES_REPO"
echo " sudo nixos-rebuild switch --flake .#delpad"
echo ""
echo "Then restart your agents."