repo-mailbox/hooks/scripts/session-start.mjs
Kjell Tore Guttormsen 0e6014fe98 feat(plugin): package as a Claude Code marketplace plugin
- .claude-plugin/plugin.json v0.1.0 (auto_discover, MIT)
- SessionStart hook: thin zero-dep .mjs wrapper (marketplace convention)
  around scripts/coord-inbox.sh, emitting the additionalContext envelope;
  always exits 0. Smoke-tested: empty mailbox -> bare continue, pending
  message -> injected with UNTRUSTED framing.
- coord-send skill bundled; examples and description use generic repo
  names only (coordination metadata never reaches a public surface).
- README (English; documents the deliver-until-done lifecycle correctly,
  review §7), CHANGELOG, LICENSE (MIT), CLAUDE.md, package.json +
  node --test wrapper around the bash selftest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBbjgS5A55RVavoyjJC4FX
2026-07-24 06:46:09 +02:00

42 lines
1.5 KiB
JavaScript

#!/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 { basename, 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)), '..', '..');
let repoRoot = '';
try {
repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'],
{ stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' }).trim();
} catch {
// not a git repo: fall back to the working directory below
}
if (!repoRoot) repoRoot = process.cwd();
const inbox = execFileSync('bash',
[join(pluginRoot, 'scripts', 'coord-inbox.sh'), '--repo', basename(repoRoot)],
{ stdio: ['ignore', 'pipe', 'ignore'], encoding: 'utf8' });
emit(inbox.trim() ? '== Repo coordination (unread messages) ==\n' + inbox : '');
} catch {
emit('');
}