Skip to main content

summarize-meeting.sh

#!/usr/bin/env bash
set -euo pipefail

# ------------------------------------------------------------
# summarize-meeting.sh
# Post-session summarization on Mac using ollama
# Usage: summarize-meeting.sh [--de|--en] [--game <slug>] [<transcript.txt>]
#        Defaults to English, most recent transcript if none given
# ------------------------------------------------------------

OLLAMA_MODEL="${OLLAMA_MODEL:-qwen2.5:32b}"
BASE="$HOME/Syncthing/TranscriptOMatic/recordings"
META_DIR="$(cd "$(dirname "$0")/../meta" 2>/dev/null && pwd || echo "$HOME/Syncthing/TranscriptOMatic/meta")"
LANG_MODE="en"
TRANSCRIPT=""
GAME_SLUG=""

# ------------------------------------------------------------
# Argument parsing
# ------------------------------------------------------------

while [[ $# -gt 0 ]]; do
  case "$1" in
    --de)     LANG_MODE="de"; shift ;;
    --en)     LANG_MODE="en"; shift ;;
    --game)   GAME_SLUG="$2"; shift 2 ;;
    -*)
      echo "Usage: summarize-meeting [--de|--en] [--game <slug>] [<transcript.txt>]" >&2
      exit 2
      ;;
    *)
      TRANSCRIPT="$1"; shift ;;
  esac
done

# ------------------------------------------------------------
# Find transcript
# ------------------------------------------------------------

if [[ -z "$TRANSCRIPT" ]]; then
  # Prefer normalized transcript; fall back to plain transcript
  TRANSCRIPT="$(ls -t "$BASE"/**/*_normalized.txt 2>/dev/null | head -n1 || true)"
  if [[ -z "$TRANSCRIPT" ]]; then
    TRANSCRIPT="$(ls -t "$BASE"/**/*_transcript.txt 2>/dev/null | head -n1 || true)"
  fi
  if [[ -z "$TRANSCRIPT" ]]; then
    echo "❌ No transcript found in $BASE" >&2
    echo "   Usage: summarize-meeting [--de|--en] [--game <slug>] [<transcript.txt>]" >&2
    exit 1
  fi
fi

if [[ ! -f "$TRANSCRIPT" ]]; then
  echo "❌ File not found: $TRANSCRIPT" >&2
  exit 1
fi

# If a plain transcript was given explicitly, check whether a normalized version exists
if [[ "$TRANSCRIPT" == *_transcript.txt ]]; then
  NORMALIZED="${TRANSCRIPT/_transcript.txt/_transcript_normalized.txt}"
  if [[ -f "$NORMALIZED" ]]; then
    echo "â„šī¸  Using normalized transcript: $NORMALIZED"
    TRANSCRIPT="$NORMALIZED"
  fi
fi

# ------------------------------------------------------------
# Auto-detect game slug from transcript filename if not given
# ------------------------------------------------------------

if [[ -z "$GAME_SLUG" ]]; then
  TRANSCRIPT_BASENAME="$(basename "$TRANSCRIPT")"
  # Try to find a matching meta file by checking if any slug appears in the filename
  for META_FILE in "$META_DIR"/*.yaml; do
    [[ -f "$META_FILE" ]] || continue
    CANDIDATE_SLUG="$(basename "$META_FILE" .yaml)"
    if [[ "$TRANSCRIPT_BASENAME" == *"$CANDIDATE_SLUG"* ]]; then
      GAME_SLUG="$CANDIDATE_SLUG"
      break
    fi
  done
fi

# ------------------------------------------------------------
# Load meta file if available
# ------------------------------------------------------------

META_CONTEXT=""
META_FILE=""

if [[ -n "$GAME_SLUG" ]]; then
  META_FILE="$META_DIR/${GAME_SLUG}.yaml"
  if [[ -f "$META_FILE" ]]; then
    META_CONTEXT="$(cat "$META_FILE")"
    echo "📋 Meta:       $META_FILE"
  else
    echo "âš ī¸  No meta file found for slug '$GAME_SLUG' in $META_DIR" >&2
  fi
fi

# ------------------------------------------------------------
# Output paths
# ------------------------------------------------------------

SESSION="$(dirname "$TRANSCRIPT")"
TRANSCRIPT_BASE="$(basename "$TRANSCRIPT" .txt)"
SUMMARY="$SESSION/${TRANSCRIPT_BASE}_summary.md"

# ------------------------------------------------------------
# Skip if summary already exists (use FORCE=1 to override)
# ------------------------------------------------------------

if [[ -f "$SUMMARY" && "${FORCE:-}" != "1" ]]; then
  echo "â­ī¸  Summary already exists, skipping: $SUMMARY"
  echo "   Use FORCE=1 summarize-meeting to overwrite."
  exit 0
fi

# ------------------------------------------------------------
# Language-specific prompt
# ------------------------------------------------------------

case "$LANG_MODE" in
  en) PROMPT_LANG="Write the summary in English." ;;
  de) PROMPT_LANG="Schreibe die Zusammenfassung auf Deutsch." ;;
esac

echo "📄 Transcript: $TRANSCRIPT"
echo "🤖 Model:      $OLLAMA_MODEL"
echo "đŸ—Ŗī¸  Language:   $LANG_MODE"
echo "📝 Summary:    $SUMMARY"
echo "----"

# ------------------------------------------------------------
# Build prompt
# ------------------------------------------------------------

if [[ -n "$META_CONTEXT" ]]; then
  CONTEXT_BLOCK="You have been provided with a context document for this session (in YAML format).
Use it to:
- Correctly identify and spell character names, player names, locations and in-game terms
- Understand roles and group memberships
- Interpret Irish (ga) and French (fr) words and phrases correctly rather than treating them as transcription errors
- Use the 'short' name for characters in the summary unless context requires the full name

Context document:
---
${META_CONTEXT}
---
"
else
  CONTEXT_BLOCK=""
fi

# ------------------------------------------------------------
# Run summarization
# ------------------------------------------------------------

ollama run "$OLLAMA_MODEL" <<EOF > "$SUMMARY"
You are an expert note-taker for tabletop roleplaying game sessions.
The transcript is a recording of a TTRPG session and contains both in-character roleplay and out-of-character table talk.

${CONTEXT_BLOCK}
Rules:
- Clearly distinguish between in-character events and out-of-character discussion
- Quote spoken statements in their original language
- ${PROMPT_LANG}
- Use character short names (as provided in context) rather than full names where natural
- Do not invent or assume events not present in the transcript

Deliver:
1) Session overview (3-5 sentences summarising the main in-game events)
2) Key in-game decisions and developments
3) Important character moments (emotional beats, revelations, relationship shifts)
4) Highlights (memorable quotes, unexpected twists, standout scenes)
5) Notable out-of-character moments (rules discussions, retcons, player notes)
6) Cliffhangers and open threads going into the next session
7) Characters introduced or significantly developed this session

Transcript:
$(cat "$TRANSCRIPT")
EOF

echo "✅ Summary written to $SUMMARY"

Â