#!/usr/bin/env node /** * check-gray-expiry.js · 2026-04-25 * UserPromptSubmit hook. Scans state/gray-observations/*.json, if any expired * emits prominent banner via additionalContext so user & AI see it on session start. */ 'use strict'; const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..'); const DIR = path.join(ROOT, 'state', 'gray-observations'); function loadObservations() { if (!fs.existsSync(DIR)) return []; const out = []; for (const name of fs.readdirSync(DIR)) { if (!name.endsWith('.json')) continue; try { const j = JSON.parse(fs.readFileSync(path.join(DIR, name), 'utf8')); j._file = name; out.push(j); } catch (_) {} } return out; } function main() { const now = Date.now(); const obs = loadObservations(); const expired = obs.filter(o => { if (o.resolved) return false; const exp = new Date(o.expires_at).getTime(); return Number.isFinite(exp) && exp <= now; }); if (expired.length === 0) process.exit(0); const lines = []; lines.push(''); lines.push('╔══════════════════════════════════════════════════════════════╗'); lines.push('║ ⚠️ 灰度观察到期提醒 · GRAY OBSERVATION EXPIRED ║'); lines.push('╠══════════════════════════════════════════════════════════════╣'); for (const o of expired) { lines.push('║ ID: ' + (o.observation_id || o._file).padEnd(56) + '║'); lines.push('║ 到期: ' + (o.expires_at || '').slice(0, 19).padEnd(54) + '║'); if (o.watched_flag) lines.push('║ 关注 flag: ' + String(o.watched_flag).padEnd(50) + '║'); if (o.on_expiry_action) { const act = String(o.on_expiry_action); for (let i = 0; i < act.length; i += 58) { lines.push('║ → ' + act.slice(i, i + 58).padEnd(58) + '║'); } } lines.push('║' + ' '.repeat(62) + '║'); } lines.push('║ 建议: 查看 manifest.jsonl 统计 → 决定 warn→enforce 升级 ║'); lines.push('║ 解决后: resolved:true 写入对应 .json 停止提醒 ║'); lines.push('╚══════════════════════════════════════════════════════════════╝'); lines.push(''); const output = { hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: lines.join('\n'), }, }; process.stdout.write(JSON.stringify(output)); process.exit(0); } try { main(); } catch (_) { process.exit(0); }