From f4017abf451d430965c60585046cdae38b7bd022 Mon Sep 17 00:00:00 2001 From: Brydon DeWitt Date: Fri, 22 May 2026 21:15:42 -0400 Subject: [PATCH] fix(install): generate copilot hooks with absolute paths Instead of symlinking ~/.copilot/hooks/agent-support.json to dotfiles hooks.json (which uses relative paths resolved from workspace root), generate the file at install time with absolute paths to dotfiles hooks. This means projects no longer need per-project hook stubs or symlinks. A project only needs .agents/hooks/post-tool-use-remnant.sh (or similar) for its own overlay, wired as a second PostToolUse in .github/hooks/. --- .agents/install.sh | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/.agents/install.sh b/.agents/install.sh index 7e970b6..db913bf 100755 --- a/.agents/install.sh +++ b/.agents/install.sh @@ -11,16 +11,38 @@ warn() { printf '\033[0;33m⚠\033[0m %s\n' "$1"; } skip() { printf '\033[0;34m–\033[0m %s\n' "$1"; } # ── 1. Copilot global hooks ────────────────────────────────────────────────── +# Generate ~/.copilot/hooks/agent-support.json with absolute paths so the hooks +# work from any workspace — no per-project symlinks or stubs needed. COPILOT_HOOKS_DIR="$HOME/.copilot/hooks" -COPILOT_HOOK_TARGET="$DOTFILES_AGENTS/frameworks/github/hooks.json" -COPILOT_HOOK_LINK="$COPILOT_HOOKS_DIR/agent-support.json" +COPILOT_HOOK_FILE="$COPILOT_HOOKS_DIR/agent-support.json" mkdir -p "$COPILOT_HOOKS_DIR" -if [[ -L "$COPILOT_HOOK_LINK" && "$(readlink "$COPILOT_HOOK_LINK")" == "$COPILOT_HOOK_TARGET" ]]; then - skip "Copilot hook symlink already set: $COPILOT_HOOK_LINK" + +# Migrate: remove old symlink if present +if [[ -L "$COPILOT_HOOK_FILE" ]]; then + rm "$COPILOT_HOOK_FILE" + log "Removed old Copilot hook symlink (migrating to generated file)" +fi + +EXPECTED_PRE="$DOTFILES_AGENTS/hooks/pre-tool-use.sh" +if [[ -f "$COPILOT_HOOK_FILE" ]] && \ + node -e "const c=JSON.parse(require('fs').readFileSync('$COPILOT_HOOK_FILE','utf8')); process.exit(c.hooks&&c.hooks.PreToolUse&&c.hooks.PreToolUse[0].command==='$EXPECTED_PRE'?0:1);" 2>/dev/null; then + skip "Copilot global hooks already up-to-date: $COPILOT_HOOK_FILE" else - ln -sf "$COPILOT_HOOK_TARGET" "$COPILOT_HOOK_LINK" - log "Copilot hook symlink: $COPILOT_HOOK_LINK → $COPILOT_HOOK_TARGET" + node -e " +const fs = require('fs'); +const d = '$DOTFILES_AGENTS/hooks'; +const hooks = { + UserPromptSubmit: [{type:'command',command:d+'/user-prompt-submit.sh',timeout:5}], + SessionStart: [{type:'command',command:d+'/session-start.sh',timeout:10}], + PreToolUse: [{type:'command',command:d+'/pre-tool-use.sh',timeout:5}], + PostToolUse: [{type:'command',command:d+'/post-tool-use.sh',timeout:5}], + PreCompact: [{type:'command',command:d+'/pre-compact.sh',timeout:10}], + Stop: [{type:'command',command:d+'/stop.sh',timeout:5}] +}; +fs.writeFileSync('$COPILOT_HOOK_FILE', JSON.stringify({hooks}, null, 2) + '\n'); +" + log "Copilot global hooks generated with absolute paths: $COPILOT_HOOK_FILE" fi # ── 2. OpenCode global plugin ────────────────────────────────────────────────