#!/usr/bin/env node // coord - SessionStart hook: inject this repo's pending coordination inbox // (directed messages + unseen broadcasts) as additionalContext. // // Thin Node wrapper (marketplace convention: hooks are .mjs) around the bash // engine scripts/coord-inbox.sh, which owns the mailbox semantics and is // covered by scripts/coord-selftest.sh. Zero dependencies. Always exits 0 - // a broken mailbox must never block a session. import { execFileSync } from 'node:child_process'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; function emit(context) { const out = { continue: true }; if (context) { out.hookSpecificOutput = { hookEventName: 'SessionStart', additionalContext: context }; } process.stdout.write(JSON.stringify(out) + '\n'); } try { const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || join(dirname(fileURLToPath(import.meta.url)), '..', '..'); // No identity resolution here. This used to resolve the repo itself and fall // back to process.cwd(), which made it a fourth independent copy of the // identity rule - and the only one that runs in production, so the engine's // guards were bypassed exactly where they mattered. The engine runs in this // same cwd and derives the identity from git alone. // // CLAUDE_COORD_REPO is the one exception, and it is a DECLARATION rather than // a derivation: a working surface that is not a git repo (~/repos, $HOME) has // nothing to derive from, so the read path declines silently and the surface // loses its injection with no error - loss wearing the shape of normal. The // operator sets this in that directory's settings to say which mailbox the // surface owns. It is not the pwd fallback returning: the fallback guessed, // this is written down, readable back, and deletable. Forwarded verbatim as // --repo, so it inherits the engine's rules - including that an explicit // override never claims .origin. Boundary rule holds: no mailbox logic here. const declared = process.env.CLAUDE_COORD_REPO; const script = join(pluginRoot, 'scripts', 'coord-inbox.sh'); const inbox = execFileSync('bash', declared ? [script, '--repo', declared] : [script], { stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' }); emit(inbox.trim() ? '== Repo coordination (unread messages) ==\n' + inbox : ''); } catch { emit(''); }