#!/usr/bin/env node /** * sessionStart hook — inject the facts a session should never have to ask for. * * 1. Are the model lanes bound, and is the picker running the model we recorded? * 2. What did the last session leave as the next action? * 3. Is any capped context file over its cap right now? * * All three are deterministic file/state checks. The point of doing them here rather * than in a prompt is that a rule asking the model to "check the caps" is a request; * this is an observation. * * Contract: stdin is the sessionStart JSON payload; stdout is * {"additional_context": "..."}. This hook is fire-and-forget — Cursor logs the * response but never blocks session creation on it. It fails open by design: * any error prints an empty object and exits 0. */ import { readFileSync, existsSync } from "node:fs"; import { join } from "node:path"; const readAll = () => new Promise((resolve) => { let data = ""; process.stdin.setEncoding("utf8"); process.stdin.on("data", (c) => (data += c)); process.stdin.on("end", () => resolve(data)); setTimeout(() => resolve(data), 2000).unref(); }); const read = (p) => { try { return existsSync(p) ? readFileSync(p, "utf8") : null; } catch { return null; } }; /** * Real content lines: not blank, not a heading, not the file's instructional blockquote, * not a table rule, not an HTML comment, and not one of the shipped `_None yet._` / * `[placeholder]` template rows. Counting boilerplate would report a freshly installed * kit as already consuming its caps. */ const isPlaceholder = (l) => /^[-|*\s]*_?(none|no )/i.test(l) || /^\|?\s*_?\[/.test(l) || /^-\s*\*\(/.test(l) || /^\|\s*\[/.test(l); const contentLines = (text) => text .split("\n") .map((l) => l.trim()) .filter( (l) => l && !l.startsWith("#") && !l.startsWith(">") && !l.startsWith("