Claude
Skills
Sign in
Back

today

Included with Lifetime
$97 forever

Show enriched daily activity from Claude Code sessions — branches, PRs, Linear issues, and what you worked on. Use when the user wants to see what they did today, review their daily work, or get a summary of their sessions.

AI Agents

What this skill does


# Today — Daily Activity Report

Generate an enriched summary of today's Claude Code activity, showing branches, PRs, Linear issues, and session details.

## Data Sources

All data lives under `~/.claude/`:

| Source | Path | Contains |
|--------|------|----------|
| User prompts | `~/.claude/history.jsonl` | Every message you typed, with timestamp, project, sessionId |
| Session transcripts | `~/.claude/projects/<encoded-path>/<sessionId>.jsonl` | Full conversations with `gitBranch` field per message |
| Session log | `~/.claude/session-log.txt` | Session durations and project names |
| Active intents | `~/.claude-intents/in-progress/` | Intent specs with Linear issue links |
| Stats cache | `~/.claude/stats-cache.json` | Aggregate daily message/session/token counts |

## Workflow

### Step 1: Extract Today's Prompts from history.jsonl

Run a Python script to parse `~/.claude/history.jsonl` and extract all entries where the timestamp falls on today's date. Each entry has: `timestamp` (epoch ms), `project`, `sessionId`, `display` (the user's message text), `pastedContents`.

```bash
python3 << 'PYEOF'
import json
from datetime import datetime

today = datetime.now().strftime('%Y-%m-%d')

with open('__HOME__/.claude/history.jsonl') as f:
    for line in f:
        d = json.loads(line)
        ts = d.get('timestamp', '')
        if isinstance(ts, (int, float)):
            dt = datetime.fromtimestamp(ts / 1000)
            if dt.strftime('%Y-%m-%d') == today:
                project = d.get('project', '?').split('/')[-1]
                sid = d.get('sessionId', '?')[:8]
                display = d.get('display', '')[:120]
                print(f'{dt.strftime("%H:%M")} | {project} | {sid} | {display}')
PYEOF
```

Replace `__HOME__` with the actual home directory path.

### Step 2: Extract Branches from Session Transcripts

Scan all session `.jsonl` files modified today across `~/.claude/projects/*/`. Read the first line of each file to get the `gitBranch` field. Collect unique (project, branch) pairs where branch is not "main" or "trunk".

```bash
python3 << 'PYEOF'
import json, glob, os
from datetime import datetime

today = datetime.now().strftime('%Y-%m-%d')
project_dirs = glob.glob(os.path.expanduser('~/.claude/projects/*/'))

seen = set()
for pdir in project_dirs:
    for jf in glob.glob(os.path.join(pdir, '*.jsonl')):
        if os.path.basename(jf).startswith('agent-'):
            continue
        mtime = datetime.fromtimestamp(os.path.getmtime(jf))
        if mtime.strftime('%Y-%m-%d') != today:
            continue
        try:
            with open(jf) as f:
                for line in f:
                    line = line.strip()
                    if not line:
                        continue
                    d = json.loads(line)
                    branch = d.get('gitBranch', '')
                    sid = d.get('sessionId', '')
                    if not sid:
                        continue
                    project = pdir.rstrip('/').split('/')[-1]
                    key = (project, sid)
                    if key not in seen and branch:
                        seen.add(key)
                        import os
                        home_prefix = '-' + os.path.expanduser('~').replace('/', '-')
                        short = project.replace(home_prefix + '-', '').replace(home_prefix, '~')
                        print(f'{short} | {branch} | {sid[:8]}')
                    break
        except:
            pass
PYEOF
```

### Step 3: Deep Scan for PR URLs, Linear Issues, and Branch References

Scan all today's session transcript `.jsonl` files for:
- **GitHub PR URLs**: regex `https://github\.com/[^\s\)"'<>]+/pull/\d+`
- **GitHub repo URLs**: regex `https://github\.com/[^\s\)"'<>]+`
- **Linear issue IDs**: regex `\b[A-Z]{2,10}-\d+\b` (exclude false positives like UTF-8, ISO-8859)
- **Branch names** with issue prefixes: extract from `gitBranch` field

Search both user messages (`display` field, `message.content`) and assistant messages.

### Step 4: Check Active Intents

Read directories under `~/.claude-intents/in-progress/`. For each intent folder, read `spec.md` to extract:
- Intent name (folder name contains date, issue ID, and description)
- Linear issue ID and URL from the spec content
- Current status

### Step 5: Format the Report

Group the output into these sections:

#### Format Template

```markdown
## Today's Activity (YYYY-MM-DD)

### Branches Worked On
- **project-name** on `branch-name`
  - HH:MM — First prompt in this session
  - HH:MM — Another prompt...

### Pull Requests
- [repo/owner#number](url) — description from first mention
  - Context from the session messages

### Linear Issues
- [ISSUE-ID](linear-url) — Issue title (from intent spec if available)
  - Linked to branch: `branch-name`
  - Intent status: In Progress

### Session Timeline
| Time | Project | What |
|------|---------|------|
| HH:MM | project | First 100 chars of prompt... |

### Stats
- X sessions, Y messages, Z tool calls (from stats-cache.json for today)
```

**Rules:**
- Omit any section that has no items
- Group prompts under their branch/PR/issue when possible
- Show the session timeline as a compact table
- Extract issue titles from intent specs when available
- For branches containing issue IDs (e.g., `stu-1287-some-description`), link them to the corresponding Linear issue
- Use 24-hour time format
- Show project names in short form (strip the home directory prefix)

### Step 6: Save Report to ~/claude-today/

After generating the report, save it as both Markdown and HTML files in `~/claude-today/`.

**File naming:** Use the **target date** (the date whose data was requested) for the filename, not necessarily today's date. For example, `/today 2026-02-09` produces `2026-02-09.md` and `2026-02-09.html`.

**Directory:** `~/claude-today/` — create it if it doesn't exist (`mkdir -p ~/claude-today`).

**Consolidation:** If `~/claude-today/YYYY-MM-DD.md` already exists, read it first and merge:
- Parse the existing file's Session Timeline table entries
- Combine with the newly extracted entries
- Deduplicate by (Time, Project, What) — keep unique rows only
- Re-sort the timeline by time
- Rebuild all sections (Branches, PRs, Linear Issues) as the union of old + new data
- Overwrite both `.md` and `.html` with the consolidated result

**HTML generation:** Convert the final Markdown report to a styled HTML file. Use this approach:
1. Write the `.md` file first using the Write tool
2. Then generate the `.html` file by wrapping the markdown content in a simple HTML template with:
   - A `<style>` block with clean typography (system font stack, max-width 800px, centered)
   - Table styling with borders and padding
   - Code/pre styling with background color
   - Convert markdown to HTML: headers (`#` -> `<h1>`), tables, lists, bold, code spans, links
   - Use a Python script with basic regex-based markdown-to-HTML conversion (no external dependencies)

```bash
python3 << 'PYEOF'
import re, os

date = "TARGET_DATE"  # replaced by the actual target date
md_path = os.path.expanduser(f"~/claude-today/{date}.md")
html_path = os.path.expanduser(f"~/claude-today/{date}.html")

with open(md_path) as f:
    md = f.read()

# Basic markdown to HTML conversion
html = md
html = re.sub(r'^### (.+)$', r'<h3>\1</h3>', html, flags=re.MULTILINE)
html = re.sub(r'^## (.+)$', r'<h2>\1</h2>', html, flags=re.MULTILINE)
html = re.sub(r'^# (.+)$', r'<h1>\1</h1>', html, flags=re.MULTILINE)
html = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', html)
html = re.sub(r'`([^`]+)`', r'<code>\1</code>', html)
html = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'<a href="\2">\1</a>', html)

# Convert tables
def convert_table(match):
    lines = match.group(0).strip().split('\n')
    rows = [l for l in lines if not re.match(r'^\|[-| ]+\|$', l)]
    out = '<table>\n'
    for i, row in enumerate(rows):
        cells = [c.strip() for c in row.strip('|').split('|')]
        tag = 'th' if i == 
Files: 1
Size: 11.4 KB
Complexity: 20/100
Category: AI Agents

Related in AI Agents