| 527 | } |
| 528 | |
| 529 | function buildStatus(options = {}) { |
| 530 | const normalizedOptions = normalizeOptions(options); |
| 531 | const nowDate = getNow(normalizedOptions); |
| 532 | const mergedOptions = { |
| 533 | ...normalizedOptions, |
| 534 | nowDate, |
| 535 | }; |
| 536 | const homeDir = getHomeDir(normalizedOptions); |
| 537 | const { errors, transcriptPaths } = findTranscriptPaths(normalizedOptions); |
| 538 | const sessions = []; |
| 539 | |
| 540 | for (const transcriptPath of transcriptPaths) { |
| 541 | try { |
| 542 | sessions.push(analyzeTranscript(transcriptPath, mergedOptions)); |
| 543 | } catch (error) { |
| 544 | errors.push({ |
| 545 | code: error.code || null, |
| 546 | message: error.message, |
| 547 | transcriptPath, |
| 548 | }); |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | sessions.sort((left, right) => { |
| 553 | if (left.state !== right.state) { |
| 554 | return left.state === 'attention' ? -1 : 1; |
| 555 | } |
| 556 | return String(right.lastEventAt || '').localeCompare(String(left.lastEventAt || '')); |
| 557 | }); |
| 558 | |
| 559 | return { |
| 560 | generatedAt: nowDate.toISOString(), |
| 561 | errors, |
| 562 | schemaVersion: 'ecc.loop-status.v1', |
| 563 | sessions, |
| 564 | source: { |
| 565 | bashTimeoutSeconds: normalizedOptions.bashTimeoutSeconds, |
| 566 | homeDir, |
| 567 | limit: normalizedOptions.limit, |
| 568 | transcriptCount: transcriptPaths.length, |
| 569 | transcriptRoot: path.join(homeDir, '.claude', 'projects'), |
| 570 | wakeGraceMultiplier: normalizedOptions.wakeGraceMultiplier, |
| 571 | }, |
| 572 | }; |
| 573 | } |
| 574 | |
| 575 | function formatSignals(signals) { |
| 576 | if (signals.length === 0) { |