Skip to content
Back to skills

2646 Session Report 45984d12

ASecurity

Generate a Markdown dashboard of recent Claude Code sessions with git activity, commits, and repo tracking. **Usage:** - `/session-report` - Show last 24 hours - `/session-report 48h` - Show last 48 hours - `/session-report 7d` - Show last 7 days - `/session-report ~/Desktop/Programming/knossot` - Filter by repo path

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
businessbashnodegit

Works with

  • claude code
  • vscode

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2646-session-report_45984d12 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2646 Session Report 45984d12?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2646 Session Report 45984d12
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2646-session-report-45984d12/badge)](https://www.skillsdirectory.com/skills/tools-only-2646-session-report-45984d12)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Session Report

Generate a Markdown dashboard of recent Claude Code sessions with git activity, commits, and repo tracking.

**Usage:**
- `/session-report` - Show last 24 hours
- `/session-report 48h` - Show last 48 hours
- `/session-report 7d` - Show last 7 days
- `/session-report ~/Desktop/Programming/knossot` - Filter by repo path

## Arguments

$ARGUMENTS

## Instructions

```bash
node -e "
const os = require('os');
const path = require('path');
const fs = require('fs');
const utils = require(os.homedir() + '/.claude/lib/tracker-utils.js');

// Parse arguments
const args = '$ARGUMENTS'.trim();
let hours = 24;
let filterRepo = null;

if (args) {
  // Parse time window: 48h, 7d, etc.
  const timeMatch = args.match(/^(\d+)(h|d)$/);
  if (timeMatch) {
    hours = parseInt(timeMatch[1]) * (timeMatch[2] === 'd' ? 24 : 1);
  } else if (args.startsWith('/') || args.startsWith('~') || args.startsWith('.')) {
    // Repo path filter
    filterRepo = args.replace(/^~/, os.homedir());
    filterRepo = path.resolve(filterRepo);
  }
}

const cutoffDate = new Date(Date.now() - hours * 3600000);

// Load data sources
const tracking = utils.loadGitTracking();
const summaryCache = utils.loadSummaryCache();
const allFiles = utils.getAllSessionFiles();
const statusData = utils.buildSessionStatus(allFiles, { maxSessions: 30 });

// If filtering by repo, get relevant session IDs
let repoSessionFilter = null;
if (filterRepo) {
  const repoSessions = utils.getSessionsForRepo(filterRepo);
  if (repoSessions.length > 0) {
    repoSessionFilter = new Set(repoSessions);
  }
}

// Build session report data
const sessions = [];
for (const s of statusData.sessions) {
  // Time filter
  if (s.timestamp && new Date(s.timestamp) < cutoffDate) continue;

  // Repo filter
  if (repoSessionFilter && !repoSessionFilter.has(s.fullId) && !repoSessionFilter.has(s.id)) continue;

  const repos = utils.getReposForSession(s.fullId || s.id);
  const summary = summaryCache[s.fullId] || summaryCache[s.id] || {};

  sessions.push({
    ...s,
    repos,
    summaryData: summary,
  });
}

// Header
const now = new Date().toISOString().replace(/\.\d+Z$/, '');
const windowStr = hours >= 24 ? (hours / 24) + 'd' : hours + 'h';
console.log('# Session Report');
console.log('Generated: ' + now + ' | Window: ' + windowStr);
if (filterRepo) {
  console.log('Filter: ' + filterRepo.replace(os.homedir(), '~'));
}
console.log('');

// Count running vs inactive
const running = sessions.filter(s => s.isRunning).length;
const inactive = sessions.length - running;
const vsCode = sessions.filter(s => s.isInVSCode).length;

if (sessions.length === 0) {
  console.log('_No sessions found in the last ' + windowStr + '._');
  console.log('');
} else {
  console.log('## Sessions (' + running + ' running, ' + inactive + ' inactive' + (vsCode ? ', ' + vsCode + ' in VS Code' : '') + ')');
  console.log('');

  sessions.forEach((s, i) => {
    // Determine project name
    const projectName = s.projectName || path.basename(s.projectPath || s.cwd || '?');

    // Status badges
    const badges = [];
    if (s.isRunning) badges.push('RUNNING');
    if (s.isInVSCode) badges.push('VS Code');
    if (!s.isRunning && !s.isInVSCode) badges.push('INACTIVE');
    const badgeStr = badges.join(', ');

    console.log('### [' + (i + 1) + '] ' + projectName + ' — ' + badgeStr);

    // Session info
    const slug = s.sessionSlug || s.slug || '';
    const shortId = (s.fullId || s.id || '').substring(0, 8);
    if (slug) {
      console.log('- **Session**: ' + slug + ' (\`' + shortId + '\`)');
    } else {
      console.log('- **Session**: \`' + shortId + '\`');
    }

    // Time info
    const age = s.timestamp ? utils.formatAge(s.timestamp) : '?';
    const model = s.summaryData.model || s.model || '';
    const turns = s.summaryData.num_turns || '';
    const cost = s.summaryData.total_cost_usd ? '\$' + s.summaryData.total_cost_usd.toFixed(2) : '';
    const infoParts = ['Started: ' + age];
    if (model) infoParts.push('Model: ' + model);
    if (turns) infoParts.push('Turns: ' + turns);
    if (cost) infoParts.push('Cost: ' + cost);
    console.log('- ' + infoParts.join(' | '));

    // Summary
    const title = s.summaryData.title || s.sessionSummary || '';
    if (title) {
      console.log('- **Summary**: ' + title);
    }

    // Repos touched (from git tracking)
    const repoEntries = Object.entries(s.repos || {});
    if (repoEntries.length > 0) {
      const repoStrs = repoEntries.map(([rpath, rdata]) => {
        const shortPath = rpath.replace(os.homedir(), '~');
        const repoName = path.basename(rpath);
        const branch = (rdata.branches || [])[0] || '';
        const commitCount = (rdata.commits || []).length;
        const ops = rdata.operations || [];
        const isReadOnly = !ops.some(op => ['commit', 'push', 'add', 'merge', 'rebase'].includes(op));

        let desc = repoName;
        if (branch) desc += ' (' + branch + ')';
        if (commitCount > 0) desc += ', ' + commitCount + ' commit' + (commitCount > 1 ? 's' : '');
        else if (isReadOnly) desc += ', read-only';
        return desc;
      });
      console.log('- **Repos**: ' + repoStrs.join(' | '));

      // Show commits
      const allCommits = [];
      for (const [rpath, rdata] of repoEntries) {
        for (const commit of (rdata.commits || [])) {
          allCommits.push({
            ...commit,
            repoName: path.basename(rpath),
          });
        }
      }
      allCommits.sort((a, b) => (b.ts || '').localeCompare(a.ts || ''));

      if (allCommits.length > 0) {
        console.log('- **Commits**:');
        allCommits.slice(0, 5).forEach(c => {
          const commitAge = c.ts ? utils.formatAge(c.ts) : '';
          const msg = c.msg ? c.msg.substring(0, 80) : '';
          console.log('  - \`' + (c.hash || '?').substring(0, 7) + '\` ' + msg + (commitAge ? ' (' + c.repoName + ', ' + commitAge + ')' : ''));
        });
      }
    }

    // Git remote
    const gitRemote = s.gitRemote || '';
    if (gitRemote) {
      console.log('- **Remote**: ' + gitRemote);
    }

    // Resume command
    const fullId = s.fullId || s.id || '';
    if (fullId) {
      console.log('- **Resume**: \`claude --resume ' + fullId + '\`');
    }

    console.log('');
  });
}

// Git Activity Timeline
const events = utils.getRecentGitEvents({ hours, maxEvents: 30 });
if (events.length > 0) {
  console.log('## Git Activity Timeline');
  console.log('');
  console.log('| Time | Repo | Session | Op | Details |');
  console.log('|------|------|---------|----|---------|');

  events.forEach(e => {
    if (e.type === 'result') return; // Skip enrichment lines in timeline
    const time = (e.ts || '').substring(11, 16) || '?';
    const repoName = path.basename(e.repo || '?');
    const shortSid = (e.short || (e.sid || '').substring(0, 8));
    const ops = e.ops || '';
    let details = '';

    // Look for enrichment data
    if (ops.includes('commit') && e.msg) {
      details = e.msg.substring(0, 60);
    } else if (e.cmd) {
      details = e.cmd.substring(0, 60);
    }

    console.log('| ' + time + ' | ' + repoName + ' | \`' + shortSid + '\` | ' + ops + ' | ' + details + ' |');
  });
  console.log('');
}

// Repo Summary
const repoIndex = tracking.repo_index || {};
const repoEntries = Object.entries(repoIndex);
if (repoEntries.length > 0) {
  console.log('## Repo Summary');
  console.log('');
  console.log('| Repo | Branches | Sessions | Commits | Last Activity |');
  console.log('|------|----------|----------|---------|---------------|');

  // Collect repo stats
  const repoStats = [];
  for (const [rpath, sids] of repoEntries) {
    const repoName = path.basename(rpath);
    const branches = new Set();
    let totalCommits = 0;
    let lastActivity = '';

    for (const sid of sids) {
      const session = (tracking.sessions || {})[sid];
      if (!session) continue;
      const repoData = (session.repos || {})[rpath];
      if (!repoData) continue;

      (repoData.branches || []).forEach(b => branches.add(b));
      totalCommits += (repoData.commits || []).length;
      if (repoData.last_seen && repoData.last_seen > lastActivity) {
        lastActivity = repoData.last_seen;
      }
    }

    repoStats.push({ repoName, rpath, branches: [...branches], sessionCount: sids.length, totalCommits, lastActivity });
  }

  // Sort by last activity
  repoStats.sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));

  repoStats.forEach(r => {
    const age = r.lastActivity ? utils.formatAge(r.lastActivity) : '?';
    console.log('| ' + r.repoName + ' | ' + r.branches.join(', ') + ' | ' + r.sessionCount + ' | ' + r.totalCommits + ' | ' + age + ' |');
  });
  console.log('');
}

console.log('---');
console.log('_Run \`/session-report\` to refresh. Resume with \`claude --resume <id>\`._');
"
```

Generates a comprehensive Markdown dashboard showing:
- **Session list** with status (running/inactive/VS Code), model, cost, turns
- **Repos touched** per session with branch and commit counts
- **Recent commits** with hashes and messages
- **Git activity timeline** (chronological event log)
- **Repo summary** table (all repos, sessions, commit counts)

Supports time window (`24h`, `48h`, `7d`) and repo path filtering.

Requires: `~/.claude/hooks/git-track.sh` (PreToolUse) to populate `git-tracking.jsonl`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.