Scans all projects for session logs not yet processed by /reflect-logs, using .reflection-state.json to track reflected status. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
153 lines
4.2 KiB
Bash
Executable File
153 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Colours
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
BOLD='\033[1m'
|
|
RESET='\033[0m'
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: unreflected-logs [OPTIONS] [DIRECTORY]
|
|
|
|
Scan project directories for session logs not yet processed by /reflect-logs.
|
|
|
|
Arguments:
|
|
DIRECTORY Root directory to scan (default: current directory)
|
|
|
|
Options:
|
|
-n, --dryrun List discovered projects without checking log status
|
|
-h, --help Show this help message
|
|
USAGE
|
|
}
|
|
|
|
DRYRUN=false
|
|
DIRECTORY="."
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-n|--dryrun) DRYRUN=true; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
-*) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;;
|
|
*) DIRECTORY="$1"; shift ;;
|
|
esac
|
|
done
|
|
|
|
DIRECTORY="$(cd "$DIRECTORY" && pwd)"
|
|
|
|
# Discovery: find directories containing memory/log/
|
|
projects=()
|
|
while IFS= read -r logdir; do
|
|
# logdir is /path/to/project/memory/log
|
|
# project root is two levels up
|
|
project_root="$(dirname "$(dirname "$logdir")")"
|
|
projects+=("$project_root")
|
|
done < <(find "$DIRECTORY" -type d \( -name node_modules -o -path "*/memory/log/*/memory" \) -prune -o -type d -name log -path "*/memory/log" -print 2>/dev/null | sort)
|
|
|
|
if [[ ${#projects[@]} -eq 0 ]]; then
|
|
echo "No projects with session logs found in $DIRECTORY"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Scanned ${#projects[@]} projects in $DIRECTORY"
|
|
echo
|
|
|
|
# Dryrun: just list projects
|
|
if [[ "$DRYRUN" == true ]]; then
|
|
echo "[dryrun] Would check ${#projects[@]} projects:"
|
|
for project in "${projects[@]}"; do
|
|
rel="${project#"$DIRECTORY"/}"
|
|
[[ "$rel" == "$project" ]] && rel="."
|
|
echo " $rel"
|
|
done
|
|
exit 0
|
|
fi
|
|
|
|
# Check phase
|
|
total_unreflected=0
|
|
projects_with_unreflected=0
|
|
projects_all_reflected=0
|
|
|
|
declare -A project_results # project_path -> output string
|
|
|
|
for project in "${projects[@]}"; do
|
|
rel="${project#"$DIRECTORY"/}"
|
|
[[ "$rel" == "$project" ]] && rel="."
|
|
|
|
# List log files
|
|
log_files=()
|
|
while IFS= read -r f; do
|
|
log_files+=("$(basename "$f")")
|
|
done < <(find "$project/memory/log" -maxdepth 1 -name "*.md" -type f 2>/dev/null | sort)
|
|
|
|
# Skip if no logs
|
|
[[ ${#log_files[@]} -eq 0 ]] && continue
|
|
|
|
# Read reflection state
|
|
state_file="$project/.reflection-state.json"
|
|
processed_keys=()
|
|
if [[ -f "$state_file" ]]; then
|
|
# Extract keys from the "processed" object using python for reliable JSON parsing
|
|
if processed_output=$(python3 -c "
|
|
import json, sys
|
|
try:
|
|
with open(sys.argv[1]) as f:
|
|
data = json.load(f)
|
|
for key in data.get('processed', {}):
|
|
print(key)
|
|
except (json.JSONDecodeError, KeyError):
|
|
sys.exit(1)
|
|
" "$state_file" 2>/dev/null); then
|
|
while IFS= read -r key; do
|
|
processed_keys+=("$key")
|
|
done <<< "$processed_output"
|
|
else
|
|
echo " Warning: malformed .reflection-state.json in $rel" >&2
|
|
fi
|
|
fi
|
|
|
|
# Compare: find unreflected logs
|
|
unreflected=()
|
|
for log_file in "${log_files[@]}"; do
|
|
log_key="log/$log_file"
|
|
found=false
|
|
for key in "${processed_keys[@]}"; do
|
|
if [[ "$key" == "$log_key" ]]; then
|
|
found=true
|
|
break
|
|
fi
|
|
done
|
|
if [[ "$found" == false ]]; then
|
|
unreflected+=("$log_file")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#unreflected[@]} -gt 0 ]]; then
|
|
echo -e "── ${BOLD}${rel}${RESET} ──"
|
|
echo -e " ${YELLOW}${#unreflected[@]} unreflected${RESET} / ${#log_files[@]} total logs"
|
|
for u in "${unreflected[@]}"; do
|
|
echo " - $u"
|
|
done
|
|
echo
|
|
total_unreflected=$((total_unreflected + ${#unreflected[@]}))
|
|
projects_with_unreflected=$((projects_with_unreflected + 1))
|
|
else
|
|
projects_all_reflected=$((projects_all_reflected + 1))
|
|
fi
|
|
done
|
|
|
|
# Summary
|
|
if [[ $projects_all_reflected -gt 0 ]]; then
|
|
echo -e "${GREEN}✓${RESET} $projects_all_reflected projects have all logs reflected."
|
|
fi
|
|
|
|
if [[ $total_unreflected -eq 0 ]]; then
|
|
echo -e "\n${GREEN}✓${RESET} All logs reflected across all projects."
|
|
exit 0
|
|
else
|
|
echo -e "\nTotal: ${YELLOW}${total_unreflected} unreflected logs${RESET} across ${projects_with_unreflected} projects"
|
|
exit 1
|
|
fi
|