| 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 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