* Read session transcript entries and compute prompt count and memory access * count. Pre-compact entries are skipped — the N-shot count and memory-access * count should reflect only the current conversation arc, not accumulated * prompts from before a compaction boundary.
()
| 237 | * prompts from before a compaction boundary. |
| 238 | */ |
| 239 | async function getTranscriptStats(): Promise<{ |
| 240 | promptCount: number |
| 241 | memoryAccessCount: number |
| 242 | }> { |
| 243 | try { |
| 244 | const filePath = getTranscriptPath() |
| 245 | const fileSize = (await stat(filePath)).size |
| 246 | // Fused reader: attr-snap lines (84% of a long session by bytes) are |
| 247 | // skipped at the fd level so peak scales with output, not file size. The |
| 248 | // one surviving attr-snap at EOF is a no-op for the count functions |
| 249 | // (neither checks type === 'attribution-snapshot'). When the last |
| 250 | // boundary has preservedSegment the reader returns full (no truncate); |
| 251 | // the findLastIndex below still slices to post-boundary. |
| 252 | const scan = await readTranscriptForLoad(filePath, fileSize) |
| 253 | const buf = scan.postBoundaryBuf |
| 254 | const entries = parseJSONL<Entry>(buf) |
| 255 | const lastBoundaryIdx = entries.findLastIndex( |
| 256 | e => |
| 257 | e.type === 'system' && |
| 258 | 'subtype' in e && |
| 259 | e.subtype === 'compact_boundary', |
| 260 | ) |
| 261 | const postBoundary = |
| 262 | lastBoundaryIdx >= 0 ? entries.slice(lastBoundaryIdx + 1) : entries |
| 263 | return { |
| 264 | promptCount: countUserPromptsFromEntries(postBoundary), |
| 265 | memoryAccessCount: countMemoryFileAccessFromEntries(postBoundary), |
| 266 | } |
| 267 | } catch { |
| 268 | return { promptCount: 0, memoryAccessCount: 0 } |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | /** |
| 273 | * Get enhanced PR attribution text with Claude contribution stats. |
no test coverage detected