Andi Smith

Technical Leader • Product Focused

Adding Timestamps to Claude Code Recap

  • By Andi Smith
  • 4 minute read

Usually when working I have a few Claude Code sessions running at once it can be hard to track which is which.

Claude Code offers /rename which is helpful for finding past conversations, but it only goes so far. On a Friday afternoon when I'm staring at 20+ tabs I start to wonder what is what.

Helpfully Claude Code shows a one-line recap of where things were. It's useful, but it doesn't say when it was written.

That matters more than you'd think. If I've been away for ten minutes, the recap is probably still accurate. If it was written yesterday afternoon, I need to check what's changed before I trust it. Without a timestamp on it, I can't easily tell the difference at a glance.

Putting a timestamp on Claude Code recaps

So I wrote a small plugin to add one.

What it does

It puts the date and time at the front of each recap.

Before:
※recap: You're fixing the flaky login test, and I've narrowed it to a race in the token refresh. Next, add a retry and rerun the suite.

After:
※recap: [10 Oct 18:43] You're fixing the flaky login test, and I've narrowed it to a race in the token refresh. Next, add a retry and rerun the suite.

Now I can see straight away whether the recap is fresh or stale.

How to install

  • Copy the script below and paste it into a terminal.
  • Start a new Claude Code session.
  • To check it's working, switch to another app for three minutes or more and come back. The recap should start with the date and time.
D=~/.claude/mods/recap-timestamp
mkdir -p $D/.claude-plugin $D/hooks
cat > $D/.claude-plugin/plugin.json <<'EOF'
{ "name": "recap-timestamp", "version": "0.1.0", "description": "Prefix session recaps with the time they were written" }
EOF
cat > $D/hooks/hooks.json <<'EOF'
{ "modules": ["./register.ts"] }
EOF
cat > $D/hooks/register.ts <<'EOF'
import type { Register } from 'claude-code'

const pad = (n: number) => String(n).padStart(2, '0')
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

const stamp = (ms: number) => {
  const d = new Date(ms)
  return `[${d.getDate()} ${MONTHS[d.getMonth()]} ${pad(d.getHours())}:${pad(d.getMinutes())}]`
}

export const register: Register = on => {
  on('session.append', ($, e, next) => {
    if (e.message.type !== 'system' || e.message.name !== 'away_summary') return next(e)

    const prefix = stamp(Date.now())
    const content = e.message.content.map(block =>
      block.type === 'text' ? { ...block, text: `${prefix} ${block.text}` } : block,
    )

    return next({ ...e, message: { ...e.message, content } })
  })
}
EOF
python3 - <<'EOF'
import json, os
p = os.path.expanduser('~/.claude/settings.json')
d = json.load(open(p)) if os.path.exists(p) else {}
env = d.setdefault('env', {})
dirs = [x for x in env.get('CLAUDE_CODE_PLUGIN_DIRS', '').split(':') if x]
if '~/.claude/mods/recap-timestamp' not in dirs:
    dirs.append('~/.claude/mods/recap-timestamp')
env['CLAUDE_CODE_PLUGIN_DIRS'] = ':'.join(dirs)
json.dump(d, open(p, 'w'), indent=2)
EOF
claude plugin validate $D

The install script writes the plugin to ~/.claude/mods/recap-timestamp, adds that folder to CLAUDE_CODE_PLUGIN_DIRS in ~/.claude/settings.json, and runs claude plugin validate to check it.

How it works

Claude Code saves each recap as a system message with the subtype away_summary. The plugin hooks into session.append, which fires as each message is saved. When it sees a recap, it adds the local time to the front of the text. Every other message passes through untouched. The core of it is only a few lines:

on('session.append', ($, e, next) => {
  if (e.message.type !== 'system' || e.message.name !== 'away_summary') return next(e)

  const prefix = stamp(Date.now())
  const content = e.message.content.map(block =>
    block.type === 'text' ? { ...block, text: `${prefix} ${block.text}` } : block,
  )

  return next({ ...e, message: { ...e.message, content } })
})

Things to know

Deliberately, this isn't a published plugin. It uses plugin function hooks which are still early access in Claude Code.

They only work on recent builds of Claude Code - this has been tested from 2.1.289. It also relies on the internal name of the recap message, which could change in any update. Until function hooks are generally available, it's better as a script you install yourself than something you'd find in a marketplace.

A few other points:

  • Recaps only appear if your terminal tells Claude Code when it gains and loses focus. iTerm2, Ghostty and the VS Code terminal all do.
  • The script leaves any existing entries in CLAUDE_CODE_PLUGIN_DIRS alone, and running it twice changes nothing.
  • To remove it, delete ~/.claude/mods/recap-timestamp and take that path out of CLAUDE_CODE_PLUGIN_DIRS.

It's a small change, but it's one of those things I now notice when it's missing. It's also a good example of how little code it takes to shape Claude Code around the way you actually work.

Andi Smith

By Andi Smith

Andi Smith is a passionate technical leader who excels at building and scaling high-performing product engineering teams with a focus on business value. He has successfully helped businesses of all sizes from start up, scale up to enterprise build value-driven solutions.

More Blog Posts

  • Don't Lose Your Head

    There's no doubt that AI is making us all faster at our jobs. But what is it doing to our judgement?

  • Deckchair Vibes

    Last week I built and shipped a browser game from a deckchair in Ibiza using nothing but my phone and AI. No laptop. No IDE. No terminal. Just Claude, GitHub and Netlify.

  • Measure Shipped Work, Not Tokens

    If everyone on your team uses AI every day, what does your AI adoption metric actually tell you?

  • AI to Resolve Incidents

    We put an AI bot between our alerting and our on-call engineer. It reads the logs, the traffic and the recent deploys, then comments on the incident before anyone has even opened Slack.