Agent skill
dev-memory-briefing
Generate a session briefing from development memory (events.jsonl, sessions.jsonl). Shows recent timeline, open questions, and suggested focus areas.
Install this agent skill to your Project
npx add-skill https://github.com/BerryKuipers/claude-code-toolkit/tree/main/.claude/skills/memory/dev-memory/briefing
SKILL.md
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 readsproject-memory- Complementary MCP-based memory system
Best Practices
- Run at session start - Get oriented before coding
- Filter to relevant scope - Use branch/epic filters
- Review open questions - Address uncertainties first
- Check next steps - Verify nothing was forgotten
- Update as you go - Commit regularly for better timeline
Troubleshooting
Empty briefing?
- Check
ai_memory/events.jsonlexists and has content - Verify filters aren't too restrictive (branch, epic, days_back)
- Run
dev-memory-updateto populate memory from recent commits
Missing events?
- Check commit hooks are running (
.claude/hooks/post-commit-memory.sh) - Verify
devMemory.enabled: truein 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
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
validate-typescript
Run TypeScript compiler type-checking (tsc --noEmit) to validate type safety and catch type errors. Returns structured output with error counts, categories (type/syntax/import errors), and affected files. Used for quality gates and pre-commit validation.
validate-coverage-threshold
Validate test coverage meets minimum thresholds (default 80% overall, 80% statements, 75% branches, 80% functions). Parses coverage reports from coverage/coverage-summary.json or test output. Returns pass/fail status with detailed metrics and identifies uncovered files.
quality-gate
Comprehensive quality validation for TypeScript/JavaScript projects - runs TypeScript checks, tests, coverage analysis, build validation, and linting with structured JSON results
run-comprehensive-tests
Execute comprehensive test suite (Vitest/Jest) with coverage reporting and failure analysis. Returns structured output with test counts (total/passed/failed), coverage percentage, duration, and detailed failure information. Used for quality gates and CI/CD validation.
validate-build
Run production build validation (npm run build, vite build, tsc) to ensure code compiles and builds successfully. Returns structured output with build status, duration, size metrics, and error details. Used for quality gates and deployment readiness checks.
validate-lint
Run ESLint and Prettier validation to check code style, formatting, and best practices. Returns structured output with error/warning counts, rule violations, and affected files. Used for code quality gates and pre-commit validation.
Didn't find tool you were looking for?