skills/bin/claude-search
dan 7e3ba83101 feat: add claude-search prototype for conversation history
- Indexes all Claude Code sessions (summaries + metadata)
- Searches by keyword in summaries and first message
- Shows date, project, message count, uuid
- Usage: claude-search <query> or --rebuild
2025-12-29 18:35:50 -05:00

93 lines
3 KiB
Bash
Executable file

#!/usr/bin/env bash
# Search Claude Code conversation history
# Usage: claude-search <query> [--rebuild]
set -euo pipefail
CLAUDE_DIR="${HOME}/.claude"
INDEX_FILE="${CLAUDE_DIR}/search-index.jsonl"
build_index() {
echo "Building index..." >&2
local tmp_index
tmp_index=$(mktemp)
for project_dir in "$CLAUDE_DIR"/projects/*/; do
[[ -d "$project_dir" ]] || continue
project_name=$(basename "$project_dir")
for session_file in "$project_dir"/*.jsonl; do
[[ -f "$session_file" ]] || continue
[[ $(basename "$session_file") == agent-* ]] && continue
uuid=$(basename "$session_file" .jsonl)
# Extract summaries
summaries=$(jq -rs '[.[] | select(.type == "summary") | .summary] | join(" | ")' "$session_file" 2>/dev/null || echo "")
# Count messages
msg_count=$(jq -s '[.[] | select(.type == "user" or .type == "assistant")] | length' "$session_file" 2>/dev/null || echo "0")
# Get timestamps
first_ts=$(jq -rs '[.[] | select(.timestamp) | .timestamp] | first // ""' "$session_file" 2>/dev/null || echo "")
# Get first user message preview
preview=$(jq -rs '[.[] | select(.type == "user") | .message.content][0] // ""' "$session_file" 2>/dev/null | head -c 100 || echo "")
# Skip empty sessions
[[ -z "$first_ts" ]] && continue
# Output index entry (compact JSON, one per line)
jq -cn \
--arg uuid "$uuid" \
--arg project "$project_name" \
--arg summaries "$summaries" \
--arg preview "$preview" \
--arg first_ts "$first_ts" \
--argjson msg_count "$msg_count" \
'{uuid: $uuid, project: $project, summaries: $summaries, preview: $preview, date: $first_ts, messages: $msg_count}' >> "$tmp_index"
done
done
mv "$tmp_index" "$INDEX_FILE"
echo "Index built: $(wc -l < "$INDEX_FILE") sessions" >&2
}
search_index() {
local query="$1"
if [[ ! -f "$INDEX_FILE" ]]; then
echo "Index not found. Building..." >&2
build_index
fi
# Search summaries and preview (case-insensitive)
grep -i "$query" "$INDEX_FILE" 2>/dev/null | jq -r '
"\(.date | split("T")[0]) [\(.project | ltrimstr("-home-dan-proj-"))] \(.messages)msg"
+ "\n " + (if .summaries != "" then .summaries else .preview end)
+ "\n " + .uuid + "\n"
' | head -50
}
case "${1:-}" in
--rebuild)
build_index
;;
--help|-h)
echo "Usage: claude-search <query> [--rebuild]"
echo ""
echo "Search Claude Code conversation history"
echo ""
echo "Options:"
echo " --rebuild Rebuild the search index"
echo " --help Show this help"
;;
"")
echo "Usage: claude-search <query>" >&2
exit 1
;;
*)
search_index "$1"
;;
esac