Skip to content
Back to skills

Mail Draft

ASecurity

Drafts outgoing email and never sends: Markdown to mail-safe HTML themed from DESIGN.md, footers from identity/mail-footers/, occasions from workflow/mail-profiles/, recipients from mandants, preflight checks; Apple Mail, Outlook or .eml. Trigger: "draft an email", "mail to", "email draft", "mail footer", "email signature", "mail profile".

  • 11 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 7, 2026
ai-agentspythongobashgit

Works with

  • cli

Security analysis

A100/100

Pro scans all 19 files and shows the line behind each finding

Scanned October 7, 2026

npx -y skills add bks-lab/open-bridge --skill mail-draft --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mail Draft?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mail Draft
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/bks-lab-mail-draft/badge)](https://www.skillsdirectory.com/skills/bks-lab-mail-draft)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mail-draft
description: >-
  Drafts outgoing email and never sends: Markdown to mail-safe HTML themed from
  DESIGN.md, footers from identity/mail-footers/, occasions from
  workflow/mail-profiles/, recipients from mandants, preflight checks; Apple
  Mail, Outlook or .eml. Trigger: "draft an email", "mail to", "email draft",
  "mail footer", "email signature", "mail profile".
metadata:
  scope: core
allowed-tools:
  - Bash(skills/mail-draft/mail-draft.sh:*)
  - Read
  - Write
---

# mail-draft

Builds a mail as a DRAFT in the person's mail client. The person reads it and
presses Send. There is no send command, not behind a flag, not in any client
module: a draft that is wrong costs a minute, a sent mail that is wrong cannot
be taken back. "Send X to Y" is a request for the finished draft.

Engine: `skills/mail-draft/mail-draft.sh` (Python, stdlib + PyYAML). Model and
every field: [`docs/mail-draft.md`](../../docs/mail-draft.md).

## What decides what

| Question | Answered by | Tier |
|---|---|---|
| Who signs, how to reach them | `identity/mail-footers/<id>.yaml` (borrows from a persona) | user / org |
| What occasion, which footer, greeting, attachments | `workflow/mail-profiles/<id>.yaml` | user / org |
| Who receives it, in which language, formal or not | `identity/mandants/<id>.yaml` (`mail_profile`, `defaults.language`, `defaults.address_form`) | user |
| Colours and type | `DESIGN.md` through the `design` theme | core / fork |
| Machine defaults (client, drafts folder) | `bridge-config.yaml` `mail:` | user |

CORE ships only the engine, the two `_schema.yaml` and the two `_template.yaml`.
Every real footer and profile is a USER file (or `org` when a team shares one
through an overlay), and the schema refuses `scope: core` for a footer.

## Decision tree

```
Request about mail
├── write / draft / "send" a mail ──────────► § Draft a mail
├── "how will X be addressed?" ─────────────► mail-draft.sh explain --to X
├── new footer / signature / profile ───────► § Set up
├── paste a signature into Outlook/Mail ────► mail-draft.sh signature <footer> --out <dir>
└── "is my mail setup ok?" ─────────────────► mail-draft.sh validate && mail-draft.sh list
```

## Draft a mail

1. Resolve the recipient and occasion first: `mail-draft.sh explain --to <who>`.
   It prints the profile, footer, language, form of address and sender, each
   with the reason. If the profile names a `style_skill`, load that skill
   before writing the text.
2. Write the body as Markdown into a file (greeting and sign-off come from the
   profile; leave them out unless the text needs its own). Syntax: headings,
   lists, tables, `> [!NOTE]` alerts, `[[Button]](url)`, `[done]` badges,
   `{{snippet:<name>}}` from the profile.
3. Preview and show the person the text part in chat:
   `mail-draft.sh preview --to <who> --subject "<s>" --body-file <f> [--attach <file>]`
4. On their go: `mail-draft.sh compose ...` with the same arguments.
   Preflight errors stop it (missing attachment, leftover `{placeholder}`,
   required attachment absent). Warnings are printed for the person.
5. Report what the command printed: client, recipients, attachments, whether
   the Drafts folder counted it, and for Outlook that "From" must be switched
   by hand. Never claim a draft that was not verified.

Recipients: an address, `mandant:<id>`, `<mandant>/<person>` or a person id.
Override anything per call: `--profile`, `--footer`/`--no-footer`,
`--footer-style`, `--theme`, `--lang`, `--informal`/`--formal`, `--client`.

## Set up

Read the matching `_template.yaml` and `_schema.yaml` first
([`rules/file-creation.md`](../../rules/file-creation.md)), then:

- a footer per way of signing: `identity/mail-footers/<id>.yaml`, `persona_ref`
  for name/email/phone, `style: card | compact | minimal | text`, `variants:`
  per language, `extends:` to build on a shared team footer
- a profile per occasion: `workflow/mail-profiles/<id>.yaml`, one `default: true`,
  `applies_to:` to be picked by mandant, mandant type or tag, `extends:` for
  variations, `variants:` for per-language wording
- on a mandant or person: `mail_profile: <id>` to pin an occasion
- finish with `mail-draft.sh validate` (CI runs it too)

## Clients

| client | draft | sender | notes |
|---|---|---|---|
| `apple-mail` | real draft, read back from Drafts (sender, attachments) | set from the footer, read back | default on macOS |
| `outlook` | open window; counted in Drafts where Outlook lets scripts see it | NOT settable, reported | no `--hide`; the new Outlook hides account folders from scripts, so its drafts read as unverified |
| `eml` | `.eml` with `X-Unsent: 1` | in the file | any OS; opens as a draft in Outlook and Thunderbird |
| `none` | only the bundle | n/a | for review or another tool |

Every run also writes a bundle (source, HTML, text, .eml, light/dark preview,
report) under `mail.drafts_dir`, by default `.bridge/mail-drafts/` (gitignored:
a draft holds addresses and private text).

## Never

- send, or add a code path that sends
- hand the body to osascript as an argument (it travels as a file); prefer
  `--body-file` over `--body` for anything longer than a line
- pick a colour in a footer or profile for your own brand (theme roles only)
- write a real footer or profile into CORE

Files in this skill

  • SKILL.md5.3 KB
  • engine/__init__.py82 B
  • engine/cli.py14.3 KB
  • engine/clients/__init__.py2.8 KB
  • engine/clients/apple_mail.py9.8 KB
  • engine/clients/outlook.py6.2 KB
  • engine/config.py3.2 KB
  • engine/draft.py11.9 KB
  • engine/footer.py22.8 KB
  • engine/lint.py4.7 KB
  • engine/markdown.py29.3 KB
  • engine/mime.py1.7 KB
  • engine/profiles.py4.7 KB
  • engine/recipients.py5.7 KB
  • engine/theme.py11.7 KB
  • engine/validate.py5.2 KB
  • mail-draft.sh2.5 KB
  • run-tests.sh523 B
  • scripts/tests/mutate.py3.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.