(data)
| 230 | } |
| 231 | } |
| 232 | |
| 233 | function readPendingIntents(projectRoot) { |
| 234 | const intents = new Map(); |
| 235 | const directory = path.join(projectRoot, '.planning', 'intents', 'pending'); |
| 236 | try { |
| 237 | if (!fs.existsSync(directory) || fs.lstatSync(directory).isSymbolicLink()) return intents; |
| 238 | const realRoot = fs.realpathSync(projectRoot); |
| 239 | const realDirectory = fs.realpathSync(directory); |
| 240 | if (escapesRoot(realRoot, realDirectory)) return intents; |
| 241 | for (const name of fs.readdirSync(realDirectory).filter((entry) => entry.endsWith('.json')).sort()) { |
| 242 | const file = path.join(realDirectory, name); |
| 243 | if (fs.lstatSync(file).isSymbolicLink()) continue; |
| 244 | const record = JSON.parse(fs.readFileSync(file, 'utf8')); |
| 245 | const intent = record.protocol_intent; |
| 246 | if (!intent || record.result?.outcome !== 'accepted' || typeof record.capability !== 'string') continue; |
| 247 | intents.set(intent.operation_id, { intent_id: intent.intent_id, action: record.capability }); |
| 248 | } |
| 249 | } catch { |
| 250 | return new Map(); |
| 251 | } |
| 252 | return intents; |
| 253 | } |
| 254 | |
| 255 | function source(pathname, status, detail, count = null, unreadable = []) { |
| 256 | return { path: pathname.replace(/\\/g, '/'), status, detail, count, unreadable }; |
| 257 | } |
| 258 | |
| 259 | function inspectDirectory(projectRoot, relativeDir, options = {}) { |
| 260 | const absolute = path.join(projectRoot, relativeDir); |
| 261 | if (!fs.existsSync(absolute)) return source(relativeDir, 'unknown', 'source is absent'); |
| 262 | let names; |
| 263 | try { |
| 264 | names = fs.readdirSync(absolute).filter((name) => !options.filter || options.filter(name)); |
| 265 | } catch (error) { |
| 266 | return source(relativeDir, 'unreadable', error.message, null, [relativeDir]); |
| 267 | } |
| 268 | if (names.length === 0) return source(relativeDir, 'empty', 'source exists and is empty', 0); |
| 269 | const unreadable = []; |
| 270 | if (options.json) { |
| 271 | for (const name of names) { |
| 272 | try { JSON.parse(fs.readFileSync(path.join(absolute, name), 'utf8')); } |
| 273 | catch { unreadable.push(`${relativeDir}/${name}`.replace(/\\/g, '/')); } |
| 274 | } |
| 275 | } |
| 276 | if (options.jsonl) { |
| 277 | for (const name of names) { |
| 278 | let lines; |
| 279 | try { lines = fs.readFileSync(path.join(absolute, name), 'utf8').split(/\r?\n/).filter(Boolean); } |
| 280 | catch { unreadable.push(`${relativeDir}/${name}`.replace(/\\/g, '/')); continue; } |
| 281 | if (lines.some((line) => { try { JSON.parse(line); return false; } catch { return true; } })) { |
| 282 | unreadable.push(`${relativeDir}/${name}`.replace(/\\/g, '/')); |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | if (unreadable.length) return source(relativeDir, 'unreadable', `${unreadable.length} file(s) could not be parsed`, names.length, unreadable); |
| 287 | return source(relativeDir, options.midRun ? 'mid-run' : 'healthy', options.midRun ? 'work is active' : 'source is readable', names.length); |
| 288 | } |
| 289 |
no test coverage detected