Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 34 additions & 25 deletions hooks/session-start.sh
Original file line number Diff line number Diff line change
@@ -1,51 +1,60 @@
#!/usr/bin/env bash
# SessionStart hook for superpowers plugin
# SessionStart hook for superpowers plugin - SAFE VERSION

set -euo pipefail

# Set SUPERPOWERS_SKILLS_ROOT environment variable
export SUPERPOWERS_SKILLS_ROOT="${HOME}/.config/superpowers/skills"

# Run skills initialization script (handles clone/fetch/auto-update)
# Run skills initialization script with timeout (handles clone/fetch/auto-update)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
init_output=$("${PLUGIN_ROOT}/lib/initialize-skills.sh" 2>&1 || echo "")
init_output=$(timeout 10 "${PLUGIN_ROOT}/lib/initialize-skills.sh" 2>&1 || echo "")

# Extract status flags
skills_updated=$(echo "$init_output" | grep "SKILLS_UPDATED=true" || echo "")
skills_behind=$(echo "$init_output" | grep "SKILLS_BEHIND=true" || echo "")
# Remove status flags from display output
init_output=$(echo "$init_output" | grep -v "SKILLS_UPDATED=true" | grep -v "SKILLS_BEHIND=true")
# Extract status flags with timeout protection
skills_updated=$(timeout 1 bash -c "echo '$init_output' | grep 'SKILLS_UPDATED=true'" 2>/dev/null || echo "")
skills_behind=$(timeout 1 bash -c "echo '$init_output' | grep 'SKILLS_BEHIND=true'" 2>/dev/null || echo "")
Comment on lines +15 to +16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix unsafe quoting when scanning init_output.

Embedding $init_output inside bash -c "echo '...'" breaks as soon as the init script emits a single quote (or other shell metacharacters), producing a syntax error and silently disabling the updated/behind detection. Use grep directly on the buffered output instead of re-invoking a shell.

Apply this diff:

-skills_updated=$(timeout 1 bash -c "echo '$init_output' | grep 'SKILLS_UPDATED=true'" 2>/dev/null || echo "")
-skills_behind=$(timeout 1 bash -c "echo '$init_output' | grep 'SKILLS_BEHIND=true'" 2>/dev/null || echo "")
+skills_updated=$(timeout 1 grep -F 'SKILLS_UPDATED=true' <<<"$init_output" || true)
+skills_behind=$(timeout 1 grep -F 'SKILLS_BEHIND=true' <<<"$init_output" || true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
skills_updated=$(timeout 1 bash -c "echo '$init_output' | grep 'SKILLS_UPDATED=true'" 2>/dev/null || echo "")
skills_behind=$(timeout 1 bash -c "echo '$init_output' | grep 'SKILLS_BEHIND=true'" 2>/dev/null || echo "")
-skills_updated=$(timeout 1 bash -c "echo '$init_output' | grep 'SKILLS_UPDATED=true'" 2>/dev/null || echo "")
skills_updated=$(timeout 1 grep -F 'SKILLS_UPDATED=true' <<<"$init_output" || true)
skills_behind=$(timeout 1 grep -F 'SKILLS_BEHIND=true' <<<"$init_output" || true)
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 15-15: skills_updated appears unused. Verify use (or export if used externally).

(SC2034)


# Run find-skills to show all available skills
find_skills_output=$("${SUPERPOWERS_SKILLS_ROOT}/skills/using-skills/find-skills" 2>&1 || echo "Error running find-skills")
# Run find-skills with timeout to show all available skills
find_skills_output=$(timeout 5 "${SUPERPOWERS_SKILLS_ROOT}/skills/using-skills/find-skills" </dev/null 2>&1 || echo "⚠️ find-skills timed out")

# Read using-skills content (renamed from getting-started)
using_skills_content=$(cat "${SUPERPOWERS_SKILLS_ROOT}/skills/using-skills/SKILL.md" 2>&1 || echo "Error reading using-skills")
# Read using-skills content
using_skills_content=$(timeout 2 cat "${SUPERPOWERS_SKILLS_ROOT}/skills/using-skills/SKILL.md" 2>&1 || echo "Error reading using-skills")

# Escape outputs for JSON
init_escaped=$(echo "$init_output" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}')
find_skills_escaped=$(echo "$find_skills_output" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}')
using_skills_escaped=$(echo "$using_skills_content" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}')

# Build initialization output message if present
init_message=""
if [ -n "$init_escaped" ]; then
init_message="${init_escaped}\n\n"
fi

# Build status messages that go at the end
# Build status message
status_message=""
if [ -n "$skills_behind" ]; then
status_message="\n\n⚠️ New skills available from upstream. Ask me to use the pulling-updates-from-skills-repository skill."
fi

# Build the full message
full_context="<EXTREMELY_IMPORTANT>
You have superpowers.

**The content below is from skills/using-skills/SKILL.md - your introduction to using skills:**

${using_skills_content}

**Tool paths (use these when you need to search for or run skills):**
- find-skills: ${SUPERPOWERS_SKILLS_ROOT}/skills/using-skills/find-skills
- skill-run: ${SUPERPOWERS_SKILLS_ROOT}/skills/using-skills/skill-run

**Skills live in:** ${SUPERPOWERS_SKILLS_ROOT}/skills/ (you work on your own branch and can edit any skill)

**Available skills (output of find-skills):**

${find_skills_output}${status_message}
</EXTREMELY_IMPORTANT>"

# Use jq to properly escape for JSON
escaped_context=$(echo "$full_context" | jq -Rs .)

# Output context injection as JSON
cat <<EOF
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n${init_message}**The content below is from skills/using-skills/SKILL.md - your introduction to using skills:**\n\n${using_skills_escaped}\n\n**Tool paths (use these when you need to search for or run skills):**\n- find-skills: ${SUPERPOWERS_SKILLS_ROOT}/skills/using-skills/find-skills\n- skill-run: ${SUPERPOWERS_SKILLS_ROOT}/skills/using-skills/skill-run\n\n**Skills live in:** ${SUPERPOWERS_SKILLS_ROOT}/skills/ (you work on your own branch and can edit any skill)\n\n**Available skills (output of find-skills):**\n\n${find_skills_escaped}${status_message}\n</EXTREMELY_IMPORTANT>"
"additionalContext": ${escaped_context}
}
}
EOF
Expand Down