this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Consolidate day view to project-level, tune summarizer prompts

- Merge session details into unified project cards (no more per-session breakdown)
- Remove SessionItem component (orphaned)
- Adjust daily brag prompt to keep 3-5 significant items per project
- Bump concurrency from 5 to 10 for faster processing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

alice 203e721b 293f2cdf

+62 -135
+1 -1
src/cli/process.ts
··· 132 132 console.log(`Found ${sessions.length} session(s) to check\n`); 133 133 134 134 // Process sessions in parallel with concurrency limit 135 - const CONCURRENCY = 5; 135 + const CONCURRENCY = 10; 136 136 const results: Array<{ 137 137 session: SessionFile; 138 138 result?: Awaited<ReturnType<typeof processSession>>;
+9 -18
src/core/summarizer.ts
··· 140 140 .map(([project, accs]) => `${project}: ${accs.join('; ')}`) 141 141 .join('\n'); 142 142 143 - const systemPrompt = `Summarize a developer's daily work. Be EXTREMELY brief. 143 + const systemPrompt = `Summarize a developer's daily work. Keep the 3-5 most significant items per project. 144 144 145 - FORMAT: "added [feature] ([scope])" or "fixed [thing] ([scope])" 146 - - Scope is just: frontend, backend, or both 147 - - ONE feature per project, maybe two if truly separate 145 + FORMAT: "feature1, feature2; fixed thing (scope)" 146 + - Scope: frontend, backend, or both 147 + - Pick the BIGGEST wins - skip minor fixes, docs, tests, refactoring 148 + - Consolidate related work: "3 notification fixes" → "notification handling" 148 149 149 - CONSOLIDATE aggressively: 150 - - "frequency UI; dose calculations; CSV updates" → "multi-dose scheduling (backend, frontend)" 151 - - "dark mode fixes; theme updates; color changes" → "dark mode (frontend)" 152 - - "tests; error handling; refactoring" → skip unless it's the ONLY work done 150 + Examples: 151 + - "date filtering, new project detection; fixed path resolution (backend, frontend)" 152 + - "multi-dose scheduling, CSV export (backend, frontend)" 153 153 154 - Do NOT list: 155 - - Tests (unless the whole session was just tests) 156 - - Types/refactoring 157 - - Documentation updates 158 - - Individual files or components 159 - 160 - GOOD: "added multi-dose scheduling (backend, frontend)" 161 - BAD: "multi-dose scheduling engine with formulation matching (backend, types), dose frequency UI..." 162 - 163 - Use exact project names. Max 10 words per project.`; 154 + Use exact project names. Max ~15 words per project.`; 164 155 165 156 const userPrompt = `Summarize this developer's day (${date}):\n\n${projectSummaries}`; 166 157
+52 -29
src/web/app/components/ProjectCard.tsx
··· 1 - import React, { useState } from 'react'; 2 - import { ChevronDown, ChevronUp, Folder } from 'lucide-react'; 3 - import SessionItem from './SessionItem'; 1 + import React, { useMemo } from 'react'; 2 + import { Folder, FileCode } from 'lucide-react'; 4 3 5 4 interface SessionDetail { 6 5 sessionId: string; ··· 24 23 } 25 24 26 25 export default function ProjectCard({ project, isNew }: Props) { 27 - const [expanded, setExpanded] = useState(true); 26 + const aggregated = useMemo(() => { 27 + const allAccomplishments: string[] = []; 28 + const allFiles = new Set<string>(); 29 + 30 + for (const session of project.sessions) { 31 + allAccomplishments.push(...session.accomplishments); 32 + session.filesChanged.forEach((f) => allFiles.add(f)); 33 + } 34 + 35 + // Dedupe accomplishments (rough - exact match only) 36 + const uniqueAccomplishments = [...new Set(allAccomplishments)]; 37 + 38 + return { 39 + accomplishments: uniqueAccomplishments, 40 + files: [...allFiles], 41 + }; 42 + }, [project.sessions]); 28 43 29 44 return ( 30 - <div className="bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden"> 31 - <button 32 - onClick={() => setExpanded(!expanded)} 33 - className="w-full flex items-center justify-between p-3 bg-gray-50/50 hover:bg-gray-50 transition-colors text-left" 34 - > 35 - <div className="flex items-center gap-2"> 36 - <div className="p-1.5 bg-blue-100 text-blue-600 rounded"> 37 - <Folder size={16} /> 38 - </div> 39 - <div className="flex items-center gap-2"> 40 - <h3 className="text-sm font-bold text-slate-800">{project.name}</h3> 41 - {isNew && ( 42 - <span className="px-1.5 py-0.5 text-xs font-medium bg-green-100 text-green-700 rounded">NEW</span> 43 - )} 44 - </div> 45 - </div> 46 - <div className="flex items-center gap-3 text-slate-500"> 47 - <span className="text-xs font-medium">{project.sessions.length} sessions</span> 48 - {expanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />} 45 + <div className="bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden p-3"> 46 + <div className="flex items-center gap-2 mb-2"> 47 + <div className="p-1.5 bg-blue-100 text-blue-600 rounded"> 48 + <Folder size={16} /> 49 49 </div> 50 - </button> 50 + <h3 className="text-sm font-bold text-slate-800">{project.name}</h3> 51 + {isNew && ( 52 + <span className="px-1.5 py-0.5 text-xs font-medium bg-green-100 text-green-700 rounded">NEW</span> 53 + )} 54 + </div> 51 55 52 - {expanded && ( 53 - <div className="px-3 pb-3"> 54 - <div className="mt-2 ml-1"> 55 - {project.sessions.map((session) => ( 56 - <SessionItem key={session.sessionId} session={session} /> 56 + {aggregated.accomplishments.length > 0 && ( 57 + <ul className="space-y-1 mb-3"> 58 + {aggregated.accomplishments.map((acc, i) => ( 59 + <li key={i} className="flex items-start gap-1.5 text-slate-600 text-sm"> 60 + <span className="mt-1.5 w-1 h-1 rounded-full bg-blue-400 shrink-0" /> 61 + <span>{acc}</span> 62 + </li> 63 + ))} 64 + </ul> 65 + )} 66 + 67 + {aggregated.files.length > 0 && ( 68 + <div className="pt-2 border-t border-gray-100"> 69 + <span className="text-xs font-semibold text-slate-400 uppercase tracking-wider flex items-center gap-1 mb-1.5"> 70 + <FileCode size={12} /> Files 71 + </span> 72 + <div className="flex flex-wrap gap-1.5"> 73 + {aggregated.files.slice(0, 8).map((f, i) => ( 74 + <span key={i} className="text-xs px-2 py-1 bg-slate-50 text-slate-600 rounded border border-slate-100 font-mono"> 75 + {f.split('/').pop()} 76 + </span> 57 77 ))} 78 + {aggregated.files.length > 8 && ( 79 + <span className="text-xs px-2 py-1 text-slate-400">+{aggregated.files.length - 8} more</span> 80 + )} 58 81 </div> 59 82 </div> 60 83 )}
-87
src/web/app/components/SessionItem.tsx
··· 1 - import React from 'react'; 2 - import { Clock, FileCode, Wrench } from 'lucide-react'; 3 - 4 - interface SessionDetail { 5 - sessionId: string; 6 - startTime: string; 7 - endTime: string; 8 - shortSummary: string; 9 - accomplishments: string[]; 10 - filesChanged: string[]; 11 - toolsUsed: string[]; 12 - } 13 - 14 - interface Props { 15 - session: SessionDetail; 16 - } 17 - 18 - export default function SessionItem({ session }: Props) { 19 - const start = new Date(session.startTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); 20 - const end = new Date(session.endTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); 21 - 22 - const duration = Math.round((new Date(session.endTime).getTime() - new Date(session.startTime).getTime()) / 60000); 23 - 24 - return ( 25 - <div className="pl-4 border-l-2 border-gray-100 pb-3 last:pb-0 relative"> 26 - <div className="absolute -left-[5px] top-0 w-2 h-2 rounded-full bg-blue-300" /> 27 - 28 - <div className="flex items-center gap-2 mb-1"> 29 - <div className="flex items-center gap-1.5 text-xs text-slate-500"> 30 - <Clock size={12} /> 31 - <span>{start} - {end}</span> 32 - <span className="px-1 py-0.5 rounded bg-gray-100 text-xs text-gray-600">{duration}m</span> 33 - </div> 34 - </div> 35 - 36 - <div className="bg-white rounded-lg border border-gray-100 p-3 shadow-sm"> 37 - <p className="text-slate-800 font-medium text-sm mb-2">{session.shortSummary}</p> 38 - 39 - {session.accomplishments.length > 0 && ( 40 - <ul className="space-y-1 mb-2"> 41 - {session.accomplishments.map((acc, i) => ( 42 - <li key={i} className="flex items-start gap-1.5 text-slate-600 text-xs"> 43 - <span className="mt-1 w-1 h-1 rounded-full bg-blue-400 shrink-0" /> 44 - <span>{acc}</span> 45 - </li> 46 - ))} 47 - </ul> 48 - )} 49 - 50 - <div className="flex flex-wrap gap-3 mt-2 pt-2 border-t border-gray-50"> 51 - {session.filesChanged.length > 0 && ( 52 - <div className="flex flex-col gap-1.5"> 53 - <span className="text-xs font-semibold text-slate-400 uppercase tracking-wider flex items-center gap-1"> 54 - <FileCode size={12} /> Files 55 - </span> 56 - <div className="flex flex-wrap gap-1.5"> 57 - {session.filesChanged.slice(0, 5).map((f, i) => ( 58 - <span key={i} className="text-xs px-2 py-1 bg-slate-50 text-slate-600 rounded border border-slate-100 font-mono"> 59 - {f.split('/').pop()} 60 - </span> 61 - ))} 62 - {session.filesChanged.length > 5 && ( 63 - <span className="text-xs px-2 py-1 text-slate-400">+{session.filesChanged.length - 5} more</span> 64 - )} 65 - </div> 66 - </div> 67 - )} 68 - 69 - {session.toolsUsed.length > 0 && ( 70 - <div className="flex flex-col gap-1.5"> 71 - <span className="text-xs font-semibold text-slate-400 uppercase tracking-wider flex items-center gap-1"> 72 - <Wrench size={12} /> Tools 73 - </span> 74 - <div className="flex flex-wrap gap-1.5"> 75 - {session.toolsUsed.map((t, i) => ( 76 - <span key={i} className="text-xs px-2 py-1 bg-orange-50 text-orange-700 rounded border border-orange-100"> 77 - {t} 78 - </span> 79 - ))} 80 - </div> 81 - </div> 82 - )} 83 - </div> 84 - </div> 85 - </div> 86 - ); 87 - }