feat(naming)!: auto-derive mailbox name from project + runtime rename
Mailbox names are now built as <project>-<session-short>, where <project> is the sanitized git-repo basename (or cwd basename) — no more env-var prefix step. Sessions can re-tag themselves at runtime via the new mcp__mailbox__rename tool (POST /v1/rename), which transfers all pending messages to the new name in a single transaction. Peers using the old name re-discover via list_mailboxes. BREAKING: \$CLAUDE_MAILBOX_NAME is no longer read. Existing setups that relied on the env-var prefix should remove it from .claude/settings.json; the prefix now comes from the working directory automatically. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,15 @@ function nowIso(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
export type RenameFailure = "invalid" | "source-missing" | "target-exists";
|
||||
|
||||
export class RenameError extends Error {
|
||||
constructor(message: string, public readonly reason: RenameFailure) {
|
||||
super(message);
|
||||
this.name = "RenameError";
|
||||
}
|
||||
}
|
||||
|
||||
function parseDate(s: string | null | undefined): Date | null {
|
||||
if (!s) return null;
|
||||
const normalized = s.includes("T") ? s : s.replace(" ", "T") + (s.endsWith("Z") ? "" : "Z");
|
||||
@@ -166,6 +175,35 @@ export class MailboxStore {
|
||||
});
|
||||
}
|
||||
|
||||
rename(from: string, to: string): { from: string; to: string; messagesTransferred: number } {
|
||||
const oldName = from.trim();
|
||||
const newName = to.trim();
|
||||
if (!oldName) throw new RenameError("from is required", "invalid");
|
||||
if (!newName) throw new RenameError("to is required", "invalid");
|
||||
if (oldName === newName) {
|
||||
this.upsertMailbox(oldName);
|
||||
return { from: oldName, to: newName, messagesTransferred: 0 };
|
||||
}
|
||||
|
||||
return runInTransaction(this.db, () => {
|
||||
const source = this.stmts.findMailbox.get(oldName) as unknown as MailboxRow | undefined;
|
||||
if (!source) throw new RenameError(`Mailbox '${oldName}' does not exist.`, "source-missing");
|
||||
const target = this.stmts.findMailbox.get(newName) as unknown as MailboxRow | undefined;
|
||||
if (target) throw new RenameError(`Mailbox '${newName}' already exists.`, "target-exists");
|
||||
|
||||
const now = nowIso();
|
||||
this.stmts.insertMailbox.run(newName, source.created_at, now);
|
||||
const movedTo = this.db
|
||||
.prepare("UPDATE messages SET to_mailbox = ? WHERE to_mailbox = ?")
|
||||
.run(newName, oldName);
|
||||
this.db
|
||||
.prepare("UPDATE messages SET from_mailbox = ? WHERE from_mailbox = ?")
|
||||
.run(newName, oldName);
|
||||
this.db.prepare("DELETE FROM mailboxes WHERE name = ?").run(oldName);
|
||||
return { from: oldName, to: newName, messagesTransferred: Number(movedTo.changes ?? 0) };
|
||||
});
|
||||
}
|
||||
|
||||
listMailboxes(forName?: string): MailboxInfo[] {
|
||||
const rows = this.stmts.listMailboxes.all() as unknown as MailboxRow[];
|
||||
const pendingMap = new Map<string, number>();
|
||||
|
||||
Reference in New Issue
Block a user