(projectRoot, limit, now)
| 534 | .slice(0, limit); |
| 535 | } |
| 536 | |
| 537 | function readHookActivity(projectRoot, limit, now) { |
| 538 | const telemetryDir = path.join(projectRoot, '.planning', 'telemetry'); |
| 539 | const errors = tailJsonl(path.join(telemetryDir, 'hook-errors.jsonl'), 200); |
| 540 | const timings = tailJsonl(path.join(telemetryDir, 'hook-timing.jsonl'), 50); |
| 541 | |
| 542 | return timings |
| 543 | .filter((entry) => entry.hook || entry.metric || entry.event) |
| 544 | .map((entry) => { |
| 545 | const timestamp = eventTimestamp(entry); |
| 546 | const hook = entry.hook || entry.metric || entry.event || 'hook'; |
| 547 | const blocked = errors.some((error) => { |
| 548 | if ((error.hook || '') !== hook) return false; |
| 549 | const left = new Date(eventTimestamp(error) || 0).getTime(); |
| 550 | const right = new Date(timestamp || 0).getTime(); |
| 551 | return Math.abs(left - right) <= 1000; |
| 552 | }); |
| 553 | return { |
| 554 | timestamp, |
| 555 | relative: relativeTime(timestamp, now), |
| 556 | hook, |
| 557 | durationMs: typeof entry.duration_ms === 'number' ? entry.duration_ms : null, |
| 558 | outcome: blocked ? 'block' : (entry.outcome || entry.status || 'pass'), |
| 559 | }; |
| 560 | }) |
| 561 | .sort((left, right) => new Date(right.timestamp || 0).getTime() - new Date(left.timestamp || 0).getTime()) |
| 562 | .slice(0, limit); |
| 563 | } |
| 564 | |
| 565 | /** |
no test coverage detected