feat(plugin): per-session mailbox identity + mailbox-update command
The hook now derives a unique mailbox name from the session_id supplied on hook stdin, so two parallel Claude Code sessions in the same project get distinct mailboxes (e.g. `claude-a8b3c1d2`, `claude-d4e5f6a7`) instead of colliding on a shared env value. An optional CLAUDE_MAILBOX_NAME base prefix flavors the names as `<base>-<sid>`. Adds: - `claude-mailbox session-announce` subcommand for the new SessionStart hook, which prints the current session's mailbox name to context - `/claude-mailbox:mailbox-update` slash command for `npm update` + daemon restart - stdin parsing helpers (parseHookStdin, deriveSessionName) with unit tests; the doctor no longer needs a mandatory name prompt
This commit is contained in:
@@ -2,6 +2,45 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
export interface HookStdinPayload {
|
||||
session_id?: string;
|
||||
hook_event_name?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export function parseHookStdin(raw: string | null | undefined): HookStdinPayload | null {
|
||||
if (!raw || !raw.trim()) return null;
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (parsed && typeof parsed === "object") return parsed as HookStdinPayload;
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function readStdinIfPiped(): string | null {
|
||||
if (process.stdin.isTTY) return null;
|
||||
try {
|
||||
return readFileSync(0, "utf8");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function shortSessionId(sessionId: string): string {
|
||||
const hex = sessionId.replace(/[^0-9a-fA-F]/g, "").toLowerCase();
|
||||
if (hex.length >= 8) return hex.slice(0, 8);
|
||||
return sessionId.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 8) || "00000000";
|
||||
}
|
||||
|
||||
export function deriveSessionName(sessionId: string, base?: string | null): string {
|
||||
const short = shortSessionId(sessionId);
|
||||
const trimmed = (base ?? "").trim();
|
||||
if (trimmed) return `${trimmed}-${short}`;
|
||||
return `claude-${short}`;
|
||||
}
|
||||
|
||||
export interface HookMessage {
|
||||
id: number;
|
||||
from: string;
|
||||
|
||||
Reference in New Issue
Block a user