Skip to content

Instantly share code, notes, and snippets.

@iconifyit
Created June 4, 2026 17:46
Show Gist options
  • Select an option

  • Save iconifyit/9f41e2148ab6eb8c7e7d0d2fa4465683 to your computer and use it in GitHub Desktop.

Select an option

Save iconifyit/9f41e2148ab6eb8c7e7d0d2fa4465683 to your computer and use it in GitHub Desktop.
Symlinks individual rules, skills, and workflows from master .agents to repo-level .agents
#!/usr/bin/env bash
function _agentify_help() {
cat <<EOF
agentify
Initializes and syncs a local project's .agents/ directory
against a globally managed agents repository.
FEATURES
- Initializes .agents/ if it does not exist
- Symlinks shared rules/skills/workflows from global agents repo
- Ensures local STATE.md exists
- Registers global AGENTS.md inheritance
- Regenerates AGENTS.md index
- Supports dry-run mode
- Supports force mode for replacing existing local paths
- Protects existing local non-symlink directories/files by default
GLOBAL STRUCTURE
~/github/@agents/
├── AGENTS.md
└── .agents/
├── rules/
├── skills/
└── workflows/
LOCAL PROJECT STRUCTURE
./project/
└── .agents/
├── STATE.md
├── rules -> symlink
├── skills -> symlink
└── workflows -> symlink
USAGE
agentify
Agentify current directory using default target:
- claude
agentify --targets claude cursor
Configure multiple targets.
agentify --dry-run
Show what would happen without making changes.
agentify --force
Replace existing local rules/skills/workflows paths even if they are
real directories or files.
agentify --force --dry-run
Preview destructive replacement without making changes.
agentify --targets claude codex --dry-run
Preview a multi-target configuration.
agentify -h
agentify --help
Show help.
SUPPORTED TARGETS
- claude
- windsurf
- cursor
- codex
- copilot
REQUIREMENTS
- sync-agents installed and available on PATH
- global agents repo exists at:
~/github/@agents
NOTES
- Shared resources are symlinked from the global repo.
- STATE.md remains project-local.
- Existing local non-symlink directories/files are NEVER replaced unless
--force is explicitly passed.
- This avoids accidental destruction of local project-specific agents.
- This avoids nested links like:
.agents/rules/rules
EOF
}
function ensure_symlink() {
local LOCAL_PATH="$1"
local TARGET_PATH="$2"
local DRY_RUN="$3"
local FORCE="$4"
if [[ ! -e "$LOCAL_PATH" ]]; then
if [[ "$DRY_RUN" == true ]]; then
echo "Dry run: would create symlink:"
echo " $LOCAL_PATH -> $TARGET_PATH"
else
ln -s "$TARGET_PATH" "$LOCAL_PATH"
fi
return 0
fi
if [[ -L "$LOCAL_PATH" ]]; then
local CURRENT_TARGET
CURRENT_TARGET="$(readlink "$LOCAL_PATH")"
if [[ "$CURRENT_TARGET" == "$TARGET_PATH" ]]; then
echo "Symlink already correct:"
echo " $LOCAL_PATH"
return 0
fi
if [[ "$DRY_RUN" == true ]]; then
echo "Dry run: would replace symlink:"
echo " $LOCAL_PATH"
echo " from: $CURRENT_TARGET"
echo " to: $TARGET_PATH"
else
rm "$LOCAL_PATH"
ln -s "$TARGET_PATH" "$LOCAL_PATH"
fi
return 0
fi
if [[ "$FORCE" == true ]]; then
if [[ "$DRY_RUN" == true ]]; then
echo "Dry run: would remove existing non-symlink path:"
echo " $LOCAL_PATH"
echo "Dry run: would create symlink:"
echo " $LOCAL_PATH -> $TARGET_PATH"
else
rm -rf "$LOCAL_PATH"
ln -s "$TARGET_PATH" "$LOCAL_PATH"
fi
return 0
fi
echo
echo "Refusing to replace non-symlink path:"
echo " $LOCAL_PATH"
echo
echo "This path already exists and is not a symlink."
echo "Replacing it could destroy local agent content."
echo
echo "Use --force if replacing this path is intentional."
return 1
}
function agentify() {
local AGENTS_GLOBAL_ROOT="$HOME/github/@agents"
local AGENTS_GLOBAL="$AGENTS_GLOBAL_ROOT/.agents"
local AGENTS_GLOBAL_FILE="$AGENTS_GLOBAL_ROOT/AGENTS.md"
local PROJECT_DIR
PROJECT_DIR="$(pwd)"
local DRY_RUN=false
local FORCE=false
local VALID_TARGETS=(
"claude"
"windsurf"
"cursor"
"codex"
"copilot"
)
local TARGETS=("claude")
while [[ $# -gt 0 ]]; do
case "$1" in
--targets)
shift
TARGETS=()
while [[ $# -gt 0 && ! "$1" =~ ^-- ]]; do
TARGETS+=("$1")
shift
done
;;
--dir)
shift
PROJECT_DIR="$(cd "$1" && pwd)"
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
--force)
FORCE=true
shift
;;
-h|--help)
_agentify_help
return 0
;;
*)
echo "Unknown argument: $1"
echo
echo "Run 'agentify --help' for usage."
return 1
;;
esac
done
for target in "${TARGETS[@]}"; do
local valid=false
for allowed in "${VALID_TARGETS[@]}"; do
if [[ "$target" == "$allowed" ]]; then
valid=true
break
fi
done
if [[ "$valid" == false ]]; then
echo "Invalid target: $target"
echo
echo "Valid targets:"
printf ' - %s\n' "${VALID_TARGETS[@]}"
return 1
fi
done
local TARGETS_ARG
TARGETS_ARG="$(IFS=,; echo "${TARGETS[*]}")"
local AGENTS_LOCAL="$PROJECT_DIR/.agents"
echo
echo "This will agentify the current directory:"
echo " $PROJECT_DIR"
echo
echo "Mode:"
if [[ "$DRY_RUN" == true ]]; then
echo " dry-run"
else
echo " apply"
fi
echo
echo "Force:"
if [[ "$FORCE" == true ]]; then
echo " enabled"
else
echo " disabled"
fi
echo
echo "Targets:"
printf ' - %s\n' "${TARGETS[@]}"
echo
echo "Global agents root:"
echo " $AGENTS_GLOBAL_ROOT"
echo
echo "The following paths will be configured:"
echo " $AGENTS_LOCAL/rules -> $AGENTS_GLOBAL/rules"
echo " $AGENTS_LOCAL/skills -> $AGENTS_GLOBAL/skills"
echo " $AGENTS_LOCAL/workflows -> $AGENTS_GLOBAL/workflows"
echo
echo "The following file will be ensured:"
echo " $AGENTS_LOCAL/STATE.md"
echo
echo "The following inheritance link will be added:"
echo " global -> $AGENTS_GLOBAL_FILE"
echo
if [[ "$DRY_RUN" != true ]]; then
read -r -p "Continue? [y/N] " confirm
case "$confirm" in
y|Y|yes|YES)
;;
*)
echo "Aborted."
return 0
;;
esac
fi
if ! command -v sync-agents >/dev/null 2>&1; then
echo "Error: sync-agents command not found."
return 1
fi
if [[ ! -d "$AGENTS_LOCAL" ]]; then
if ! command -v sync-agents >/dev/null 2>&1; then
echo "Error: sync-agents command not found."
return 1
fi
if [[ "$DRY_RUN" == true ]]; then
echo "Dry run: would initialize:"
echo " $AGENTS_LOCAL"
else
sync-agents init --dir "$PROJECT_DIR"
fi
fi
if [[ "$DRY_RUN" == true ]]; then
echo "Dry run: would ensure:"
echo " $AGENTS_LOCAL/STATE.md"
else
touch "$AGENTS_LOCAL/STATE.md"
fi
ensure_symlink \
"$AGENTS_LOCAL/rules" \
"$AGENTS_GLOBAL/rules" \
"$DRY_RUN" \
"$FORCE" || return 1
ensure_symlink \
"$AGENTS_LOCAL/skills" \
"$AGENTS_GLOBAL/skills" \
"$DRY_RUN" \
"$FORCE" || return 1
ensure_symlink \
"$AGENTS_LOCAL/workflows" \
"$AGENTS_GLOBAL/workflows" \
"$DRY_RUN" \
"$FORCE" || return 1
if [[ "$DRY_RUN" == true ]]; then
sync-agents inherit global "$AGENTS_GLOBAL_FILE" --dry-run --dir "$PROJECT_DIR" || true
sync-agents index --dry-run --dir "$PROJECT_DIR"
sync-agents sync --targets "$TARGETS_ARG" --dry-run --dir "$PROJECT_DIR"
else
sync-agents inherit global "$AGENTS_GLOBAL_FILE" --dir "$PROJECT_DIR" || true
sync-agents index --dir "$PROJECT_DIR"
sync-agents sync --targets "$TARGETS_ARG" --dir "$PROJECT_DIR"
fi
echo
if [[ "$DRY_RUN" == true ]]; then
echo "Agentify dry run complete."
else
echo "Agentify complete."
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment