Claude Code Plugins

Community-maintained marketplace

Feedback

Generate a session briefing from development memory (events.jsonl, sessions.jsonl). Shows recent timeline, open questions, and suggested focus areas.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name dev-memory-briefing
description Generate a session briefing from development memory (events.jsonl, sessions.jsonl). Shows recent timeline, open questions, and suggested focus areas.

Dev Memory Briefing Skill

Generate a comprehensive session briefing from stored development events to provide context at the start of a coding session.

Purpose

Answer the question: "Where are we in this project, and what should I focus on today?"

When to Use

Session Start

  • Beginning a new coding session
  • Returning to a project after time away
  • Switching between projects
  • Before picking up an issue

Context Refresh

  • When you need to remember what happened recently
  • Before code review or planning
  • When onboarding to a project area

Input Requirements

Required

{
  repo: string;              // Repository name (or path)
}

Optional

{
  branch?: string;           // Filter to specific branch (default: current branch)
  epic_id?: string;          // Filter to specific epic
  issue_id?: string;         // Filter to specific issue
  max_events?: number;       // Limit results (default: 20)
  days_back?: number;        // Look back N days (default: 30)
  format?: 'markdown' | 'json';  // Output format (default: markdown)
}

Behavior

Step 1: Locate Memory Files

MEMORY_DIR="ai_memory"
EVENTS_FILE="$MEMORY_DIR/events.jsonl"
SESSIONS_FILE="$MEMORY_DIR/sessions.jsonl"
BRIEFING_FILE="$MEMORY_DIR/SESSION_BRIEFING.md"

# Check if memory exists
if [ ! -f "$EVENTS_FILE" ]; then
  echo "No development memory found. Start by making commits to build timeline."
  exit 0
fi

Step 2: Load and Filter Events

Note: For large files, consider using a streaming approach (e.g., Node.js readline module) instead of loading the entire file into memory.

const loadEvents = (options: BriefingOptions): DevEvent[] => {
  const eventsFile = 'ai_memory/events.jsonl';
  if (!fs.existsSync(eventsFile)) return [];

  const lines = fs.readFileSync(eventsFile, 'utf-8').split('\n').filter(l => l.trim());
  const events: DevEvent[] = [];

  for (const line of lines) {
    try {
      const event = JSON.parse(line);

      // Filter by repo
      if (options.repo && event.repo !== options.repo) continue;

      // Filter by branch
      if (options.branch && event.branch !== options.branch) continue;

      // Filter by epic
      if (options.epic_id && event.epic_id !== options.epic_id) continue;

      // Filter by issue
      if (options.issue_id) {
        if (!event.related_issues || !event.related_issues.includes(`#${options.issue_id}`)) {
          continue;
        }
      }

      // Filter by date range
      if (options.days_back) {
        const cutoff = new Date();
        cutoff.setDate(cutoff.getDate() - options.days_back);
        if (new Date(event.timestamp) < cutoff) continue;
      }

      events.push(event);
    } catch (e) {
      console.warn(`Skipping malformed event line: ${line.substring(0, 50)}...`);
    }
  }

  // Sort by timestamp descending (most recent first)
  events.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());

  // Limit results
  return events.slice(0, options.max_events || 20);
};

Step 2.5: Load Sessions Data

Note: For large files, consider reading backwards to find recent sessions more efficiently.

const loadSessions = (options: BriefingOptions): DevSession[] => {
  const sessionsFile = 'ai_memory/sessions.jsonl';
  if (!fs.existsSync(sessionsFile)) return [];

  const lines = fs.readFileSync(sessionsFile, 'utf-8').split('\n').filter(l => l.trim());
  const sessionsMap = new Map<string, DevSession>();

  // Load all sessions, keeping only the latest version of each session ID
  for (const line of lines) {
    try {
      const session = JSON.parse(line);

      // Filter by repo
      if (options.repo && session.repo !== options.repo) continue;

      // Filter by branch
      if (options.branch && session.branch !== options.branch) continue;

      // Filter by date range
      if (options.days_back) {
        const cutoff = new Date();
        cutoff.setDate(cutoff.getDate() - options.days_back);
        if (new Date(session.timestamp_end) < cutoff) continue;
      }

      // Keep latest version of each session (sessions can be updated)
      sessionsMap.set(session.id, session);
    } catch (e) {
      console.warn(`Skipping malformed session line: ${line.substring(0, 50)}...`);
    }
  }

  // Convert map to array and sort by end time descending
  const sessions = Array.from(sessionsMap.values());
  sessions.sort((a, b) => new Date(b.timestamp_end).getTime() - new Date(a.timestamp_end).getTime());

  return sessions.slice(0, 5); // Return last 5 sessions
};

Step 3: Analyze Current State

const analyzeState = (events: DevEvent[]) => {
  if (events.length === 0) {
    return {
      status: 'No recent activity',
      activeBranches: [],
      activeEpics: [],
      recentTypes: {},
    };
  }

  // Count events by type
  const recentTypes = events.reduce((acc, e) => {
    acc[e.type] = (acc[e.type] || 0) + 1;
    return acc;
  }, {} as Record<string, number>);

  // Find active branches (branches with activity in last 7 days)
  const recentCutoff = new Date();
  recentCutoff.setDate(recentCutoff.getDate() - 7);

  const activeBranches = [...new Set(
    events
      .filter(e => new Date(e.timestamp) > recentCutoff)
      .map(e => e.branch)
  )];

  // Find active epics
  const activeEpics = [...new Set(
    events
      .filter(e => e.epic_id)
      .map(e => e.epic_id)
  )];

  // Determine high-level status
  const featureCount = recentTypes['feature_implemented'] || 0;
  const bugCount = recentTypes['bug_fixed'] || 0;
  const refactorCount = recentTypes['refactor'] || 0;

  let status = 'Mixed development activity';
  if (featureCount > bugCount + refactorCount) status = 'Actively building features';
  if (bugCount > featureCount) status = 'Bug fixing focus';
  if (refactorCount > featureCount && refactorCount > bugCount) status = 'Code quality improvements';

  return { status, activeBranches, activeEpics, recentTypes };
};

Step 4: Collect Open Questions and Next Steps

const collectActions = (events: DevEvent[]) => {
  const allQuestions: string[] = [];
  const allNextSteps: string[] = [];

  for (const event of events) {
    if (event.open_questions) {
      allQuestions.push(...event.open_questions.map(q => `[${event.title}] ${q}`));
    }
    if (event.next_steps) {
      allNextSteps.push(...event.next_steps.map(s => `[${event.title}] ${s}`));
    }
  }

  // Deduplicate
  const uniqueQuestions = [...new Set(allQuestions)];
  const uniqueNextSteps = [...new Set(allNextSteps)];

  return { openQuestions: uniqueQuestions, nextSteps: uniqueNextSteps };
};

Step 5: Generate Markdown Briefing

const generateMarkdownBriefing = (
  repo: string,
  branch: string | undefined,
  events: DevEvent[],
  sessions: DevSession[],
  state: ReturnType<typeof analyzeState>,
  actions: ReturnType<typeof collectActions>
): string => {
  const lines: string[] = [];

  // Header
  lines.push(`# ${repo} โ€“ Development Briefing`);
  lines.push('');
  lines.push(`**Generated:** ${new Date().toISOString()}`);
  if (branch) lines.push(`**Branch:** ${branch}`);
  lines.push('');

  // Current state
  lines.push('## Where We Are');
  lines.push('');
  lines.push(`- **Status:** ${state.status}`);
  if (state.activeBranches.length > 0) {
    lines.push(`- **Active branches:** ${state.activeBranches.join(', ')}`);
  }
  if (state.activeEpics.length > 0) {
    lines.push(`- **Active epics:** ${state.activeEpics.join(', ')}`);
  }
  lines.push('');

  // Recent activity breakdown
  lines.push('## Recent Activity');
  lines.push('');
  Object.entries(state.recentTypes)
    .sort((a, b) => b[1] - a[1])
    .forEach(([type, count]) => {
      lines.push(`- **${type}:** ${count}`);
    });
  lines.push('');

  // Recent sessions
  if (sessions.length > 0) {
    lines.push('## Recent Sessions');
    lines.push('');
    sessions.forEach(session => {
      const startDate = session.timestamp_start.split('T')[0];
      const duration = Math.round(
        (new Date(session.timestamp_end).getTime() - new Date(session.timestamp_start).getTime()) / (1000 * 60)
      );
      lines.push(`- **[${startDate}]** ${session.agent} - ${duration}min`);
      lines.push(`  - ${session.summary}`);
      if (session.notes && session.notes.length > 0) {
        session.notes.forEach(note => lines.push(`  - ๐Ÿ“ ${note}`));
      }
    });
    lines.push('');
  }

  // Timeline
  lines.push('## Timeline (Last 20 Events)');
  lines.push('');

  for (const event of events) {
    const date = event.timestamp.split('T')[0];
    const typeEmoji = {
      feature_implemented: 'โœจ',
      bug_fixed: '๐Ÿ›',
      refactor: 'โ™ป๏ธ',
      decision: '๐Ÿ“‹',
      test_added: '๐Ÿงช',
      docs_updated: '๐Ÿ“š',
      breaking_change: 'โš ๏ธ',
    }[event.type] || 'โ€ข';

    lines.push(`- **[${date}]** ${typeEmoji} ${event.title}`);

    if (event.summary && event.summary !== event.title) {
      lines.push(`  - ${event.summary.substring(0, 200)}`);
    }

    if (event.related_issues || event.related_prs) {
      const refs = [
        ...(event.related_issues || []),
        ...(event.related_prs || []),
      ];
      lines.push(`  - Related: ${refs.join(', ')}`);
    }

    lines.push('');
  }

  // Open questions
  if (actions.openQuestions.length > 0) {
    lines.push('## Open Questions');
    lines.push('');
    actions.openQuestions.slice(0, 10).forEach(q => {
      lines.push(`- ${q}`);
    });
    lines.push('');
  }

  // Next steps
  if (actions.nextSteps.length > 0) {
    lines.push('## Suggested Next Steps');
    lines.push('');
    actions.nextSteps.slice(0, 10).forEach(s => {
      lines.push(`- [ ] ${s}`);
    });
    lines.push('');
  }

  // Footer
  lines.push('---');
  lines.push('');
  lines.push('*Generated by dev-memory-briefing skill*');
  lines.push('');

  return lines.join('\n');
};

Step 6: Write Briefing File

# Write to SESSION_BRIEFING.md
echo "$BRIEFING_MARKDOWN" > ai_memory/SESSION_BRIEFING.md

Step 7: Return Briefing

Return the briefing text for Claude to inject into the session.

Example Briefing Output

# WescoBar-Universe-Storyteller โ€“ Development Briefing

**Generated:** 2025-12-10T22:30:00Z
**Branch:** feature/context-pipeline-v2

## Where We Are

- **Status:** Actively building features
- **Active branches:** feature/context-pipeline-v2, main
- **Active epics:** epic-context-pipeline-v2

## Recent Activity

- **feature_implemented:** 3
- **bug_fixed:** 2
- **refactor:** 1

## Timeline (Last 20 Events)

- **[2025-12-10]** โœจ Context pipeline Phase 3 โ€“ prompt routing per use case
  - Implemented context slice routing per use case with config-driven providers.
  - Related: #352, #366, #374

- **[2025-12-10]** ๐Ÿ› Fix memory leak in context compaction
  - Compaction was holding references to old context slices. Reduced memory by ~40%.
  - Related: #305

- **[2025-12-09]** โ™ป๏ธ Extract repository layer from services
  - Moved all Prisma queries from UserService to UserRepository.

## Open Questions

- [Context pipeline] Refine scoring heuristics for multi-provider ranking?
- [Memory leak fix] Monitor production memory usage after deploy

## Suggested Next Steps

- [ ] [Context pipeline] Add tests for fallback provider selection
- [ ] [Context pipeline] Wire into video prompt pipeline later
- [ ] [Repository refactor] Add repository layer for Products and Orders

---

*Generated by dev-memory-briefing skill*

Output Formats

Markdown (Default)

Returns formatted markdown suitable for:

  • Displaying in terminal
  • Inserting into session notes
  • Writing to SESSION_BRIEFING.md

JSON

Returns structured data for programmatic use:

{
  "repo": "WescoBar-Universe-Storyteller",
  "branch": "feature/context-pipeline-v2",
  "generated_at": "2025-12-10T22:30:00Z",
  "state": {
    "status": "Actively building features",
    "active_branches": ["feature/context-pipeline-v2", "main"],
    "active_epics": ["epic-context-pipeline-v2"],
    "recent_types": {
      "feature_implemented": 3,
      "bug_fixed": 2,
      "refactor": 1
    }
  },
  "timeline": [
    {
      "date": "2025-12-10",
      "type": "feature_implemented",
      "title": "Context pipeline Phase 3...",
      "summary": "Implemented context slice routing...",
      "related_refs": ["#352", "#366", "#374"]
    }
  ],
  "open_questions": ["..."],
  "next_steps": ["..."]
}

Usage Examples

Session Start Briefing

# At session start, generate briefing for current branch
REPO=$(basename "$(git rev-parse --show-toplevel)")
BRANCH=$(git branch --show-current)

# Claude invokes dev-memory-briefing skill
# Output shown to user to provide context

Epic-Specific Briefing

# Focus on specific epic
REPO="WescoBar-Universe-Storyteller"
EPIC="epic-context-pipeline-v2"

# Generate briefing filtered to this epic

Issue-Specific Briefing

# Before picking up issue #352
REPO="WescoBar-Universe-Storyteller"
ISSUE="352"

# Show all events related to this issue

Integration with Workflows

SessionStart Hook (Optional)

In .claude/settings.json:

{
  "sessionStart": [
    "bash scripts/sync-claude-toolkit.sh",
    "bash scripts/generate-session-briefing.sh"
  ]
}

Where generate-session-briefing.sh:

#!/bin/bash
# Generate and display briefing at session start

if [ -f "ai_memory/events.jsonl" ]; then
  echo "๐Ÿ“‹ Generating session briefing..."
  # Claude would invoke dev-memory-briefing skill here
  # and display the result
fi

Conductor Workflow Integration

  • Phase 1: Issue Pickup - Generate briefing for issue context
  • Before code review - Review recent changes timeline
  • Sprint planning - Review epic progress

Configuration

In .claude/config.yml:

devMemory:
  briefing:
    enabled: true
    showOnSessionStart: false    # Don't auto-show (opt-in)
    defaultMaxEvents: 20
    defaultDaysBack: 30
    includeOpenQuestions: true
    includeNextSteps: true

Related Skills

  • dev-memory-update - Creates the events this skill reads
  • project-memory - Complementary MCP-based memory system

Best Practices

  1. Run at session start - Get oriented before coding
  2. Filter to relevant scope - Use branch/epic filters
  3. Review open questions - Address uncertainties first
  4. Check next steps - Verify nothing was forgotten
  5. Update as you go - Commit regularly for better timeline

Troubleshooting

Empty briefing?

  • Check ai_memory/events.jsonl exists and has content
  • Verify filters aren't too restrictive (branch, epic, days_back)
  • Run dev-memory-update to populate memory from recent commits

Missing events?

  • Check commit hooks are running (.claude/hooks/post-commit-memory.sh)
  • Verify devMemory.enabled: true in config
  • Manually backfill with dev-memory-update

Briefing too long?

  • Reduce max_events (default: 20)
  • Reduce days_back (default: 30)
  • Filter to specific branch or epic