this repo has no description
0
fork

Configure Feed

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

Add work type classification for session scope

Classify files as frontend/backend based on path patterns.
Include simplified scope (frontend, backend, or both) in transcript
for LLM to use when framing summaries.

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

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

alice 569f6259 584948f0

+222
+222
src/core/session-reader.ts
··· 210 210 } 211 211 212 212 /** 213 + * Work type classification based on files changed 214 + */ 215 + export type WorkType = 'feature' | 'infrastructure' | 'tests' | 'docs' | 'mixed'; 216 + 217 + export interface WorkScope { 218 + frontend: number; 219 + backend: number; 220 + tests: number; 221 + types: number; 222 + config: number; 223 + docs: number; 224 + } 225 + 226 + export interface WorkClassification { 227 + type: WorkType; 228 + signals: string[]; // Human-readable explanation of why 229 + scope: WorkScope; 230 + scopeSummary: string; // e.g., "frontend, backend" or "tests" 231 + } 232 + 233 + /** 234 + * Check if a file path looks like frontend code 235 + */ 236 + function isFrontend(file: string): boolean { 237 + const lower = file.toLowerCase(); 238 + return ( 239 + lower.includes('/components/') || 240 + lower.includes('/pages/') || 241 + lower.includes('/screens/') || 242 + lower.includes('/views/') || 243 + lower.includes('/ui/') || 244 + lower.includes('/app/') || 245 + lower.includes('/apps/web/') || 246 + lower.includes('/web/') || 247 + lower.includes('/frontend/') || 248 + lower.includes('/client/') || 249 + lower.endsWith('.tsx') || 250 + lower.endsWith('.jsx') || 251 + lower.endsWith('.css') || 252 + lower.endsWith('.scss') 253 + ); 254 + } 255 + 256 + /** 257 + * Check if a file path looks like backend code 258 + */ 259 + function isBackend(file: string): boolean { 260 + const lower = file.toLowerCase(); 261 + return ( 262 + lower.includes('/api/') || 263 + lower.includes('/server/') || 264 + lower.includes('/services/') || 265 + lower.includes('/lib/') || 266 + lower.includes('/core/') || 267 + lower.includes('/packages/') || 268 + lower.includes('/backend/') || 269 + lower.includes('/handlers/') || 270 + lower.includes('/routes/') || 271 + lower.includes('/controllers/') || 272 + lower.includes('/models/') || 273 + lower.includes('/utils/') || 274 + (lower.endsWith('.ts') && !lower.endsWith('.test.ts') && !lower.endsWith('.spec.ts') && !lower.endsWith('.d.ts') && !isFrontend(file)) 275 + ); 276 + } 277 + 278 + /** 279 + * Classify the type of work based on file paths 280 + */ 281 + export function classifyWork(files: string[]): WorkClassification { 282 + const signals: string[] = []; 283 + const scope: WorkScope = { 284 + frontend: 0, 285 + backend: 0, 286 + tests: 0, 287 + types: 0, 288 + config: 0, 289 + docs: 0, 290 + }; 291 + 292 + let featureFiles = 0; 293 + 294 + for (const file of files) { 295 + const lower = file.toLowerCase(); 296 + const filename = file.split('/').pop() || ''; 297 + 298 + // Tests 299 + if ( 300 + lower.includes('.test.') || 301 + lower.includes('.spec.') || 302 + lower.includes('__tests__') || 303 + lower.includes('/test/') || 304 + lower.includes('/tests/') 305 + ) { 306 + scope.tests++; 307 + continue; 308 + } 309 + 310 + // Types/interfaces 311 + if ( 312 + filename === 'types.ts' || 313 + filename === 'interfaces.ts' || 314 + lower.endsWith('.d.ts') || 315 + lower.includes('/types/') || 316 + lower.includes('/interfaces/') 317 + ) { 318 + scope.types++; 319 + continue; 320 + } 321 + 322 + // Config/devops 323 + if ( 324 + lower.includes('.config.') || 325 + lower.includes('/config/') || 326 + lower.includes('.github/') || 327 + lower.includes('dockerfile') || 328 + lower.includes('.yml') || 329 + lower.includes('.yaml') || 330 + filename.startsWith('.') || 331 + filename === 'package.json' || 332 + filename === 'tsconfig.json' 333 + ) { 334 + scope.config++; 335 + continue; 336 + } 337 + 338 + // Docs 339 + if ( 340 + lower.endsWith('.md') || 341 + lower.includes('/docs/') || 342 + lower.includes('/documentation/') 343 + ) { 344 + scope.docs++; 345 + continue; 346 + } 347 + 348 + // Feature work - classify as frontend or backend 349 + featureFiles++; 350 + if (isFrontend(file)) { 351 + scope.frontend++; 352 + } else if (isBackend(file)) { 353 + scope.backend++; 354 + } else { 355 + // Default to backend for unclassified .ts files 356 + scope.backend++; 357 + } 358 + } 359 + 360 + const total = files.length; 361 + if (total === 0) { 362 + return { 363 + type: 'mixed', 364 + signals: ['no files changed'], 365 + scope, 366 + scopeSummary: '', 367 + }; 368 + } 369 + 370 + // Build scope summary - simplified to frontend/backend/both 371 + let scopeSummary = ''; 372 + const hasFrontend = scope.frontend > 0; 373 + const hasBackend = scope.backend > 0; 374 + if (hasFrontend && hasBackend) { 375 + scopeSummary = 'frontend, backend'; 376 + } else if (hasFrontend) { 377 + scopeSummary = 'frontend'; 378 + } else if (hasBackend) { 379 + scopeSummary = 'backend'; 380 + } else if (scope.tests > 0) { 381 + scopeSummary = 'tests'; 382 + } else if (scope.docs > 0) { 383 + scopeSummary = 'docs'; 384 + } else if (scope.config > 0) { 385 + scopeSummary = 'config'; 386 + } 387 + 388 + // Determine primary type (>50% of files) 389 + const threshold = total * 0.5; 390 + 391 + if (scope.tests > threshold) { 392 + signals.push(`${scope.tests}/${total} files are tests`); 393 + return { type: 'tests', signals, scope, scopeSummary }; 394 + } 395 + 396 + if (scope.docs > threshold) { 397 + signals.push(`${scope.docs}/${total} files are documentation`); 398 + return { type: 'docs', signals, scope, scopeSummary }; 399 + } 400 + 401 + if (scope.types + scope.config > threshold) { 402 + if (scope.types > scope.config) { 403 + signals.push(`${scope.types}/${total} files are types`); 404 + } else { 405 + signals.push(`${scope.config}/${total} files are config`); 406 + } 407 + return { type: 'infrastructure', signals, scope, scopeSummary }; 408 + } 409 + 410 + if (featureFiles > threshold) { 411 + signals.push(`${featureFiles}/${total} files are feature code`); 412 + return { type: 'feature', signals, scope, scopeSummary }; 413 + } 414 + 415 + // Mixed - build a description 416 + if (featureFiles > 0) signals.push(`${featureFiles} feature`); 417 + if (scope.tests > 0) signals.push(`${scope.tests} test`); 418 + if (scope.types > 0) signals.push(`${scope.types} type`); 419 + if (scope.config > 0) signals.push(`${scope.config} config`); 420 + if (scope.docs > 0) signals.push(`${scope.docs} doc`); 421 + 422 + return { type: 'mixed', signals, scope, scopeSummary }; 423 + } 424 + 425 + /** 213 426 * Create a condensed transcript for LLM summarization 214 427 * Leads with action summary (files changed) to ensure implementation work is captured 215 428 */ ··· 250 463 } 251 464 } 252 465 } 466 + 467 + // Classify the work based on file paths 468 + const allFiles = [...filesWritten, ...filesEdited]; 469 + const classification = classifyWork(allFiles); 470 + parts.push(`WORK TYPE: ${classification.type}`); 471 + if (classification.scopeSummary) { 472 + parts.push(`SCOPE: ${classification.scopeSummary}`); 473 + } 474 + parts.push(''); 253 475 254 476 // Show action summary at the TOP 255 477 if (filesWritten.length > 0) {