Automate / Weekly digest
A weekly digest of your calls, and how much you talked.
Every Friday at five: one Markdown page with each call of the week, its minutes, and your share of the talking. No AI needed.
A weekly meeting report on a Mac is one script and one launchd job. The script asks RecordCue's read-only CLI for the recordings of the last seven days and writes a Markdown table — when, which service, how many minutes, how much of the voiced time you spoke, whether a transcript exists — followed by a totals line: calls, hours, your average talk share. The talk-time ratio comes from the speaker timeline RecordCue stores beside each recording, so it needs no model and never reads the audio. Only jq and python3 are involved, both on macOS. An optional block at the end adds one sentence per call from Claude Code, and that is the only part that uses an AI.
What you get
~/Movies/RecordCue/Digests/2026-W37.md, named by ISO week, opened in whatever handles Markdown on your Mac. The second section appears only when you turn SUMMARISE=1 on:
# Calls, 2026-W37 | When | Service | Min | You spoke | Transcript | |---|---|--:|--:|:--:| | Mon 07 Sep 10:02 | Zoom | 28 | 41% | yes | | Mon 07 Sep 16:30 | Slack | 9 | 63% | yes | | Tue 08 Sep 18:07 | Google Meet | 54 | 33% | yes | | Wed 09 Sep 11:00 | Zoom | 31 | 22% | no | | Thu 10 Sep 09:30 | FaceTime | 12 | 58% | yes | | Fri 11 Sep 14:00 | Google Meet | 47 | 37% | yes | **6 calls, 3 hours; you spoke 42% on average.** ## What each call was about - **Mon 10:02 Zoom** — Planning the Q4 launch: dates, the pricing page, who owns the announcement. - **Mon 16:30 Slack** — A quick check on the build failure; the fix is in review. - **Tue 18:07 Google Meet** — Customer onboarding call: their data import and a request for SSO. - **Thu 09:30 FaceTime** — Catch-up with a contractor about the illustration deadline. - **Fri 14:00 Google Meet** — Weekly team sync: hiring update, support backlog, the offsite agenda.
RecordCue ignores the Digests folder; it scans for recordings, not Markdown. A year of these files is a plain-text record of how your weeks are spent in calls.
Prerequisites
- RecordCue, and recordings from the week. The “you spoke” column comes from the speaker timeline, which every recording with both microphone and system audio has; automatic transcription is only needed for the optional summaries and the transcript column.
jq(brew install jq; recent macOS ships one) andpython3(macOS ships one). Nothing else for the AI-free digest.- For the optional one-line summaries: Claude Code signed in, or Codex, or Ollama — see the variations.
- Not needed: the hook runner from /automate/hooks/. This is a scheduled job, not an action; it runs on a calendar, not when a recording finishes. The two coexist.
The script
Saved as ~/.config/recordcue/weekly-digest.sh. It takes one optional argument, the window (7d by default), and names the file from it. The decoder for the speaker timeline is the same arithmetic as on the CLI reference, inlined so the script is one file. Read it before you run it.
#!/bin/zsh
# ~/.config/recordcue/weekly-digest.sh — every call of the past week on one
# Markdown page, with your share of the talking. launchd runs it on Friday
# at 17:00; run it by hand any time. https://www.recordcue.app/automate/weekly-digest/
set -u
export PATH="/opt/homebrew/bin:/usr/local/bin:$HOME/.local/bin:$PATH"
rc="${RECORDCUE_APP:-/Applications/RecordCue.app}/Contents/MacOS/RecordCue"
since="${1:-7d}" # 7d weekly; 24h daily; 30d monthly
case "$since" in
24h) name=$(date +%F) ;; # 2026-09-11
30d) name=$(date +%Y-%m) ;; # 2026-09
*) name=$(date +%G-W%V) ;; # 2026-W37, the ISO week
esac
out="$HOME/Movies/RecordCue/Digests/$name.md"
mkdir -p "${out:h}"
# Exit 3 means no recordings in the window: no digest, no error.
json=$("$rc" --agent-cli list --since "$since" --json) || exit 0
calls=$(jq '.recordings | length' <<<"$json")
(( calls > 0 )) || exit 0
# Your share of the voiced time, from the speaker timeline:
# (M + B) / (M + S + B). Prints "—" when the recording has no timeline.
share() {
python3 - "$1" <<'EOF'
import re, sys
parts = sys.argv[1].split(":", 2)
if len(parts) < 3 or parts[0] != "rc1":
print("—"); sys.exit()
n = {s: 0 for s in ".MSB"}
for s, c in re.findall(r"([.MSB])(\d+)", parts[2]):
n[s] += int(c)
voiced = n["M"] + n["S"] + n["B"]
print(f"{(n['M'] + n['B']) / voiced:.0%}" if voiced else "—")
EOF
}
sum=0; n=0
{
print "# Calls, $name"
print
print "| When | Service | Min | You spoke | Transcript |"
print "|---|---|--:|--:|:--:|"
jq -c '.recordings | sort_by(.startedAt) | .[]' <<<"$json" |
while IFS= read -r rec; do
when=$(jq -r '.startedAt | fromdateiso8601 | localtime | strftime("%a %d %b %H:%M")' <<<"$rec")
svc=$(jq -r '.service // "Recording"' <<<"$rec")
min=$(jq -r '((.durationMs // 0) / 60000) | round' <<<"$rec")
act=$(jq -r '.speakerActivity.data // empty' <<<"$rec")
spoke=$( [[ -n "$act" ]] && share "$act" || print "—" )
tx=$( [[ -n $(jq -r '.transcript.path // empty' <<<"$rec") ]] && print yes || print no )
print "| $when | $svc | $min | $spoke | $tx |"
[[ "$spoke" == — ]] || { sum=$(( sum + ${spoke%\%} )); n=$(( n + 1 )); }
done
hours=$(jq '[.recordings[].durationMs // 0] | add / 360000 | round / 10' <<<"$json")
print
if (( n > 0 )); then
print "**$calls calls, $hours hours; you spoke $(( sum / n ))% on average.**"
else
print "**$calls calls, $hours hours.**"
fi
} >"$out"
# Optional, and the only part that uses a model: one line per call from
# Claude Code, which reads each transcript on stdin.
# SUMMARISE=1 ~/.config/recordcue/weekly-digest.sh
if [[ "${SUMMARISE:-0}" == 1 ]]; then
{
print; print "## What each call was about"; print
jq -r '.recordings | sort_by(.startedAt) | .[] | select(.transcript)
| [(.startedAt | fromdateiso8601 | localtime | strftime("%a %H:%M")),
(.service // "Recording"), .transcript.path] | @tsv' <<<"$json" |
while IFS=$'\t' read -r when svc file; do
line=$(claude -p "One sentence, plain text, no preamble: what was this call about?" <"$file") || continue
print -r -- "- **$when $svc** — $line"
done
} >>"$out"
fi
open "$out"Two details worth knowing. The CLI exits 3 when the window holds no recordings, and the script then writes nothing rather than an empty digest. And transcript is absent from the JSON, not null, until a transcript exists, which is why the column is read with .transcript.path // empty.
Schedule it with launchd
A launchd agent with a StartCalendarInterval runs the script every Friday at 17:00. If the Mac is asleep at that moment, launchd runs it on wake.
mkdir -p ~/.config/recordcue
# Save the script above as ~/.config/recordcue/weekly-digest.sh, then:
chmod +x ~/.config/recordcue/weekly-digest.sh
cat > ~/Library/LaunchAgents/app.recordcue.weekly.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key> <string>app.recordcue.weekly</string>
<key>ProgramArguments</key> <array>
<string>/bin/zsh</string>
<string>$HOME/.config/recordcue/weekly-digest.sh</string>
</array>
<key>StartCalendarInterval</key> <dict>
<key>Weekday</key> <integer>5</integer>
<key>Hour</key> <integer>17</integer>
<key>Minute</key> <integer>0</integer>
</dict>
<key>StandardOutPath</key> <string>$HOME/.config/recordcue/weekly-digest.log</string>
<key>StandardErrorPath</key> <string>$HOME/.config/recordcue/weekly-digest.log</string>
</dict></plist>
EOF
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/app.recordcue.weekly.plistTo remove it later:
launchctl bootout gui/$(id -u)/app.recordcue.weekly rm ~/Library/LaunchAgents/app.recordcue.weekly.plist
Test it
# Run it now instead of waiting for Friday: launchctl kickstart -k gui/$(id -u)/app.recordcue.weekly # Or by hand, over a longer window if the week was quiet: ~/.config/recordcue/weekly-digest.sh 30d ls ~/Movies/RecordCue/Digests cat ~/.config/recordcue/weekly-digest.log # empty when all is well
The digest opens when the script finishes. Run by hand with 30d, it writes a monthly file instead of a weekly one, so the test does not overwrite Friday's digest.
What the talk-share number is, and is not
RecordCue keeps a timeline of every call in 100 ms buckets: the microphone was louder, the system audio was louder, both, or neither. Your share is the voiced buckets in which the microphone was louder or both were, over all voiced buckets. Forty percent means you spoke about two fifths of the time anyone did. It knows which signal was louder, not who a voice belonged to: a colleague beside you counts as you, a video you played counts as them, and a noisy fan can tilt it. Long runs of M in the timeline are your monologues, which is the thing most people are really asking about. Compare weeks, not single calls.
Variations
Post it to Slack
The Slack recipe sets up an incoming webhook and keeps its URL in a file. Reuse it. Slack shows the table as plain text; the totals line is the part people read.
# Add before the final `open`, or instead of it. The webhook URL lives in
# the same file the Slack recipe uses; never inline it.
curl -fsS -X POST -H 'Content-type: application/json' \
--data "$(jq -Rs '{text: .}' <"$out")" \
"$(cat ~/.config/recordcue/slack-webhook)" >/dev/nullWrite it into Obsidian
The Obsidian recipe files every call as a note; the digest fits beside them.
# One line changes the destination. Obsidian renders the table as a table. out="$HOME/Documents/Vault/Calls/Digests/$name.md"
Daily, or monthly
The same script with a different window and a different calendar. Give each its own label (app.recordcue.daily, app.recordcue.monthly) and plist.
# Daily: pass 24h, and drop Weekday from the plist. <key>ProgramArguments</key> <array> <string>/bin/zsh</string> <string>$HOME/.config/recordcue/weekly-digest.sh</string> <string>24h</string> </array> <key>StartCalendarInterval</key> <dict> <key>Hour</key> <integer>18</integer> <key>Minute</key> <integer>0</integer> </dict> # Monthly: pass 30d, and run on the first of the month. <key>StartCalendarInterval</key> <dict> <key>Day</key> <integer>1</integer> <key>Hour</key> <integer>9</integer> <key>Minute</key> <integer>0</integer> </dict>
Another model for the summaries
Replace the claude -p line with codex exec -o "$tmp" "…" <"$file" and read $tmp, or, so that nothing leaves the Mac at all, ollama run llama3.2 "…" <"$file". Whichever you pick, each transcript leaves the Mac only if that command sends it — because you sent it, to the AI you chose, under your own account. The digest above that block never does.
Have your agent set it up
Paste into Claude Code, Codex or any local agent that can fetch a page and run a shell on this Mac.
Read https://www.recordcue.app/automate/weekly-digest/ and set it up on this Mac exactly as that page describes. Show me every file before writing it, then run the test and show me the result.
Questions
- How do I get a weekly summary report of all my meetings on a Mac?
- Record the calls with RecordCue, then run the script on this page from launchd every Friday at 17:00. It asks the read-only CLI for the recordings of the last seven days and writes one Markdown file per ISO week with a row per call, a totals line, and opens it. The report is built from local metadata with jq and python3; nothing is sent anywhere and no AI is involved unless you turn the optional one-line-per-call block on.
- How can I measure how much I talk in meetings?
- RecordCue records a timeline of which side was louder in every 100 ms of a call: the microphone, the system audio, both, or neither. Your talk share is the share of voiced buckets where the microphone was louder or both were, so 40% means you spoke about two fifths of the time anyone was speaking. The digest computes it per call and as a weekly average from that metadata, without reading the audio.
- Is the talk-time ratio accurate?
- It is a level comparison, not voice recognition. It knows whether your microphone or the far side was louder in each bucket, not whose voice it was, so a colleague in the same room counts as you and a video you played counts as them. Treat it as a trend across weeks rather than a precise figure for one call.
- Can I track total meeting time per week or per month?
- Yes. The totals line gives calls and hours for the window; run the same script with 30d for a monthly figure or 24h for a daily one, and each writes its own file. The files are plain Markdown in ~/Movies/RecordCue/Digests, so a year of them is easy to grep or to paste into a spreadsheet.