From 1f9f2c0571dff343376677a1956e03c541e5ff7e Mon Sep 17 00:00:00 2001 From: Darren Hinde <107584450+darrenhinde@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:57:46 +0000 Subject: [PATCH] fix(plugin): invoke hook via bash to avoid +x permission requirement (#245) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(shellcheck): declare and assign separately in router.sh (SC2155) * feat(plugin): enhance context management and skill invocation - Update .context-manifest.json to include profile and detailed source information. - Revise context-scout.md to clarify context root discovery and navigation. - Modify commands to replace deprecated skills with updated ones (brainstorming → approach, systematic-debugging → debugger). - Streamline install-context.md for clarity and remove interactive profile selection. - Improve oac-status.md to provide a more comprehensive status report. - Add OAC system paths to session-start.sh for better context management. - Remove obsolete skills and improve documentation for existing commands. * fix(plugin): fix hook JSON escaping and unconditional session firing - Fix double-escaped skill catalogue: use $'\n' real newlines instead of literal \n strings so escape_for_json encodes them correctly as \n in JSON - Fix OAC_SYSTEM_PATHS embedded raw into JSON string: build with real newlines and pass through escape_for_json before interpolation - Remove SessionStart matcher so hook fires on every session start unconditionally (startup, resume, clear, compact and any future types) - Remove redundant async:false field; add explicit timeout:30 * fix(plugin): add Windows hook wrapper, fix manifest check, remove bundled context - Add run-hook.cmd polyglot wrapper for Windows compatibility — routes session-start through bash on Windows (Git Bash, MSYS2, Cygwin) and falls back silently if no bash found so plugin still loads - Fix warning manifest check to look in correct locations: project-local .claude/.context-manifest.json and global ~/.claude/.context-manifest.json instead of the plugin root (which is never writable for marketplace users) - Update warning message skill name: install-context -> context-setup - Restore SessionStart matcher (startup|resume|clear|compact) for explicit session type filtering - Remove bundled scripts/context/ tree (250+ files) — context is downloaded at runtime via /install-context, not shipped with the plugin * fix(plugin): invoke hook via bash to avoid +x permission requirement Plugin files installed from marketplace are copied with 644 permissions (no execute bit). Invoking session-start.sh directly requires +x, which fails silently. Using 'bash "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"' bypasses the execute bit entirely — bash reads the file, not executes it. - hooks.json: replace run-hook.cmd indirection with direct bash invocation - hooks.json: add explicit timeout:30, remove async:false (default) - Remove run-hook.cmd and extensionless session-start (no longer needed) - Ensure session-start.sh tracked as 100644 (read-only) in git - plugin.json: bump version 1.0.0 -> 1.0.1 so marketplace picks up changes - plugin.json: update description to OpenAgentsControl branding --- .../claude-code/.claude-plugin/plugin.json | 4 +- plugins/claude-code/hooks/hooks.json | 6 +- plugins/claude-code/hooks/run-hook.cmd | 46 ---------- plugins/claude-code/hooks/session-start | 90 ------------------- 4 files changed, 5 insertions(+), 141 deletions(-) delete mode 100755 plugins/claude-code/hooks/run-hook.cmd delete mode 100755 plugins/claude-code/hooks/session-start diff --git a/plugins/claude-code/.claude-plugin/plugin.json b/plugins/claude-code/.claude-plugin/plugin.json index c5a0aef..8b3b45a 100644 --- a/plugins/claude-code/.claude-plugin/plugin.json +++ b/plugins/claude-code/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "oac", - "description": "OpenAgents Control - Intelligent code review, TDD test generation, automated documentation, and smart task breakdown with context-aware AI agents", - "version": "1.0.0", + "description": "OpenAgentsControl — multi-agent orchestration for Claude Code. Context-aware development with skills, subagents, parallel execution, and automated code review.", + "version": "1.0.1", "author": { "name": "darrenhinde", "url": "https://github.com/darrenhinde" diff --git a/plugins/claude-code/hooks/hooks.json b/plugins/claude-code/hooks/hooks.json index 820bf0f..7ab0312 100644 --- a/plugins/claude-code/hooks/hooks.json +++ b/plugins/claude-code/hooks/hooks.json @@ -5,11 +5,11 @@ "hooks": [ { "type": "command", - "command": "'${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd' session-start", - "async": false + "command": "bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh\"", + "timeout": 30 } ] } ] } -} \ No newline at end of file +} diff --git a/plugins/claude-code/hooks/run-hook.cmd b/plugins/claude-code/hooks/run-hook.cmd deleted file mode 100755 index e23639d..0000000 --- a/plugins/claude-code/hooks/run-hook.cmd +++ /dev/null @@ -1,46 +0,0 @@ -: << 'CMDBLOCK' -@echo off -REM Cross-platform polyglot wrapper for hook scripts. -REM On Windows: cmd.exe runs the batch portion, which finds and calls bash. -REM On Unix: the shell interprets this as a script (: is a no-op in bash). -REM -REM Hook scripts use extensionless filenames (e.g. "session-start" not -REM "session-start.sh") so Claude Code's Windows auto-detection -- which -REM prepends "bash" to any command containing .sh -- doesn't interfere. -REM -REM Usage: run-hook.cmd [args...] - -if "%~1"=="" ( - echo run-hook.cmd: missing script name >&2 - exit /b 1 -) - -set "HOOK_DIR=%~dp0" - -REM Try Git for Windows bash in standard locations -if exist "C:\Program Files\Git\bin\bash.exe" ( - "C:\Program Files\Git\bin\bash.exe" "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9 - exit /b %ERRORLEVEL% -) -if exist "C:\Program Files (x86)\Git\bin\bash.exe" ( - "C:\Program Files (x86)\Git\bin\bash.exe" "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9 - exit /b %ERRORLEVEL% -) - -REM Try bash on PATH (e.g. user-installed Git Bash, MSYS2, Cygwin) -where bash >nul 2>nul -if %ERRORLEVEL% equ 0 ( - bash "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9 - exit /b %ERRORLEVEL% -) - -REM No bash found - exit silently rather than error -REM (plugin still works, just without SessionStart context injection) -exit /b 0 -CMDBLOCK - -# Unix: run the named script directly -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -SCRIPT_NAME="$1" -shift -exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@" \ No newline at end of file diff --git a/plugins/claude-code/hooks/session-start b/plugins/claude-code/hooks/session-start deleted file mode 100755 index 188b34f..0000000 --- a/plugins/claude-code/hooks/session-start +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash -# SessionStart hook for OAC plugin - -set -euo pipefail - -# Determine plugin root directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" -PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" -SKILL_FILE="${PLUGIN_ROOT}/skills/using-oac/SKILL.md" - -# Read using-oac content -using_oac_content=$(cat "${SKILL_FILE}" 2>&1 || echo "Error reading using-oac skill") - - -# Escape string for JSON embedding -# SECURITY: Prevents command injection attacks from malicious SKILL.md files -escape_for_json() { - local s="$1" - # Escape backslashes FIRST - order matters! - s="${s//\\/\\\\}" - # Escape double quotes - s="${s//\"/\\\"}" - # Escape newlines, carriage returns, tabs - s="${s//$'\n'/\\n}" - s="${s//$'\r'/\\r}" - s="${s//$'\t'/\\t}" - printf '%s' "$s" -} - -using_oac_escaped=$(escape_for_json "$using_oac_content") - -# Build skill catalogue from skills directory -skill_catalogue="" -if [ -d "${PLUGIN_ROOT}/skills" ]; then - for skill_dir in "${PLUGIN_ROOT}/skills"/*/; do - skill_name=$(basename "$skill_dir") - skill_file="${skill_dir}SKILL.md" - if [ -f "$skill_file" ]; then - # Extract description from frontmatter - description=$(grep -m1 '^description:' "$skill_file" 2>/dev/null | sed 's/^description: *//;s/^"//;s/"$//' || echo "") - if [ -n "$description" ]; then - skill_catalogue="${skill_catalogue}\n- oac:${skill_name} — ${description}" - else - skill_catalogue="${skill_catalogue}\n- oac:${skill_name}" - fi - fi - done -fi - -# Build warning message for first-time users -# Context can be installed project-local (.claude/.context-manifest.json) -# or globally (~/.claude/.context-manifest.json) -PROJECT_MANIFEST="$(pwd)/.claude/.context-manifest.json" -GLOBAL_MANIFEST="${HOME}/.claude/.context-manifest.json" -warning_message="" -if [[ ! -f "${PROJECT_MANIFEST}" ]] && [[ ! -f "${GLOBAL_MANIFEST}" ]]; then - warning_message="\n\nIN YOUR FIRST REPLY AFTER SEEING THIS MESSAGE YOU MUST TELL THE USER: **No context files found.** Invoke the context-setup skill now to set up context for this project — it will check your project type and recommend the right profile. Say: \"I notice you don't have context files set up. Let me get that sorted for you.\" Then immediately run the context-setup skill." -fi - -warning_escaped=$(escape_for_json "$warning_message") -skill_catalogue_escaped=$(escape_for_json "$skill_catalogue") - -# Build OAC system paths block -PROTOCOL_PATH="${PLUGIN_ROOT}/skills/context-discovery/context-discovery-protocol.md" -plugin_root_escaped=$(escape_for_json "$PLUGIN_ROOT") -protocol_path_escaped=$(escape_for_json "$PROTOCOL_PATH") -OAC_SYSTEM_PATHS="## OAC System Paths\n- Plugin Root: ${plugin_root_escaped}\n- Context Discovery Protocol: ${protocol_path_escaped}" - -# Context scout instruction — run proactively at session start when context is available -CONTEXT_SCOUT_INSTRUCTION="## Context Discovery\nBefore responding to any coding or implementation request this session, use the 'oac:context-discovery' skill to locate the project's coding standards. This runs once per session — do not repeat it if already done." - -context_scout_escaped=$(escape_for_json "$CONTEXT_SCOUT_INSTRUCTION") - -# Build context string -OAC_CONTEXT="\nYou have OAC (OpenAgents Control) installed. BEFORE responding to the user's first message, you MUST invoke the Skill tool with name='using-oac'. This is mandatory. Do not skip it. Do not respond first and invoke later. Invoke the skill FIRST.\n\n**Below is the full content of your 'oac:using-oac' skill — your introduction to using OAC skills. For all other skills, use the 'Skill' tool:**\n\n${using_oac_escaped}\n\n## Available OAC Skills (invoke with the Skill tool):\n${skill_catalogue_escaped}\n\n${OAC_SYSTEM_PATHS}\n\n${context_scout_escaped}\n\n${warning_escaped}\n" - -# Output dual-format JSON for cross-tool compatibility -# - additionalContext: Claude Code (hookSpecificOutput) -# - additional_context: Cursor / OpenCode / other tools -cat <