session-start.sh 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #!/usr/bin/env bash
  2. # SessionStart hook for OAC plugin
  3. set -euo pipefail
  4. # Determine plugin root directory
  5. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
  6. PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
  7. SKILL_FILE="${PLUGIN_ROOT}/skills/using-oac/SKILL.md"
  8. # Read using-oac content
  9. using_oac_content=$(cat "${SKILL_FILE}" 2>&1 || echo "Error reading using-oac skill")
  10. # Escape string for JSON embedding
  11. # SECURITY: Prevents command injection attacks from malicious SKILL.md files
  12. escape_for_json() {
  13. local s="$1"
  14. # Escape backslashes FIRST - order matters!
  15. s="${s//\\/\\\\}"
  16. # Escape double quotes
  17. s="${s//\"/\\\"}"
  18. # Escape newlines, carriage returns, tabs
  19. s="${s//$'\n'/\\n}"
  20. s="${s//$'\r'/\\r}"
  21. s="${s//$'\t'/\\t}"
  22. printf '%s' "$s"
  23. }
  24. using_oac_escaped=$(escape_for_json "$using_oac_content")
  25. # Build skill catalogue from skills directory
  26. # Use real newlines (not literal \n) so escape_for_json encodes them correctly as \n in JSON
  27. skill_catalogue=""
  28. if [ -d "${PLUGIN_ROOT}/skills" ]; then
  29. for skill_dir in "${PLUGIN_ROOT}/skills"/*/; do
  30. skill_name=$(basename "$skill_dir")
  31. skill_file="${skill_dir}SKILL.md"
  32. if [ -f "$skill_file" ]; then
  33. # Extract description from frontmatter
  34. description=$(grep -m1 '^description:' "$skill_file" 2>/dev/null | sed 's/^description: *//;s/^"//;s/"$//' || echo "")
  35. if [ -n "$description" ]; then
  36. skill_catalogue="${skill_catalogue}"$'\n'"- oac:${skill_name} — ${description}"
  37. else
  38. skill_catalogue="${skill_catalogue}"$'\n'"- oac:${skill_name}"
  39. fi
  40. fi
  41. done
  42. fi
  43. # Build warning message for first-time users
  44. warning_message=""
  45. if [[ ! -f "${PLUGIN_ROOT}/.context-manifest.json" ]] && [[ ! -f "$(pwd)/.context-manifest.json" ]]; then
  46. 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 install-context 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 install-context skill.</important-reminder>"
  47. fi
  48. warning_escaped=$(escape_for_json "$warning_message")
  49. skill_catalogue_escaped=$(escape_for_json "$skill_catalogue")
  50. # Build OAC system paths block
  51. PROTOCOL_PATH="${PLUGIN_ROOT}/skills/context-discovery/context-discovery-protocol.md"
  52. plugin_root_escaped=$(escape_for_json "$PLUGIN_ROOT")
  53. protocol_path_escaped=$(escape_for_json "$PROTOCOL_PATH")
  54. OAC_SYSTEM_PATHS="## OAC System Paths\n- Plugin Root: ${plugin_root_escaped}\n- Context Discovery Protocol: ${protocol_path_escaped}"
  55. # Build context string
  56. OAC_CONTEXT="<EXTREMELY_IMPORTANT>\nYou have OAC (OpenAgents Control).\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${warning_escaped}\n</EXTREMELY_IMPORTANT>"
  57. # Output dual-format JSON for cross-tool compatibility
  58. # - additionalContext: Claude Code (hookSpecificOutput)
  59. # - additional_context: Cursor / OpenCode / other tools
  60. cat <<EOF
  61. {
  62. "additional_context": "${OAC_CONTEXT}",
  63. "hookSpecificOutput": {
  64. "hookEventName": "SessionStart",
  65. "additionalContext": "${OAC_CONTEXT}"
  66. }
  67. }
  68. EOF
  69. exit 0