界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
开发与工程 · SKILL.md

cross-project-mail

cross-project-mail

【开发与工程】Use when sending or receiving messages between AI agents working in different sibling projects on the same host. Provides a flat-file mailbox (`~/.ai-mailbox/`), a `cpmail` CLI (send/list/read/ack/migrate/doctor), and a SessionStart hook that prints unread count per project. v1 is single-host trust model (no HMAC, no daemon, no MCP server) — cross-ma…

独立 GitHub 快照 · 自动分类 · 以原作者文档为准

技能说明

GitHub

name: cross-project-mail description: Use when sending or receiving messages between AI agents working in different sibling projects on the same host. Provides a flat-file mailbox (~/.ai-mailbox/), a cpmail CLI (send/list/read/ack/migrate/doctor), and a SessionStart hook that prints unread count per project. v1 is single-host trust model (no HMAC, no daemon, no MCP server) — cross-machine and adversarial trust deferred to v2 (M3). Subsumes the manual cross-repo-review.md Outbound/Inbound convention via a one-shot idempotent migrator.

cross-project-mail — Cross-Project AI Agent Mailbox (v1, M1 walking skeleton)

Overview

A flat-file mailbox so AI agents in sibling projects on one host can leave each other messages that survive session boundaries. Replaces the manual cross-repo-review.md Outbound/Inbound file convention with a real CLI + SessionStart notification + idempotent migrator.

v1 trust model: single-user, single-host. All agents on this host share user trust. The only enforced XPIA defense is <user_data> delimiter wrapping on read; HMAC / quarantine / sender-allowlist deferred to v2 (M3) where cross-host trust boundaries make them pay their way.

Not in v1: MCP server, FastMCP transport, polling daemon, mcp_agent_mail wrapper, cross-machine (AB) mode, VS Code Copilot Chat integration, wiki-MCP. See docs/plans/2026-05-16-cross-project-mail-v1-design.md for the staged roadmap and the rationale for de-scoping from the full Stage 2 design.

When to Use

  • Sending a message from an AI agent in project A to an agent (or any agent) in project B on the same host
  • Checking inbox at session start (SessionStart hook does this automatically)
  • Migrating an existing cross-repo-review.md file to mailbox messages (one-shot, idempotent)
  • Auditing the mailbox layout (cpmail doctor)
  • Replying to a prior message or starting a thread

When NOT to Use

  • Cross-machine messaging (deferred to M3 — use the in-flight git mirror or wait for v2)
  • Adversarial cross-agent security (v1 trusts all local agents; v2 adds HMAC + quarantine)
  • High-volume messaging (>100/day) — the design budget is ≤100 msg/day across all projects
  • Real-time / sub-second latency — SessionStart-hook polling is session-granular, not push
  • Substitute for git commit messages or PR descriptions — those remain authoritative for code change context

Quick Start

# Install (one-time, per host)
bash ~/.claude/skills/cross-project-mail/install/install.sh

# Send a message
echo "Reviewed your Windows installer fix — looks good, merging" | \
  cpmail send --to vs-code-foundry --subject "installer PR ack" --source-type human

# List unread for current project
cpmail list --unread

# Read a specific message (body is wrapped in <user_data>...</user_data>)
cpmail read 01HXY7K3M9TBVN8P4ZQGRJ2WAD

# Acknowledge (moves to .acked/)
cpmail ack 01HXY7K3M9TBVN8P4ZQGRJ2WAD

# Migrate an existing cross-repo-review.md (idempotent — safe to re-run)
cpmail migrate --from /path/to/vs-code-foundry/cross-repo-review.md

# Validate mailbox layout
cpmail doctor

Hard Rules

Command Reference

CommandPurpose
cpmail send --to <project> [--agent <slug>] --subject <s> [--source-type <t>] [--reply-to <ulid>] [--label <l>]... [--from <stdin|@file>]Compose and write a message envelope. Body from stdin or @path/to/file.
cpmail list [--project <slug>] [--unread|--all|--acked] [--limit N] [--since <iso>]List messages in an inbox. Defaults to current project + unread.
cpmail read <ulid>Print frontmatter + body. Body is wrapped in <user_data>...</user_data>.
cpmail ack <ulid>Mark as acked. Moves file to inbox/<project>/.acked/.
cpmail migrate --from <path> [--dry-run]Convert a cross-repo-review.md file into mailbox messages. Idempotent.
cpmail doctor [--mailbox <path>]Validate layout, report orphans, corrupted frontmatter, unknown enums.
cpmail _detect-project [--from-cwd <path>]Internal: detect current project name. Used by SessionStart hook.
cpmail --helpShow all commands.

Exit Codes

  • 0 — success
  • 1 — runtime error (filesystem, permissions, etc.)
  • 2 — invalid input (bad schema, missing required field, invalid enum value)
  • 3 — not found (message id, project, file)

Anti-Patterns

Anti-PatternWhy it failsCorrect
cat ~/.ai-mailbox/inbox/foundry-lab/01HXY...mdBypasses the <user_data> delimiter wrap — exposes prompt-injection surfacecpmail read 01HXY...
Hand-editing a message fileSkips schema validation; may corrupt the closed-set field whitelistUse cpmail send (re-send if you need to amend); cpmail ack to close
Sending with source_type: human for an AI-summarized web fetchFalse provenance — recipient under-applies skepticismUse source_type: web_fetch_quoted when body quotes web content; ai_summary when AI-condensed
Polling ~/.ai-mailbox/inbox/ from a tight loopDoS yourself; the hook is for SessionStart onlyUse cpmail list --unread on demand
Storing secrets in message bodyThe mailbox is plain markdown on disk, mode 0600 but not encryptedReference secrets by path or env-var name; never inline them
Cross-machine sync via rsync ~/.ai-mailbox/v1 has no consistency model for concurrent writers across hostsWait for M3 (mcp_agent_mail wrapper) or use git mirror with careful single-writer discipline
Sending without a subjectAll commands require subject; sender-discipline (recipient grep relies on it)Include --subject "..."; ≤200 chars
Modifying ~/.ai-mailbox/inbox/*/.acked/ filesThe ack archive is intentionally read-once-write-onceNever; use cpmail doctor if state seems wrong

File Layout (Reference)

~/.ai-mailbox/
├── inbox/<recipient-project>/
│   ├── <ulid>.md              # unread messages
│   └── .acked/<ulid>.md       # acked archive
├── outbox/<sender-project>/   # symlinks back into inbox/
└── .config.yaml               # optional path overrides, project aliases

Message Envelope (Reference)

See schemas/envelope.v1.json for the formal JSON Schema. Example:

---
schema_version: "1"
msg_id: 01HXY7K3M9TBVN8P4ZQGRJ2WAD
sent_at: "2026-05-17T14:32:10Z"
sender:
  project: vs-code-foundry
  agent: claude_code
  host: workstation
recipient:
  project: foundry-lab
  agent: null
subject: "Windows installer cmd+ps1 hardening"
source_type: human
reply_to: null
thread_id: null
labels: [cross-repo-review, installer]
ack_state: unread
attachments: []
---

Two cross-platform installer bugs that no Linux session could surface...

SessionStart Hook

Installed at ~/.claude/skills/cross-project-mail/hooks/session-start.sh. Budget: <100ms. Wired into Claude Code via ~/.claude/settings.json hooks.SessionStart. Codex + Gemini parity via symlinks. Copilot CLI: documented gap (#971) — workaround is a shell alias.

Output (only when unread > 0):

[mail] 3 unread for foundry-lab (run: cpmail list --unread)

Tests

tests/test_cpmail.py covers ~30 cases: envelope round-trip, CLI send/list/read/ack, SessionStart hook (0 / N unread), migrator idempotency, doctor on broken mailbox, performance budgets (p95 hook <100ms with 100 unread; cpmail list p95 <200ms with 1000 messages). Run: cd tests && python3 -m pytest.

Cross-References

  • Design doc: docs/plans/2026-05-16-cross-project-mail-v1-design.md
  • M3 follow-up (mcp_agent_mail wrapper for cross-machine): preserved in archive at progress/forge-cycle-s034/stage2-design/
  • M4 follow-up (wiki-MCP for global knowledge search): separate cycle, not in this skill
  • Related: process-observation (telemetry sink we may use in M2), wiki (M4 substrate)
我的收藏