Skill: nadalpiantini/pre-compact-resume-card (version 1.0.0)

--- name: pre-compact-resume-card description: Generates an operational resume card before Claude context compaction — so the next session knows exactly where it left off, what mode it was in, and what to do next. Install the PreCompact and SessionStart hooks to activate. license: MIT --- # Pre-Compact Resume Card Claude forgets when context compacts. This skill installs a PreCompact hook that captures real operational state before compaction fires — branch, commit, last user intent, last assistant action, detected mode, and next step. The next session loads it automatically via SessionStart. ## What It Does Before every context compaction, the hook: 1. Backs up the transcript to `thinking/session-logs/` (keeps last 30) 2. Parses the JSONL transcript to extract the last user intent and assistant action 3. Detects the current work mode (coding / debugging / deploy / ideation / git workflow) 4. Writes `.claude/session-resume-card.md` with all of this At session start, the card is injected into context automatically — no manual copy-paste needed. ## Install ```bash npx clawhub@latest install @openclaw/pre-compact-resume-card ``` Or manually: copy `scripts/pre-compact.sh` and `scripts/session-start.sh` into your project's `.claude/scripts/`, then register the hooks in `.claude/settings.json` using `references/settings-example.json` as template. ## Hook Registration ```json { "hooks": { "PreCompact": [{ "matcher": "", "hooks": [{ "type": "command", "command": "bash .claude/scripts/pre-compact.sh" }] }], "SessionStart": [{ "matcher": "", "hooks": [{ "type": "command", "command": "bash .claude/scripts/session-start.sh" }] }] } } ``` ## The Insight Written rules get forgotten. Mechanical hooks don't. This skill is the difference between Claude starting fresh (useless) and Claude starting exactly where you left off (valuable). The resume card costs ~200 tokens per session start — cheap insurance against rebuilding context from scratch. ## Files - `scripts/pre-compact.sh` — fires before compaction: backup + parse transcript + write resume card - `scripts/session-start.sh` — inject the resume card at session start - `references/settings-example.json` — hook registration template

path: references/settings-example.json
content: {
  "hooks": {
    "PreCompact": [{ "matcher": "", "hooks": [{ "type": "command", "command": "bash .claude/scripts/pre-compact.sh" }] }],
    "SessionStart": [{ "matcher": "", "hooks": [{ "type": "command", "command": "bash .claude/scripts/session-start.sh" }] }]
  }
}


path: scripts/pre-compact.sh
content: #!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"

INPUT=$(cat)

TRANSCRIPT_PATH=$(echo "$INPUT" | python3 -c "
import json, sys
try:
    d = json.load(sys.stdin)
    print(d.get('transcript_path', ''))
except:
    print('')
" 2>/dev/null || echo "")

TRIGGER=$(echo "$INPUT" | python3 -c "
import json, sys
try:
    d = json.load(sys.stdin)
    print(d.get('trigger', 'unknown'))
except:
    print('unknown')
" 2>/dev/null || echo "unknown")

# --- Backup existente (sin cambios) ---
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
  BACKUP_DIR="${PROJECT_DIR}/thinking/session-logs"
  mkdir -p "$BACKUP_DIR"
  TIMESTAMP=$(date +%Y%m%d_%H%M%S)
  cp "$TRANSCRIPT_PATH" "$BACKUP_DIR/session_${TRIGGER}_${TIMESTAMP}.jsonl"
  ls -t "$BACKUP_DIR"/session_*.jsonl 2>/dev/null | tail -n +31 | xargs rm -f 2>/dev/null || true
fi

# --- Resume Card: estado operativo real ---
OUT="${PROJECT_DIR}/.claude/session-resume-card.md"
TRANSCRIPT="$TRANSCRIPT_PATH"

if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
  # fallback: buscar el más reciente en .claude/
  TRANSCRIPT=$(ls -t "${PROJECT_DIR}/.claude/"*.jsonl 2>/dev/null | head -n 1 || echo "")
fi

{
  echo "## Resume Card — $(date '+%Y-%m-%d %H:%M')"
  echo ""

  # Proyecto activo
  echo "### Proyecto activo"
  BRANCH=$(git -C "$PROJECT_DIR" branch --show-current 2>/dev/null || echo "N/A")
  COMMIT=$(git -C "$PROJECT_DIR" rev-parse --short HEAD 2>/dev/null || echo "N/A")
  echo "branch: $BRANCH"
  echo "commit: $COMMIT"
  echo ""

  # Estado git
  echo "### Cambios en curso"
  git -C "$PROJECT_DIR" status --short 2>/dev/null | head -n 8 || echo "(limpio)"
  echo ""

  # Última intención + última acción (extraídas del transcript con Python)
  if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then
    python3 - "$TRANSCRIPT" <<'PYEOF'
import sys, json

path = sys.argv[1]
messages = []
try:
    with open(path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                obj = json.loads(line)
                if isinstance(obj, dict) and obj.get('role') in ('user', 'assistant'):
                    messages.append(obj)
            except:
                pass
except:
    pass

def extract_text(msg):
    c = msg.get('content', '')
    if isinstance(c, str):
        return c[:200]
    if isinstance(c, list):
        for block in c:
            if isinstance(block, dict) and block.get('type') == 'text':
                return block.get('text', '')[:200]
    return ''

last_user = next((extract_text(m) for m in reversed(messages) if m['role'] == 'user'), 'N/A')
last_asst = next((extract_text(m) for m in reversed(messages) if m['role'] == 'assistant'), 'N/A')

print("### Última intención (user)")
print(last_user.replace('\n', ' ').strip())
print("")
print("### Última acción (assistant)")
print(last_asst.replace('\n', ' ').strip())
print("")

# Modo: heurística sobre texto combinado
combined = (last_user + " " + last_asst).lower()
mode = "coding"
if any(w in combined for w in ["error", "fail", "bug", "roto", "arregla", "fix"]):
    mode = "debugging"
elif any(w in combined for w in ["deploy", "vercel", "railway", "push", "produccion"]):
    mode = "deploy"
elif any(w in combined for w in ["idea", "brainstorm", "diseña", "concepto", "que opinas"]):
    mode = "ideation"
elif any(w in combined for w in ["commit", "git", "merge", "branch"]):
    mode = "git workflow"

print("### Modo")
print(mode)
print("")
PYEOF
  else
    echo "### Última intención (user)"
    echo "N/A (transcript no disponible)"
    echo ""
    echo "### Última acción (assistant)"
    echo "N/A"
    echo ""
    echo "### Modo"
    echo "unknown"
    echo ""
  fi

  # Next step
  echo "### Next step"
  if [ -f "${PROJECT_DIR}/.claude/current-task.md" ]; then
    head -n 3 "${PROJECT_DIR}/.claude/current-task.md"
  else
    echo "Continuar última acción del assistant"
  fi

} > "$OUT" 2>/dev/null || true

exit 0


path: scripts/session-start.sh
content: #!/bin/bash
set -eo pipefail
cd "${CLAUDE_PROJECT_DIR:-$(pwd)}"

echo "## Session Context"
echo ""
echo "### Date"
echo "$(date +%Y-%m-%d) ($(date +%A))"
echo ""

echo "### Recent Changes (last 48h)"
git log --oneline --since="48 hours ago" --no-merges 2>/dev/null | head -15 || echo "(no git history)"
echo ""

echo "### Active Packages"
ls -d packages/*/ 2>/dev/null | sed 's|packages/||;s|/$||' | head -15 || echo "(none)"
echo ""

echo "### Active Apps"
ls -d apps/*/ 2>/dev/null | sed 's|apps/||;s|/$||' | head -10 || echo "(none)"
echo ""

echo "### Brain Notes"
ls brain/*.md 2>/dev/null | sed 's|brain/||' || echo "(brain/ not found)"
echo ""

echo "### Open Tasks (memory index)"
head -20 memory/MEMORY.md 2>/dev/null || echo "(no memory index)"
echo ""

# Resume Card: inyectar estado pre-compaction si existe
if [ -f .claude/session-resume-card.md ]; then
  echo "### Resume Card (estado pre-compaction)"
  cat .claude/session-resume-card.md
  echo ""
fi

exit 0


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)