session-start.sh 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #!/bin/bash
  2. # SessionStart hook for OAC plugin
  3. # Injects using-oac skill and checks .tmp cleanup, .oac config, and context installation
  4. set -euo pipefail
  5. # Resolve plugin root directory
  6. PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
  7. SKILL_FILE="${PLUGIN_ROOT}/skills/using-oac/SKILL.md"
  8. TMP_SESSIONS_DIR=".tmp/sessions"
  9. CONTEXT_MANIFEST=".context-manifest.json"
  10. OAC_EXAMPLE="${PLUGIN_ROOT}/.oac.example"
  11. # Load using-oac skill content
  12. if [[ ! -f "${SKILL_FILE}" ]]; then
  13. echo '{"error": "using-oac skill not found at '"${SKILL_FILE}"'"}' >&2
  14. exit 1
  15. fi
  16. SKILL_CONTENT=$(cat "${SKILL_FILE}")
  17. # Escape for JSON using parameter substitution (fast method)
  18. # Replace backslash, double-quote, newline, tab, carriage return
  19. SKILL_CONTENT="${SKILL_CONTENT//\\/\\\\}" # Backslash
  20. SKILL_CONTENT="${SKILL_CONTENT//\"/\\\"}" # Double-quote
  21. SKILL_CONTENT="${SKILL_CONTENT//$'\n'/\\n}" # Newline
  22. SKILL_CONTENT="${SKILL_CONTENT//$'\t'/\\t}" # Tab
  23. SKILL_CONTENT="${SKILL_CONTENT//$'\r'/\\r}" # Carriage return
  24. # Check for .oac configuration
  25. CONFIG_EXISTS=false
  26. CONFIG_LOCATION=""
  27. RECOMMENDATIONS=()
  28. if [[ -f ".oac" ]]; then
  29. CONFIG_EXISTS=true
  30. CONFIG_LOCATION="project (.oac)"
  31. elif [[ -f "$HOME/.oac" ]]; then
  32. CONFIG_EXISTS=true
  33. CONFIG_LOCATION="global (~/.oac)"
  34. else
  35. RECOMMENDATIONS+=("Copy .oac.example to .oac to customize settings")
  36. fi
  37. # Check for context installation
  38. CONTEXT_INSTALLED=false
  39. CONTEXT_AGE_DAYS=0
  40. CONTEXT_WARNING=""
  41. if [[ -f "${CONTEXT_MANIFEST}" ]]; then
  42. CONTEXT_INSTALLED=true
  43. # Check age of context manifest (>30 days = suggest update)
  44. if [[ "$(uname)" == "Darwin" ]]; then
  45. # macOS
  46. MANIFEST_MTIME=$(stat -f %m "${CONTEXT_MANIFEST}" 2>/dev/null || echo 0)
  47. else
  48. # Linux
  49. MANIFEST_MTIME=$(stat -c %Y "${CONTEXT_MANIFEST}" 2>/dev/null || echo 0)
  50. fi
  51. CURRENT_TIME=$(date +%s)
  52. CONTEXT_AGE_DAYS=$(( (CURRENT_TIME - MANIFEST_MTIME) / 86400 ))
  53. if [[ ${CONTEXT_AGE_DAYS} -gt 30 ]]; then
  54. CONTEXT_WARNING="⚠️ Context is ${CONTEXT_AGE_DAYS} days old. Consider running /oac:setup --core to update."
  55. RECOMMENDATIONS+=("Run /oac:setup --core to update context (${CONTEXT_AGE_DAYS} days old)")
  56. fi
  57. else
  58. RECOMMENDATIONS+=("Run /oac:setup --core to download context files")
  59. fi
  60. # Detect first-time user
  61. FIRST_TIME_USER=false
  62. if [[ "${CONFIG_EXISTS}" == "false" ]] && [[ "${CONTEXT_INSTALLED}" == "false" ]]; then
  63. FIRST_TIME_USER=true
  64. fi
  65. # Check .tmp/ cleanup needs
  66. CLEANUP_SCRIPT="${PLUGIN_ROOT}/scripts/cleanup-tmp.sh"
  67. CLEANUP_WARNING=""
  68. if [[ -x "${CLEANUP_SCRIPT}" ]]; then
  69. # Check for old .tmp files
  70. OLD_SESSIONS=0
  71. OLD_TASKS=0
  72. OLD_EXTERNAL=0
  73. # Count old sessions (>7 days)
  74. if [[ -d ".tmp/sessions" ]]; then
  75. OLD_SESSIONS=$(find ".tmp/sessions" -mindepth 1 -maxdepth 1 -type d -mtime +7 2>/dev/null | wc -l | tr -d ' ')
  76. fi
  77. # Count old completed tasks (>30 days)
  78. if [[ -d ".tmp/tasks" ]]; then
  79. for task_dir in .tmp/tasks/*; do
  80. if [[ ! -d "$task_dir" ]]; then continue; fi
  81. all_completed=true
  82. has_subtasks=false
  83. for subtask_file in "$task_dir"/subtask_*.json; do
  84. if [[ ! -f "$subtask_file" ]]; then continue; fi
  85. has_subtasks=true
  86. if ! grep -q '"status": "completed"' "$subtask_file" 2>/dev/null; then
  87. all_completed=false
  88. break
  89. fi
  90. if [[ $(find "$subtask_file" -mtime +30 2>/dev/null | wc -l) -eq 0 ]]; then
  91. all_completed=false
  92. break
  93. fi
  94. done
  95. if [[ "$has_subtasks" == "true" ]] && [[ "$all_completed" == "true" ]]; then
  96. ((OLD_TASKS++))
  97. fi
  98. done
  99. fi
  100. # Count old external context (>7 days)
  101. if [[ -d ".tmp/external-context" ]]; then
  102. OLD_EXTERNAL=$(find ".tmp/external-context" -mindepth 1 -maxdepth 1 -type d -mtime +7 2>/dev/null | wc -l | tr -d ' ')
  103. fi
  104. TOTAL_OLD=$((OLD_SESSIONS + OLD_TASKS + OLD_EXTERNAL))
  105. if [[ ${TOTAL_OLD} -gt 0 ]]; then
  106. 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}"
  107. RECOMMENDATIONS+=("Run cleanup script to remove old temporary files")
  108. fi
  109. fi
  110. # Build context injection message
  111. CONTEXT_MESSAGE="# OpenAgents Control (OAC) Workflow
  112. **EXTREMELY_IMPORTANT**: This skill is automatically loaded for every development task.
  113. ${SKILL_CONTENT}"
  114. # Add first-time user welcome
  115. if [[ "${FIRST_TIME_USER}" == "true" ]]; then
  116. CONTEXT_MESSAGE="${CONTEXT_MESSAGE}
  117. ---
  118. ## 👋 Welcome to OpenAgents Control!
  119. This appears to be your first time using OAC. Here's how to get started:
  120. 1. **Download context files**: Run \`/oac:setup --core\` to get coding standards and patterns
  121. 2. **Customize settings**: Copy \`.oac.example\` to \`.oac\` to configure your preferences
  122. 3. **Learn the workflow**: Run \`/oac:help\` to see the 6-stage development workflow
  123. Once context is installed, OAC will automatically discover relevant standards and patterns for every task."
  124. fi
  125. # Add context warning if needed
  126. if [[ -n "${CONTEXT_WARNING}" ]]; then
  127. CONTEXT_MESSAGE="${CONTEXT_MESSAGE}
  128. ---
  129. ${CONTEXT_WARNING}"
  130. fi
  131. # Add cleanup warning if needed
  132. if [[ -n "${CLEANUP_WARNING}" ]]; then
  133. CONTEXT_MESSAGE="${CONTEXT_MESSAGE}
  134. ---
  135. ${CLEANUP_WARNING}"
  136. fi
  137. # Escape context message for JSON
  138. CONTEXT_MESSAGE="${CONTEXT_MESSAGE//\\/\\\\}"
  139. CONTEXT_MESSAGE="${CONTEXT_MESSAGE//\"/\\\"}"
  140. CONTEXT_MESSAGE="${CONTEXT_MESSAGE//$'\n'/\\n}"
  141. CONTEXT_MESSAGE="${CONTEXT_MESSAGE//$'\t'/\\t}"
  142. CONTEXT_MESSAGE="${CONTEXT_MESSAGE//$'\r'/\\r}"
  143. # Build recommendations JSON array
  144. RECOMMENDATIONS_JSON="["
  145. FIRST_REC=true
  146. for rec in "${RECOMMENDATIONS[@]}"; do
  147. if [[ "${FIRST_REC}" == "true" ]]; then
  148. FIRST_REC=false
  149. else
  150. RECOMMENDATIONS_JSON="${RECOMMENDATIONS_JSON},"
  151. fi
  152. # Escape recommendation for JSON
  153. REC_ESCAPED="${rec//\\/\\\\}"
  154. REC_ESCAPED="${REC_ESCAPED//\"/\\\"}"
  155. RECOMMENDATIONS_JSON="${RECOMMENDATIONS_JSON}\"${REC_ESCAPED}\""
  156. done
  157. RECOMMENDATIONS_JSON="${RECOMMENDATIONS_JSON}]"
  158. # Output JSON with hookSpecificOutput format
  159. cat <<EOF
  160. {
  161. "hookSpecificOutput": {
  162. "hookEventName": "SessionStart",
  163. "additionalContext": "<EXTREMELY_IMPORTANT>OAC workflow is active. Follow 6-stage process: Analyze, Plan & Approve, LoadContext, Execute, Validate, Complete.</EXTREMELY_IMPORTANT>",
  164. "firstTimeUser": ${FIRST_TIME_USER},
  165. "contextInstalled": ${CONTEXT_INSTALLED},
  166. "contextAgeDays": ${CONTEXT_AGE_DAYS},
  167. "configExists": ${CONFIG_EXISTS},
  168. "configLocation": "${CONFIG_LOCATION}",
  169. "recommendations": ${RECOMMENDATIONS_JSON},
  170. "contextInjection": {
  171. "priority": "EXTREMELY_IMPORTANT",
  172. "content": "${CONTEXT_MESSAGE}"
  173. },
  174. "tmpCleanup": {
  175. "oldSessions": ${OLD_SESSIONS:-0},
  176. "oldTasks": ${OLD_TASKS:-0},
  177. "oldExternal": ${OLD_EXTERNAL:-0},
  178. "total": ${TOTAL_OLD:-0},
  179. "warning": "${CLEANUP_WARNING}"
  180. }
  181. }
  182. }
  183. EOF