Эх сурвалжийг харах

fix(plugin): invoke hook via bash to avoid +x permission requirement (#245)

* 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
Darren Hinde 5 сар өмнө
parent
commit
1f9f2c0571

+ 2 - 2
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"

+ 3 - 3
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
           }
         ]
       }
     ]
   }
-}
+}

+ 0 - 46
plugins/claude-code/hooks/run-hook.cmd

@@ -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 <script-name> [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}" "$@"

+ 0 - 90
plugins/claude-code/hooks/session-start

@@ -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\n<important-reminder>IN 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.</important-reminder>"
-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="<EXTREMELY_IMPORTANT>\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</EXTREMELY_IMPORTANT>"
-
-# Output dual-format JSON for cross-tool compatibility
-# - additionalContext: Claude Code (hookSpecificOutput)
-# - additional_context: Cursor / OpenCode / other tools
-cat <<EOF
-{
-  "additional_context": "${OAC_CONTEXT}",
-  "hookSpecificOutput": {
-    "hookEventName": "SessionStart",
-    "additionalContext": "${OAC_CONTEXT}"
-  }
-}
-EOF
-
-exit 0