()
| 109 | .filter((name) => name.endsWith('.md')) |
| 110 | .filter((name) => { |
| 111 | try { |
| 112 | const stat = fs.lstatSync(path.join(dir, name)); |
| 113 | return stat.isFile() && !stat.isSymbolicLink(); |
| 114 | } catch { return false; } |
| 115 | }) |
| 116 | .sort((a, b) => b.localeCompare(a)) |
| 117 | .slice(0, 50) |
| 118 | .map((name) => { |
| 119 | const filePath = path.join(dir, name); |
| 120 | let modifiedAt = null; |
| 121 | try { modifiedAt = fs.statSync(filePath).mtime.toISOString(); } catch { /* render as unknown */ } |
| 122 | return { name, path: `.planning/handoffs/${name}`, modifiedAt }; |
| 123 | }) |
| 124 | .sort((a, b) => String(b.modifiedAt).localeCompare(String(a.modifiedAt)) || b.name.localeCompare(a.name)); |
| 125 | } catch { |
| 126 | return []; |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | function readDaemon() { |
| 131 | try { |
| 132 | const raw = fs.readFileSync(path.join(projectRoot, '.planning', 'daemon.json'), 'utf8'); |
| 133 | return JSON.parse(raw); |
| 134 | } catch { |
no test coverage detected