Browse Source

fix: Fix SessionStart hook and bash permission issue

- Simplify SessionStart hook with proper JSON escaping
- Use escape_for_json function for clean output
- Remove complex status checking (keep it simple)
- Add first-time user welcome message
- Fix bash permission error in /oac:setup command
- Change from 'bash script' to just 'script' to avoid approval requirement
- Add exit 0 at end of hook script

Fixes:
- SessionStart hook now properly injects using-oac skill
- /oac:setup no longer requires bash command approval
darrenhinde 5 months ago
parent
commit
1742538ac0
2 changed files with 30 additions and 209 deletions
  1. 1 1
      plugins/claude-code/commands/oac-setup.md
  2. 29 208
      plugins/claude-code/hooks/session-start.sh

+ 1 - 1
plugins/claude-code/commands/oac-setup.md

@@ -11,7 +11,7 @@ Download context files from the OpenAgents Control repository to enable context-
 
 ## Download Progress
 
-!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/download-context.sh $ARGUMENTS`
+!`${CLAUDE_PLUGIN_ROOT}/scripts/download-context.sh $ARGUMENTS`
 
 ## What Was Downloaded
 

+ 29 - 208
plugins/claude-code/hooks/session-start.sh

@@ -1,226 +1,47 @@
-#!/bin/bash
+#!/usr/bin/env bash
 # SessionStart hook for OAC plugin
-# Injects using-oac skill and checks .tmp cleanup, .oac config, and context installation
 
 set -euo pipefail
 
-# Resolve plugin root directory
-PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
+# 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"
-TMP_SESSIONS_DIR=".tmp/sessions"
-CONTEXT_MANIFEST=".context-manifest.json"
-OAC_EXAMPLE="${PLUGIN_ROOT}/.oac.example"
 
-# Load using-oac skill content
-if [[ ! -f "${SKILL_FILE}" ]]; then
-  echo '{"error": "using-oac skill not found at '"${SKILL_FILE}"'"}' >&2
-  exit 1
-fi
-
-SKILL_CONTENT=$(cat "${SKILL_FILE}")
-
-# Escape for JSON using parameter substitution (fast method)
-# Replace backslash, double-quote, newline, tab, carriage return
-SKILL_CONTENT="${SKILL_CONTENT//\\/\\\\}"  # Backslash
-SKILL_CONTENT="${SKILL_CONTENT//\"/\\\"}"  # Double-quote
-SKILL_CONTENT="${SKILL_CONTENT//$'\n'/\\n}"  # Newline
-SKILL_CONTENT="${SKILL_CONTENT//$'\t'/\\t}"  # Tab
-SKILL_CONTENT="${SKILL_CONTENT//$'\r'/\\r}"  # Carriage return
-
-# Check for .oac configuration
-CONFIG_EXISTS=false
-CONFIG_LOCATION=""
-RECOMMENDATIONS=()
-
-if [[ -f ".oac" ]]; then
-  CONFIG_EXISTS=true
-  CONFIG_LOCATION="project (.oac)"
-elif [[ -f "$HOME/.oac" ]]; then
-  CONFIG_EXISTS=true
-  CONFIG_LOCATION="global (~/.oac)"
-else
-  RECOMMENDATIONS+=("Copy .oac.example to .oac to customize settings")
-fi
-
-# Check for context installation
-CONTEXT_INSTALLED=false
-CONTEXT_AGE_DAYS=0
-CONTEXT_WARNING=""
-
-if [[ -f "${CONTEXT_MANIFEST}" ]]; then
-  CONTEXT_INSTALLED=true
-  
-  # Check age of context manifest (>30 days = suggest update)
-  if [[ "$(uname)" == "Darwin" ]]; then
-    # macOS
-    MANIFEST_MTIME=$(stat -f %m "${CONTEXT_MANIFEST}" 2>/dev/null || echo 0)
-  else
-    # Linux
-    MANIFEST_MTIME=$(stat -c %Y "${CONTEXT_MANIFEST}" 2>/dev/null || echo 0)
-  fi
-  
-  CURRENT_TIME=$(date +%s)
-  CONTEXT_AGE_DAYS=$(( (CURRENT_TIME - MANIFEST_MTIME) / 86400 ))
-  
-  if [[ ${CONTEXT_AGE_DAYS} -gt 30 ]]; then
-    CONTEXT_WARNING="⚠️  Context is ${CONTEXT_AGE_DAYS} days old. Consider running /oac:setup --core to update."
-    RECOMMENDATIONS+=("Run /oac:setup --core to update context (${CONTEXT_AGE_DAYS} days old)")
-  fi
-else
-  RECOMMENDATIONS+=("Run /oac:setup --core to download context files")
-fi
-
-# Detect first-time user
-FIRST_TIME_USER=false
-if [[ "${CONFIG_EXISTS}" == "false" ]] && [[ "${CONTEXT_INSTALLED}" == "false" ]]; then
-  FIRST_TIME_USER=true
-fi
-
-# Check .tmp/ cleanup needs
-CLEANUP_SCRIPT="${PLUGIN_ROOT}/scripts/cleanup-tmp.sh"
-CLEANUP_WARNING=""
-
-if [[ -x "${CLEANUP_SCRIPT}" ]]; then
-  # Check for old .tmp files
-  OLD_SESSIONS=0
-  OLD_TASKS=0
-  OLD_EXTERNAL=0
-  
-  # Count old sessions (>7 days)
-  if [[ -d ".tmp/sessions" ]]; then
-    OLD_SESSIONS=$(find ".tmp/sessions" -mindepth 1 -maxdepth 1 -type d -mtime +7 2>/dev/null | wc -l | tr -d ' ')
-  fi
-  
-  # Count old completed tasks (>30 days)
-  if [[ -d ".tmp/tasks" ]]; then
-    for task_dir in .tmp/tasks/*; do
-      if [[ ! -d "$task_dir" ]]; then continue; fi
-      
-      all_completed=true
-      has_subtasks=false
-      
-      for subtask_file in "$task_dir"/subtask_*.json; do
-        if [[ ! -f "$subtask_file" ]]; then continue; fi
-        has_subtasks=true
-        
-        if ! grep -q '"status": "completed"' "$subtask_file" 2>/dev/null; then
-          all_completed=false
-          break
-        fi
-        
-        if [[ $(find "$subtask_file" -mtime +30 2>/dev/null | wc -l) -eq 0 ]]; then
-          all_completed=false
-          break
-        fi
-      done
-      
-      if [[ "$has_subtasks" == "true" ]] && [[ "$all_completed" == "true" ]]; then
-        ((OLD_TASKS++))
-      fi
-    done
-  fi
-  
-  # Count old external context (>7 days)
-  if [[ -d ".tmp/external-context" ]]; then
-    OLD_EXTERNAL=$(find ".tmp/external-context" -mindepth 1 -maxdepth 1 -type d -mtime +7 2>/dev/null | wc -l | tr -d ' ')
-  fi
-  
-  TOTAL_OLD=$((OLD_SESSIONS + OLD_TASKS + OLD_EXTERNAL))
-  
-  if [[ ${TOTAL_OLD} -gt 0 ]]; then
-    CLEANUP_WARNING="⚠️  Found ${TOTAL_OLD} old temporary items (${OLD_SESSIONS} sessions, ${OLD_TASKS} tasks, ${OLD_EXTERNAL} external cache). Run cleanup script to review: bash ${CLEANUP_SCRIPT}"
-    RECOMMENDATIONS+=("Run cleanup script to remove old temporary files")
-  fi
-fi
-
-# Build context injection message
-CONTEXT_MESSAGE="# OpenAgents Control (OAC) Workflow
-
-**EXTREMELY_IMPORTANT**: This skill is automatically loaded for every development task.
-
-${SKILL_CONTENT}"
-
-# Add first-time user welcome
-if [[ "${FIRST_TIME_USER}" == "true" ]]; then
-  CONTEXT_MESSAGE="${CONTEXT_MESSAGE}
-
----
-
-## 👋 Welcome to OpenAgents Control!
-
-This appears to be your first time using OAC. Here's how to get started:
-
-1. **Download context files**: Run \`/oac:setup --core\` to get coding standards and patterns
-2. **Customize settings**: Copy \`.oac.example\` to \`.oac\` to configure your preferences
-3. **Learn the workflow**: Run \`/oac:help\` to see the 6-stage development workflow
-
-Once context is installed, OAC will automatically discover relevant standards and patterns for every task."
-fi
-
-# Add context warning if needed
-if [[ -n "${CONTEXT_WARNING}" ]]; then
-  CONTEXT_MESSAGE="${CONTEXT_MESSAGE}
-
----
-
-${CONTEXT_WARNING}"
-fi
-
-# Add cleanup warning if needed
-if [[ -n "${CLEANUP_WARNING}" ]]; then
-  CONTEXT_MESSAGE="${CONTEXT_MESSAGE}
+# Read using-oac content
+using_oac_content=$(cat "${SKILL_FILE}" 2>&1 || echo "Error reading using-oac skill")
+
+# Escape string for JSON embedding using bash parameter substitution
+# Each ${s//old/new} is a single C-level pass - orders of magnitude
+# faster than character-by-character loop
+escape_for_json() {
+    local s="$1"
+    s="${s//\\/\\\\}"
+    s="${s//\"/\\\"}"
+    s="${s//$'\n'/\\n}"
+    s="${s//$'\r'/\\r}"
+    s="${s//$'\t'/\\t}"
+    printf '%s' "$s"
+}
 
----
+using_oac_escaped=$(escape_for_json "$using_oac_content")
 
-${CLEANUP_WARNING}"
+# Build warning message for first-time users
+warning_message=""
+if [[ ! -f ".context-manifest.json" ]]; then
+    warning_message="\n\n<important-reminder>IN YOUR FIRST REPLY AFTER SEEING THIS MESSAGE YOU MUST TELL THE USER:👋 **Welcome to OpenAgents Control!** To get started, run /oac:setup to download context files. Then use /oac:help to learn the 6-stage workflow.</important-reminder>"
 fi
 
-# Escape context message for JSON
-CONTEXT_MESSAGE="${CONTEXT_MESSAGE//\\/\\\\}"
-CONTEXT_MESSAGE="${CONTEXT_MESSAGE//\"/\\\"}"
-CONTEXT_MESSAGE="${CONTEXT_MESSAGE//$'\n'/\\n}"
-CONTEXT_MESSAGE="${CONTEXT_MESSAGE//$'\t'/\\t}"
-CONTEXT_MESSAGE="${CONTEXT_MESSAGE//$'\r'/\\r}"
+warning_escaped=$(escape_for_json "$warning_message")
 
-# Build recommendations JSON array
-RECOMMENDATIONS_JSON="["
-FIRST_REC=true
-for rec in "${RECOMMENDATIONS[@]}"; do
-  if [[ "${FIRST_REC}" == "true" ]]; then
-    FIRST_REC=false
-  else
-    RECOMMENDATIONS_JSON="${RECOMMENDATIONS_JSON},"
-  fi
-  # Escape recommendation for JSON
-  REC_ESCAPED="${rec//\\/\\\\}"
-  REC_ESCAPED="${REC_ESCAPED//\"/\\\"}"
-  RECOMMENDATIONS_JSON="${RECOMMENDATIONS_JSON}\"${REC_ESCAPED}\""
-done
-RECOMMENDATIONS_JSON="${RECOMMENDATIONS_JSON}]"
-
-# Output JSON with hookSpecificOutput format
+# Output context injection as JSON
 cat <<EOF
 {
   "hookSpecificOutput": {
     "hookEventName": "SessionStart",
-    "additionalContext": "<EXTREMELY_IMPORTANT>OAC workflow is active. Follow 6-stage process: Analyze, Plan & Approve, LoadContext, Execute, Validate, Complete.</EXTREMELY_IMPORTANT>",
-    "firstTimeUser": ${FIRST_TIME_USER},
-    "contextInstalled": ${CONTEXT_INSTALLED},
-    "contextAgeDays": ${CONTEXT_AGE_DAYS},
-    "configExists": ${CONFIG_EXISTS},
-    "configLocation": "${CONFIG_LOCATION}",
-    "recommendations": ${RECOMMENDATIONS_JSON},
-    "contextInjection": {
-      "priority": "EXTREMELY_IMPORTANT",
-      "content": "${CONTEXT_MESSAGE}"
-    },
-    "tmpCleanup": {
-      "oldSessions": ${OLD_SESSIONS:-0},
-      "oldTasks": ${OLD_TASKS:-0},
-      "oldExternal": ${OLD_EXTERNAL:-0},
-      "total": ${TOTAL_OLD:-0},
-      "warning": "${CLEANUP_WARNING}"
-    }
+    "additionalContext": "<EXTREMELY_IMPORTANT>\nYou are using OpenAgents Control (OAC).\n\n**Below is the full content of your 'using-oac' skill - your guide to the 6-stage workflow. For all other skills, use the 'Skill' tool:**\n\n${using_oac_escaped}\n\n${warning_escaped}\n</EXTREMELY_IMPORTANT>"
   }
 }
 EOF
+
+exit 0