Community Skills (32): - jat: jat-start, jat-verify, jat-complete - pi-mono: codex-cli, codex-5.3-prompting, interactive-shell - picoclaw: github, weather, tmux, summarize, skill-creator - dyad: 18 skills (swarm-to-plan, multi-pr-review, fix-issue, lint, etc.) - dexter: dcf valuation skill Agents (23): - pi-mono subagents: scout, planner, reviewer, worker - toad: 19 agent configs (Claude, Codex, Gemini, Copilot, OpenCode, etc.) System Prompts (91): - Anthropic: 15 Claude prompts (opus-4.6, code, cowork, etc.) - OpenAI: 49 GPT prompts (gpt-5 series, o3, o4-mini, tools) - Google: 13 Gemini prompts (2.5-pro, 3-pro, workspace, cli) - xAI: 5 Grok prompts - Other: 9 misc prompts (Notion, Raycast, Warp, Kagi, etc.) Hooks (9): - JAT hooks for session management, signal tracking, activity logging Prompts (6): - pi-mono templates for PR review, issue analysis, changelog audit Sources analyzed: jat, ralph-desktop, toad, pi-mono, cmux, pi-interactive-shell, craft-agents-oss, dexter, picoclaw, dyad, system_prompts_leaks, Prometheus, zed, clawdbot, OS-Copilot, and more
127 lines
4.8 KiB
Bash
127 lines
4.8 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# pre-ask-user-question.sh - Claude PreToolUse hook for AskUserQuestion
|
|
#
|
|
# This hook captures the question data BEFORE the user answers,
|
|
# writing it to a temp file for the IDE to display.
|
|
#
|
|
# PreToolUse is required because PostToolUse runs after the user
|
|
# has already answered, making the question data irrelevant.
|
|
|
|
set -euo pipefail
|
|
|
|
# Read tool info from stdin
|
|
TOOL_INFO=$(cat)
|
|
|
|
# Extract session ID from hook data
|
|
SESSION_ID=$(echo "$TOOL_INFO" | jq -r '.session_id // ""' 2>/dev/null || echo "")
|
|
|
|
if [[ -z "$SESSION_ID" ]]; then
|
|
exit 0 # Can't determine session, skip
|
|
fi
|
|
|
|
# Get tmux session name - try multiple methods
|
|
TMUX_SESSION=""
|
|
# Method 1: From TMUX env var (may not be passed to hook subprocess)
|
|
if [[ -n "${TMUX:-}" ]]; then
|
|
TMUX_SESSION=$(tmux display-message -p '#S' 2>/dev/null || echo "")
|
|
fi
|
|
# Method 2: From agent session file (more reliable)
|
|
if [[ -z "$TMUX_SESSION" ]]; then
|
|
# Build list of directories to search: current dir + configured projects
|
|
SEARCH_DIRS="."
|
|
JAT_CONFIG="$HOME/.config/jat/projects.json"
|
|
if [[ -f "$JAT_CONFIG" ]]; then
|
|
PROJECT_PATHS=$(jq -r '.projects[].path // empty' "$JAT_CONFIG" 2>/dev/null | sed "s|^~|$HOME|g")
|
|
for PROJECT_PATH in $PROJECT_PATHS; do
|
|
if [[ -d "${PROJECT_PATH}/.claude" ]]; then
|
|
SEARCH_DIRS="$SEARCH_DIRS $PROJECT_PATH"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Check both .claude/sessions/agent-{id}.txt (current) and .claude/agent-{id}.txt (legacy)
|
|
for BASE_DIR in $SEARCH_DIRS; do
|
|
for SUBDIR in "sessions" ""; do
|
|
if [[ -n "$SUBDIR" ]]; then
|
|
AGENT_FILE="${BASE_DIR}/.claude/${SUBDIR}/agent-${SESSION_ID}.txt"
|
|
else
|
|
AGENT_FILE="${BASE_DIR}/.claude/agent-${SESSION_ID}.txt"
|
|
fi
|
|
if [[ -f "$AGENT_FILE" ]]; then
|
|
AGENT_NAME=$(cat "$AGENT_FILE" 2>/dev/null | tr -d '\n')
|
|
if [[ -n "$AGENT_NAME" ]]; then
|
|
TMUX_SESSION="jat-${AGENT_NAME}"
|
|
break 2
|
|
fi
|
|
fi
|
|
done
|
|
done
|
|
fi
|
|
|
|
# Build question data JSON
|
|
QUESTION_DATA=$(echo "$TOOL_INFO" | jq -c --arg tmux "$TMUX_SESSION" '{
|
|
session_id: .session_id,
|
|
tmux_session: $tmux,
|
|
timestamp: (now | todate),
|
|
questions: .tool_input.questions
|
|
}' 2>/dev/null || echo "{}")
|
|
|
|
# Write to session ID file
|
|
QUESTION_FILE="/tmp/claude-question-${SESSION_ID}.json"
|
|
echo "$QUESTION_DATA" > "$QUESTION_FILE" 2>/dev/null || true
|
|
|
|
# Also write to tmux session name file for easy IDE lookup
|
|
if [[ -n "$TMUX_SESSION" ]]; then
|
|
TMUX_QUESTION_FILE="/tmp/claude-question-tmux-${TMUX_SESSION}.json"
|
|
echo "$QUESTION_DATA" > "$TMUX_QUESTION_FILE" 2>/dev/null || true
|
|
fi
|
|
|
|
# Also emit a needs_input signal so the IDE transitions to needs-input state
|
|
# This triggers the question polling in SessionCard
|
|
if [[ -n "$TMUX_SESSION" ]]; then
|
|
# Extract the first question text for the signal
|
|
QUESTION_TEXT=$(echo "$TOOL_INFO" | jq -r '.tool_input.questions[0].question // "Question from agent"' 2>/dev/null || echo "Question from agent")
|
|
QUESTION_TYPE=$(echo "$TOOL_INFO" | jq -r 'if .tool_input.questions[0].multiSelect then "multi-select" else "choice" end' 2>/dev/null || echo "choice")
|
|
|
|
# Get current task ID from JAT Tasks if available
|
|
TASK_ID=""
|
|
if command -v jt &>/dev/null && [[ -n "$AGENT_NAME" ]]; then
|
|
TASK_ID=$(jt list --json 2>/dev/null | jq -r --arg agent "$AGENT_NAME" '.[] | select(.assignee == $agent and .status == "in_progress") | .id' 2>/dev/null | head -1 || echo "")
|
|
fi
|
|
|
|
# Build signal data - use type: "state" and state: "needs_input"
|
|
# This matches the format expected by the SSE server in +server.ts
|
|
# which maps signal states using SIGNAL_STATE_MAP (needs_input -> needs-input)
|
|
SIGNAL_DATA=$(jq -n -c \
|
|
--arg state "needs_input" \
|
|
--arg session_id "$SESSION_ID" \
|
|
--arg tmux "$TMUX_SESSION" \
|
|
--arg task_id "$TASK_ID" \
|
|
--arg question "$QUESTION_TEXT" \
|
|
--arg question_type "$QUESTION_TYPE" \
|
|
'{
|
|
type: "state",
|
|
state: $state,
|
|
session_id: $session_id,
|
|
tmux_session: $tmux,
|
|
timestamp: (now | todate),
|
|
task_id: $task_id,
|
|
data: {
|
|
taskId: $task_id,
|
|
question: $question,
|
|
questionType: $question_type
|
|
}
|
|
}' 2>/dev/null || echo "{}")
|
|
|
|
# Write signal files
|
|
echo "$SIGNAL_DATA" > "/tmp/jat-signal-${SESSION_ID}.json" 2>/dev/null || true
|
|
echo "$SIGNAL_DATA" > "/tmp/jat-signal-tmux-${TMUX_SESSION}.json" 2>/dev/null || true
|
|
|
|
# Also append to timeline for history tracking (JSONL format)
|
|
TIMELINE_FILE="/tmp/jat-timeline-${TMUX_SESSION}.jsonl"
|
|
echo "$SIGNAL_DATA" >> "$TIMELINE_FILE" 2>/dev/null || true
|
|
fi
|
|
|
|
exit 0
|