Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
195 lines
4.8 KiB
JavaScript
Executable File
195 lines
4.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* DSS Session Summary Hook
|
|
* Generates a summary report at the end of each Claude Code session.
|
|
* Written from scratch for DSS.
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { execSync } = require('child_process');
|
|
|
|
// Configuration
|
|
const DEFAULT_CONFIG = {
|
|
session_summary: {
|
|
enabled: true,
|
|
output_file: '.dss-session-summary.md',
|
|
include_git_diff: true,
|
|
include_file_list: true,
|
|
max_diff_lines: 100
|
|
}
|
|
};
|
|
|
|
function loadConfig() {
|
|
const configPath = path.join(process.env.HOME || '', '.dss', 'hooks-config.json');
|
|
try {
|
|
if (fs.existsSync(configPath)) {
|
|
const userConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
return { ...DEFAULT_CONFIG, ...userConfig };
|
|
}
|
|
} catch (e) {
|
|
// Use defaults
|
|
}
|
|
return DEFAULT_CONFIG;
|
|
}
|
|
|
|
function getGitInfo() {
|
|
const info = {
|
|
branch: '',
|
|
status: '',
|
|
diff: '',
|
|
modifiedFiles: []
|
|
};
|
|
|
|
try {
|
|
// Check if in git repo
|
|
execSync('git rev-parse --is-inside-work-tree', { stdio: 'pipe' });
|
|
|
|
// Get branch
|
|
info.branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
|
|
// Get status
|
|
info.status = execSync('git status --short', { encoding: 'utf8' }).trim();
|
|
|
|
// Get modified files
|
|
const statusLines = info.status.split('\n').filter(Boolean);
|
|
info.modifiedFiles = statusLines.map(line => {
|
|
const parts = line.trim().split(/\s+/);
|
|
return {
|
|
status: parts[0],
|
|
file: parts.slice(1).join(' ')
|
|
};
|
|
});
|
|
|
|
// Get diff summary
|
|
try {
|
|
info.diff = execSync('git diff --stat', { encoding: 'utf8' }).trim();
|
|
} catch (e) {
|
|
info.diff = '';
|
|
}
|
|
} catch (e) {
|
|
// Not a git repo or git not available
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
function getSessionStats() {
|
|
// Try to read from session state if available
|
|
const stats = {
|
|
startTime: new Date().toISOString(),
|
|
filesModified: 0,
|
|
linesAdded: 0,
|
|
linesRemoved: 0
|
|
};
|
|
|
|
try {
|
|
// Get diff stats from git
|
|
const diffStat = execSync('git diff --numstat', { encoding: 'utf8' });
|
|
const lines = diffStat.trim().split('\n').filter(Boolean);
|
|
|
|
for (const line of lines) {
|
|
const [added, removed] = line.split('\t');
|
|
stats.linesAdded += parseInt(added) || 0;
|
|
stats.linesRemoved += parseInt(removed) || 0;
|
|
stats.filesModified++;
|
|
}
|
|
} catch (e) {
|
|
// Git not available
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
|
|
function generateReport(config) {
|
|
const summaryConfig = config.session_summary || {};
|
|
const gitInfo = getGitInfo();
|
|
const stats = getSessionStats();
|
|
|
|
const timestamp = new Date().toLocaleString();
|
|
const lines = [];
|
|
|
|
lines.push('# DSS Session Summary');
|
|
lines.push(`\n**Generated:** ${timestamp}`);
|
|
|
|
if (gitInfo.branch) {
|
|
lines.push(`**Branch:** ${gitInfo.branch}`);
|
|
}
|
|
|
|
lines.push('\n## Changes Overview');
|
|
lines.push('');
|
|
lines.push(`- Files modified: ${stats.filesModified}`);
|
|
lines.push(`- Lines added: +${stats.linesAdded}`);
|
|
lines.push(`- Lines removed: -${stats.linesRemoved}`);
|
|
|
|
if (summaryConfig.include_file_list && gitInfo.modifiedFiles.length > 0) {
|
|
lines.push('\n## Modified Files');
|
|
lines.push('');
|
|
lines.push('| Status | File |');
|
|
lines.push('|--------|------|');
|
|
|
|
const statusLabels = {
|
|
'M': 'Modified',
|
|
'A': 'Added',
|
|
'D': 'Deleted',
|
|
'R': 'Renamed',
|
|
'??': 'Untracked'
|
|
};
|
|
|
|
for (const file of gitInfo.modifiedFiles.slice(0, 20)) {
|
|
const label = statusLabels[file.status] || file.status;
|
|
lines.push(`| ${label} | ${file.file} |`);
|
|
}
|
|
|
|
if (gitInfo.modifiedFiles.length > 20) {
|
|
lines.push(`| ... | +${gitInfo.modifiedFiles.length - 20} more files |`);
|
|
}
|
|
}
|
|
|
|
if (summaryConfig.include_git_diff && gitInfo.diff) {
|
|
lines.push('\n## Diff Summary');
|
|
lines.push('');
|
|
lines.push('```');
|
|
const diffLines = gitInfo.diff.split('\n');
|
|
const maxLines = summaryConfig.max_diff_lines || 100;
|
|
lines.push(diffLines.slice(0, maxLines).join('\n'));
|
|
if (diffLines.length > maxLines) {
|
|
lines.push(`... (${diffLines.length - maxLines} more lines)`);
|
|
}
|
|
lines.push('```');
|
|
}
|
|
|
|
lines.push('\n---');
|
|
lines.push('*Generated by DSS Session Summary Hook*');
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
function main() {
|
|
const config = loadConfig();
|
|
|
|
if (!config.session_summary?.enabled) {
|
|
process.exit(0);
|
|
}
|
|
|
|
try {
|
|
const report = generateReport(config);
|
|
const outputFile = config.session_summary.output_file || '.dss-session-summary.md';
|
|
const outputPath = path.join(process.cwd(), outputFile);
|
|
|
|
fs.writeFileSync(outputPath, report, 'utf8');
|
|
|
|
// Output confirmation
|
|
console.log(JSON.stringify({
|
|
systemMessage: `Session summary saved to ${outputFile}`,
|
|
continue: true
|
|
}));
|
|
} catch (e) {
|
|
// Fail silently
|
|
}
|
|
|
|
process.exit(0);
|
|
}
|
|
|
|
main();
|