operate-agent-mail
ALWAYS invoke this skill when a workflow registers a mail identity, sends a message record, lists a recipient's records, reads or acknowledges one message, or waits for a later record in the agent-mail store. NEVER construct an `am` command or derive the mail project key without this skill.
- License 许可
- MIT
- 最近更新
- 2026-11-11
- Stars
- ★ 1
- Forks
- 1
01 前言 Front Matter
- name
- operate-agent-mail
- description
- ALWAYS invoke this skill when a workflow registers a mail identity, sends a message record, lists a recipient's records, reads or acknowledges one message, or waits for a later record in the agent-mail store. NEVER construct an `am` command or derive the mail project key without this skill.
- allowed-tools
- Write, Bash(python3 "${CLAUDE_SKILL_DIR}/scripts/agent_mail.py":*)
- argument-hint
- <operation or JSON request>
02 资源文件 1
- 脚本scripts/agent_mail.py60.7 KB
03 SKILL.md 正文
<objective>
A versioned JSON agent-mail operation result — a registered identity, a delivered record with its store-assigned id, the recipient's listed records, the message a read or an acknowledgement judged, or the first later record or a named timeout or unreachable outcome of a bounded wait — under the project key of the store the operation acted on, or that key alone as an unversioned projectKey answer, with every store identity preserved verbatim.
</objective>
<operation_surface>
The source-owned request operations, each submitted as a JSON request to run:
| Operation | Arguments | Result data |
|---|---|---|
register |
agent, program, model; optional task, displayName |
the registered agent name and store id |
send |
record |
the record with its store-assigned id |
list |
agent; optional allRecords, includeBodies, limit |
records listed for that recipient |
read |
agent, messageId |
the agent and messageId the recipient judged |
acknowledge |
agent, messageId |
the agent and messageId the sender can now see judged |
wait |
agent, afterId, timeoutSeconds |
an outcome of received with the record, timeout, or unreachable |
One further form answers outside the request shape, so it takes no JSON request and the request-building steps of <workflow> do not reach it. A run request naming it is rejected as operation-unavailable; its command is in <invocation_forms>.
| CLI form | Arguments | Result data |
|---|---|---|
project-key |
none | projectKey |
The record and its delivery rules:
- Repository. Every operation accepts an optional
repositoryin itsarguments, an absolute path string. The operation then acts on the store under the project key of that repository; a request naming none acts on the store of the adapter's own working directory. A value that is no string or no absolute path is rejected asinvalid-schemabefore any command runs, and a path that resolves to no repository answersrepository-unresolved, never the key of the adapter's own working directory. The adapter reads no overlay or configuration to select a repository; the request names it. The result'sprojectKeyreports the store the operation acted on. - No registration on behalf. The adapter registers no identity for a caller: a send from a name the named store does not hold returns the store's failure as
command-failed, andregisteragainst that repository creates the identity. - Fields. A
recordcarries exactlyschema(1),kind,correlation,sender,recipient,subject,body, andackRequired; the store assignsidon delivery. - Kinds a sender writes.
order,fact,question,answer,delegation-request, and the four terminal handbacksdelegation-completed,delegation-failed,delegation-rejected, anddelegation-unavailable. On read-back the adapter reports each record's kind as one of those or asunclassified, andsubjectcarries the text to use in either case. Never derive a kind from a subject: the adapter owns the prefix it writes and the conditions a row meets to classify, and a row missing any of them reads asunclassifiedwith its subject verbatim. - Mapping. The adapter maps
correlationonto the store's thread,kindonto a subject prefix, andackRequiredonto the store's acknowledgement requirement, and reads each back; a store limit never shapes the record. A correlation the store's thread alphabet rejects — one with a character outside letters, digits,.,_, and-, a non-alphanumeric first character, or the prefixxc-— travels as anxc-escape the adapter writes and reads back to the original correlation, so any correlation round-trips. A correlation whose written thread id would exceed 128 characters is rejected asinvalid-schemabefore any command runs, and so is any text argument carrying a NUL byte. - Read state. Only
readandacknowledgechange a record's read state.listandwaitchange none, so looking at records never judges them.readrecords, visibly to the recipient alone, that the recipient judged one message.acknowledgeis a read the sender can observe. Neither stands in for the other: usereadfor a private judgment andacknowledgeonly when the sender is meant to see it. - List.
listreturns the records the recipient has not yet judged; withallRecordstrue it returns every record, read or unread. A request carryingunreadOnlyis rejected asinvalid-schema. - One recipient.
recipientnames one agent. A value carrying the store's,separator is rejected withinvalid-schemabefore any command runs. - Foreign rows. A row another sender wrote reads back rather than failing the listing: without a thread it reads with
correlation: null, classified by the kind rule above, andackRequiredis true for an acknowledgement status ofrequired,pending, orackedand false for every other status. A thread id that is no escape the adapter writes reads back verbatim ascorrelation. A row without the store'sid,from, orsubjectkey is a malformed store response and fails the read asinvalid-schema. - Labels. A position label is the display name the store records for an agent.
registerpasses an optionaldisplayNameto the store as the agent's label and registers only theagentname the request supplies. A listed record, with or without a body, and a send result carry the store'ssender_display_nameverbatim inresponsebeside the message,nullwhen the sender has no label; a send result also carriesto_display_names, which maps each recipient's stable name visible to the reader to its label. No label enters therecord. A label names no target:agent,sender, andrecipienttake the stable name, andmessageIdtakes the store id. - Wait.
waitlists the records ofagentin the complete form, read and unread records alike, until a record whose store id exceedsafterIdis present ortimeoutSecondspasses. Pass the id of the last record handled, or0for none, so a run that ends at any point loses no record.data.outcomeisreceivedwith the first suchrecord,timeoutwhen none arrived in time, orunreachablewhen the listing cannot reach back toafterId; the store's labels reach the result inresponseas forlist. A request withouttimeoutSecondsisinvalid-schemabefore any store command runs,timeoutSecondsis a whole number from 1 to 3600, and no store command outlives it. A wait keeps no cursor, state file, or lock, and changes no record's read state; judge a record withreadoracknowledge. Anunreachableoutcome means the record afterafterIdcannot be told from the listing, so a later record is never returned in its place; reconcile withlistandallRecords. - Liveness. No operation reports liveness, staleness, or session state, because the store's per-agent activity timestamp moves for reasons unrelated to a session being alive.
- Credential. A registration leaves the credential the store keeps for that name, and the next send from that name authenticates through it. The store offers no separate credential command, so the adapter makes none and no step of this skill refreshes it.
The project key is the repository's own common Git directory, so every worktree of one pool, the pool's bare repository, and the pool's main checkout resolve one mail project, and no checkout's deletion removes it. The adapter reads that directory for the repository a request names, or for its own working directory when the request names none, before every operation and reads no other working directory, environment variable, or parent path in its place. The lookup drops every variable that could make Git answer from something other than that directory — one naming a repository and one bounding where Git may look are known cases, not the only ones — and carries every other through, so a repository reachable only across a mount boundary still resolves. A working directory that is no repository yields repository-unresolved.
</operation_surface>
<workflow>
-
Interpret
$ARGUMENTSas one request operation with its arguments, or as a complete JSON request. When it is empty, run nothing and report that one operation from<operation_surface>is required; the adapter has no default operation. When it namesproject-key, take the CLI form in<invocation_forms>and stop; steps 2 to 4 govern request operations only. -
Build this source-owned request shape and set only the arguments the operation accepts:
{ "schemaVersion": 1, "operation": "send", "arguments": { "record": { "schema": 1, "kind": "fact", "correlation": "change-88-exec", "sender": "AmberGull", "recipient": "PeachFrog", "subject": "changeset pushed", "body": "Full commit SHA and branch, verbatim.", "ackRequired": true } } }An operation that acts on another repository's store carries
"repository": "<absolute path>"beside its otherarguments; omit it to act on the store of the adapter's own working directory. -
Write the request with the file-write tool to a file in the scratch directory the harness names for this session, under a file name unique to the agent and the request, and submit it through the input redirect in
<invocation_forms>. Where the harness names no scratch directory, use the one the environment designates, such as$RUNNER_TEMPon a hosted runner. -
For a
runrequest, accept onlystatus: "succeeded", which exits zero and carries exactly seven fields, every one of them preserved:schemaVersion,operation,status,commandExitCode,projectKey(the key of the store the operation acted on), the store'sresponse, anddata. A delivered message is therecordindatacarrying its store-assignedid. A request the adapter read and rejected answers instead withschemaVersion,operation— the requested operation, orunknownwhere the request named none —status(command-failed,invalid-schema,store-unavailable,repository-unresolved, oroperation-unavailable),detail, andcommandExitCodewhere a store command returned an exit code, and exits 1. Stdin the adapter cannot read as a JSON object is rejected before any operation is read, so that answer carriesschemaVersion,operationasunknown,status: "invalid-schema", anddetail, with nocommandExitCode, and exits 2. A failure of the store command itself, of any class, answersstore-unavailableorcommand-failedin the same versioned shape. Stop on the exactstatusanddetailof every failing form; none of them admits a fallback command, key, or store. Theproject-keyoperation answers in its own shape, stated with its form below.
</workflow>
<invocation_forms>
To submit a request, with <request-file> the file written in step 3:
python3 "${CLAUDE_SKILL_DIR}/scripts/agent_mail.py" run < <request-file>
To read the project key alone, for the adapter's own working directory:
python3 "${CLAUDE_SKILL_DIR}/scripts/agent_mail.py" project-key
To read the key of another repository, add the option --repository <absolute-path>; the form then answers that repository's key, or repository-unresolved when the path resolves to no repository, and never the key of its own working directory.
python3 "${CLAUDE_SKILL_DIR}/scripts/agent_mail.py" project-key --repository <absolute-path>
This form answers {"projectKey": "<absolute path>"} and exits zero, or {"status": "repository-unresolved", "detail": "<reason>"} and exits non-zero. It carries no schemaVersion, status on success, commandExitCode, response, or data, because it runs no store command.
</invocation_forms>
<constraints>
- ALWAYS execute the bundled script through
${CLAUDE_SKILL_DIR}— the skill loader substitutes that expression into this body before the command runs, so only this spelling reaches the shell as this skill's real directory. - NEVER import the script from another filesystem location or manufacture a path outside this skill directory — the substituted expression is the only route that resolves.
- NEVER copy
${CLAUDE_SKILL_DIR}into an agent definition or export it — it is no shell variable, so outside this body it yields an empty prefix rather than an error. - ALWAYS preserve store identities verbatim: message ids, thread ids, agent names, and timestamps, because downstream skills index on the literal and the operator compares it against the store.
- ALWAYS hold a bounded wait to one invocation and re-arm it with the id of the last record handled; the adapter loops only inside its timeout.
- ALWAYS supply arguments under the field names in
<operation_surface>and leave the mapping to the adapter: it alone turns a field into anamoption or a store field and reads it back, and it rejects an argument outside the operation's shape asinvalid-schemarather than dropping it. - ALWAYS register a position's own stable name only, with that position's own label when it has one; register no other name and no other position's label.
- NEVER select, route, or address by a label; the stable name or the store id selects every target, and a label may be shared by two agents and changes with its owner.
- NEVER pass a request through a heredoc, a pipe, or an inline argument, and NEVER remove the request file — a request carries code spans, quotes, and JSON that the dangerous-command guard refuses in those forms, and the harness clears its scratch directory.
- NEVER invoke raw
amcommands,amcommand help, or read the store's database. - NEVER derive the project key outside the adapter; it reads the key from the repository a request names, or from its own working directory when none is named, and no other working directory, environment variable, or parent path stands in for it.
- NEVER treat a read or an acknowledgement as agreement, ownership, authorization, or acceptance of a proposal; it records only that the recipient judged one message, and says nothing about its content.
- NEVER read or acknowledge a record on another recipient's behalf, and NEVER acknowledge when a private
readsuffices. - NEVER report or relay the registration token the store returns; the adapter removes it from every result.
</constraints>
<testing>
The bundled adapter is covered over generated request, record, and repository-lookup domains, with controlled CommandRunner implementations at the command boundary and real repositories behind the repository lookup:
- every operation accepts an optional
repository, maps to the same store command under that repository's key, and reports that key as the result'sprojectKey, and the project-key form given--repositoryanswers that repository's key; - a
repositorythat is no absolute path string runs no store command, and a named path that resolves to no repository answersrepository-unresolvedrather than the key of the adapter's working directory; - every registry operation's argument vector is read against the store CLI's captured usage text under the resolved project key;
- a real pool's linked worktree, bare repository, main checkout, and a symlinked route to one of them each resolve one key, while the pool's parent directory resolves none;
- every environment variable Git's own behaviour confirms moves the answer off the working directory — by answering with another repository, or with none — leaves the key unchanged, each alone and all together, read from the pool shape whose lookup Git confirmed it moves, while the parent directory still resolves none;
- a wait returns the first record past its id, read or unread, as a
receivedoutcome, atimeoutoutcome when none arrives, and anunreachableoutcome for a full listing that does not reach back to the id, under a controlled clock; listmaps to the store's non-marking listing in its unjudged form, or its complete form withallRecords, andreadandacknowledgeeach map to their own store command, never to each other's;- a list, a wait, and every operation other than
readandacknowledgeleave read state unchanged; - a wait without a timeout runs no store command, no store command inside a wait is given more time than remains, and a wait leaves no cursor, state file, or lock and runs neither a read nor an acknowledgement command;
- generated records round-trip through the store field mapping, including correlations the store's thread alphabet rejects, which round-trip through the
xc-escape; - a correlation whose written thread id exceeds 128 characters and a text argument carrying a NUL byte are rejected before any store command runs;
- every emitted result, including the failure of unparseable input, carries the versioned shape, and a runner error of any class maps to
store-unavailableorcommand-failed; - a second terminal handback of the same kind with different content is rejected as a conflicting handback naming the difference;
- a raw
amcommand is recognized in argument-vector and shell-string form; - an
order, itsdelegation-request, and its one correlated terminal handback are delivered through this capability's own send path; - repeated and conflicting terminal handbacks reduce to one result;
- the CLI run where no executable resolves returns
repository-unresolvedwith no fallback, for every operation; - every operation completes where only the adapter's own programs resolve;
- a captured registration response reaches the result without its token;
- a registration request's display name reaches the store as its label, and a request without one passes none;
- the store's
sender_display_nameon listed records with and without bodies and on send results,nullincluded, andto_display_nameson send results reach the result verbatim beside the message, with no label inside the record; - a label carried in a target position is passed as the identity and never resolved, and a store label never selects the sender or recipient of a record.
</testing>
<failure_modes>
A store keyed under the earlier derivation read back as an empty list. Claude listed the records of an agent that had been registered before the project key moved from the pool's main checkout path to the repository's own common Git directory, and took the empty result as "no messages". The registrations and message records were still in the store under the previous key, so the read was addressing a different project. A list that reads back empty for an agent known to be registered is a key mismatch rather than an absent-message state: re-register the agent under the current key with register before reading an empty result as an answer. Carrying the earlier project's records into the current key is no operation in <operation_surface>; it is a store-side change, so report the mismatch and the two keys and leave that change to whoever operates the store.
</failure_modes>
<success_criteria>
- A successful
runoperation is established only when the bundled script exits zero and emits exactly the seven fields of the versioned success result —schemaVersion: 1,operation,status: "succeeded",commandExitCode: 0,projectKey,response, anddata— without exposingamcommand grammar; a successfulproject-keyoperation is established only when it exits zero and emitsprojectKey. - Every record sent and read back carries the same
schema,kind,correlation,sender,recipient,subject,body, andackRequired, plus the store-assignedidon read. - A request naming a
repositoryacts on that repository's store, and its result'sprojectKeyis that repository's key. - An absent store or an unresolvable repository yields its named unavailable result and no fallback.
- No registration result carries the store's registration token.
- A wait ends with exactly one of the named outcomes
received,timeout, orunreachable, never with a record at or below itsafterId. - A label the store returns reaches the result verbatim beside its message, and no target is selected by it.
- A
list, await, or any operation other thanreadandacknowledgeleaves every record's read state as it found it.
</success_criteria>
04 同仓库更多 Skills
plugin
暂无简介
sanitize-powerpoint
ALWAYS invoke this skill when sanitizing, cleaning up, auditing, or aligning a PowerPoint (.pptx) deck — slide-master and layout structure, layout type attributes, stray fonts, non-theme colors, or layout naming. NEVER hand-edit pptx XML without this skill.
draw-excalidraw
ALWAYS invoke this skill when creating Excalidraw diagrams, visualizing workflows, architectures, or concepts. NEVER generate Excalidraw JSON without this skill.
typescript-test-standards
TypeScript test standards enforced across all skills. Loaded by other skills, not invoked directly.
typescript-code-standards
TypeScript code standards enforced across all skills. Loaded by other skills, not invoked directly.
typescript-architecture-standards
TypeScript ADR conventions enforced across architect and auditor skills. Loaded by other skills, not invoked directly.