Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions .claude/skills/list-email-templates/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: list-email-templates
description: Discover available email templates from the SchemaVaults mail-server `GET /api/templates` endpoint. Use before sending a template-based email to find the correct `template_id` and understand the expected `template_props` shape.
description: Discover available email templates from the SchemaVaults mail-server `GET /api/templates` endpoint, via either the `listEmailTemplates()` helper or the `schemavaults-send-email list-templates` CLI from `@schemavaults/send-email`. Use before sending a template-based email to find the correct `template_id` and understand the expected `template_props` shape. For one-off shell discovery, prefer the CLI (`bunx schemavaults-send-email list-templates`).
---

# List Email Templates
Expand Down Expand Up @@ -37,7 +37,22 @@ await listEmailTemplates({
});
```

## Usage -- from the shell
## Usage -- from the shell via the CLI (preferred for one-off discovery)

`@schemavaults/send-email` ships a `schemavaults-send-email` binary that wraps the same helpers. From any repo that has the package installed, run:

```bash
bunx schemavaults-send-email list-templates # JSON output (default)
bunx schemavaults-send-email list-templates --format table # tab-separated id<TAB>description
```

(Use `npx schemavaults-send-email …` if you don't have `bun` available.) The CLI reads `SCHEMAVAULTS_MAIL_API_KEY` from the environment, exactly like the helper. Pipe `--format json` output through `jq` for filtering, e.g.:

```bash
bunx schemavaults-send-email list-templates | jq '.[].id'
```

## Usage -- raw HTTP

```bash
curl -sS \
Expand All @@ -47,7 +62,7 @@ curl -sS \

## Usage -- Claude Code querying templates directly

Claude can query the template catalog from any repo that depends on `@schemavaults/send-email` by writing a short script to `/tmp/` and running it with Bun:
For one-off discovery from a Claude Code session, prefer the CLI above -- a single shell command, no script file needed. Fall back to a `/tmp/` Bun script only when you need to do something more elaborate than print the catalog (e.g. fetch + filter + cross-reference with another data source):

```ts
// /tmp/list-email-templates.ts
Expand Down
141 changes: 93 additions & 48 deletions .claude/skills/send-email-to-mailing-list/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: send-email-to-mailing-list
description: Send an email to a mailing list via the SchemaVaults mail-server `/api/send` route, using the `sendEmailToMailingList()` helper from `@schemavaults/send-email`. Use when any server-side TypeScript/JavaScript code needs to send a notification to a mailing list audience — **or when Claude Code itself wants to send a one-shot notification at the end of a task** (by writing a short script to `/tmp/` and running it with `bun`).
description: Send an email to a mailing list via the SchemaVaults mail-server `/api/send` route, using either the `sendEmailToMailingList()` helper or the `schemavaults-send-email send-to-mailing-list` CLI from `@schemavaults/send-email`. Use when any server-side TypeScript/JavaScript code needs to send a notification to a mailing list audience — **or when Claude Code itself wants to send a one-shot notification at the end of a task** (preferred: invoke the CLI via `bunx schemavaults-send-email send-to-mailing-list …`; fallback: write a short script to `/tmp/` and run it with `bun`).
---

# Send Email to Mailing List
Expand Down Expand Up @@ -116,66 +116,111 @@ await sendEmailToMailingList({
});
```

## Usage -- CLI (preferred for one-off / ad-hoc sends)

`@schemavaults/send-email` ships a `schemavaults-send-email` binary that wraps the same helper. For any one-off send -- a manual notification, a quick smoke test, a `bash` cron entry, or Claude Code firing off a single end-of-workflow email -- the CLI is the simplest path. No `/tmp/` script, no `bun run`.

```bash
# Raw text/html (both required)
bunx schemavaults-send-email send-to-mailing-list \
--subject "[ops] nightly backup finished" \
--text "Backup completed at $(date -u +%FT%TZ). 0 errors." \
--html "<p>Backup completed at $(date -u +%FT%TZ). <strong>0 errors.</strong></p>"

# Template-based
bunx schemavaults-send-email send-to-mailing-list \
--subject "Welcome aboard, Alice" \
--template-id welcome-email \
--template-props '{"name":"Alice"}'

# Override the mailing list per-call
bunx schemavaults-send-email send-to-mailing-list \
--mailing-list-id 00000000-0000-0000-0000-000000000000 \
--subject "..." --text "..." --html "..."

# Long bodies: read from files instead of inline strings
bunx schemavaults-send-email send-to-mailing-list \
--subject "weekly digest" \
--text-file /tmp/digest.txt \
--html-file /tmp/digest.html

# Or supply the entire request body as a JSON file (validated server-side)
bunx schemavaults-send-email send-to-mailing-list --body-file /tmp/payload.json
```

(Substitute `npx` for `bunx` if `bun` is unavailable.) The CLI reads `SCHEMAVAULTS_MAIL_API_KEY` and `SCHEMAVAULTS_MAILING_LIST_ID` from the environment exactly like the helper, exits non-zero with a one-line error on failure, and exits `0` on a successful 200 from the mail-server.

Run `bunx schemavaults-send-email send-to-mailing-list --help` for the full flag reference.

## Usage -- Claude Code post-workflow notification

Claude itself can use this skill to send a one-shot notification to a mailing list at the end of a workflow in any repo that depends on `@schemavaults/send-email` (this repo already does). Because the helper lives in `node_modules/`, Claude can drop a standalone TypeScript file into `/tmp/` and run it with Bun -- no new dependencies, no build step, no changes to the repo under review.

### Pattern

1. **Write the script to `/tmp/send-notification-after-workflow.ts`** (fresh on every run -- `/tmp/` is scratch space, overwrite freely):

```ts
// /tmp/send-notification-after-workflow.ts
import { sendEmailToMailingList } from "@schemavaults/send-email";

async function main(): Promise<void> {
await sendEmailToMailingList({
body: {
subject: "[claude-code] workflow finished: <short description>",
message: {
text:
"Claude just finished a workflow.\n\n" +
"Summary:\n" +
"- <bullet 1>\n" +
"- <bullet 2>\n" +
"- <bullet 3>\n",
html:
"<p>Claude just finished a workflow.</p>" +
"<p><strong>Summary:</strong></p>" +
"<ul>" +
"<li>&lt;bullet 1&gt;</li>" +
"<li>&lt;bullet 2&gt;</li>" +
"<li>&lt;bullet 3&gt;</li>" +
"</ul>",
},
},
});
console.log("[notify] sent");
}

main().catch((err) => {
console.error("[notify] failed:", err);
process.exit(1);
});
```
Claude itself can use this skill to send a one-shot notification to a mailing list at the end of a workflow in any repo that depends on `@schemavaults/send-email` (this repo already does).

2. **Fill in real content.** Replace `<short description>` and the bullet placeholders with a concrete summary of what the workflow actually did. Keep the subject under ~70 characters and the body scannable (3-5 bullets is usually enough).
### Preferred: invoke the CLI directly

3. **Run it from the repo root** so Bun resolves `@schemavaults/send-email` through the repo's `node_modules/`:
For most end-of-workflow notifications (a few sentences plus a short bullet list) the CLI is the right tool -- one shell command, no scratch file:

```bash
bun run /tmp/send-notification-after-workflow.ts
```
```bash
bunx schemavaults-send-email send-to-mailing-list \
--subject "[claude-code] workflow finished: <short description>" \
--text "$(printf 'Claude just finished a workflow.\n\nSummary:\n- <bullet 1>\n- <bullet 2>\n- <bullet 3>\n')" \
--html "$(printf '<p>Claude just finished a workflow.</p><p><strong>Summary:</strong></p><ul><li>&lt;bullet 1&gt;</li><li>&lt;bullet 2&gt;</li><li>&lt;bullet 3&gt;</li></ul>')"
```

Replace the `<short description>` and bullet placeholders with a concrete summary. Keep the subject under ~70 characters and the body scannable (3-5 bullets is usually enough). A non-zero exit means the helper threw -- surface the error in your summary to the user rather than retrying silently.

### Fallback: write a `/tmp/` script

Reach for the script form only when the body is large enough or templated enough that string-quoting in shell is awkward (e.g. multi-paragraph HTML, dynamic data assembly, conditional content):

4. **Check the exit code.** `0` means the email was accepted by the mail-server. Non-zero means the helper threw -- surface the error in your summary to the user rather than retrying silently.
```ts
// /tmp/send-notification-after-workflow.ts
import { sendEmailToMailingList } from "@schemavaults/send-email";

async function main(): Promise<void> {
await sendEmailToMailingList({
body: {
subject: "[claude-code] workflow finished: <short description>",
message: {
text:
"Claude just finished a workflow.\n\n" +
"Summary:\n" +
"- <bullet 1>\n" +
"- <bullet 2>\n" +
"- <bullet 3>\n",
html:
"<p>Claude just finished a workflow.</p>" +
"<p><strong>Summary:</strong></p>" +
"<ul>" +
"<li>&lt;bullet 1&gt;</li>" +
"<li>&lt;bullet 2&gt;</li>" +
"<li>&lt;bullet 3&gt;</li>" +
"</ul>",
},
},
});
console.log("[notify] sent");
}

main().catch((err) => {
console.error("[notify] failed:", err);
process.exit(1);
});
```

Run from the repo root so Bun resolves `@schemavaults/send-email` through the repo's `node_modules/`:

```bash
bun run /tmp/send-notification-after-workflow.ts
```

### When to trigger this

Send **exactly one** notification at the **end** of a workflow, after all commits and pushes have landed, so the email reflects the final state.

### Cautions

- The env vars `SCHEMAVAULTS_MAIL_API_KEY` and `SCHEMAVAULTS_MAILING_LIST_ID` must be set in Claude's process. If they're missing, the helper throws a clear error -- report it to the user instead of retrying blindly.
- The env vars `SCHEMAVAULTS_MAIL_API_KEY` and `SCHEMAVAULTS_MAILING_LIST_ID` must be set in Claude's process. If they're missing, the helper (and CLI) throws a clear error -- report it to the user instead of retrying blindly.
- **One notification per workflow, not per step.** If a workflow had no meaningful outcome (e.g. "user asked a question, Claude answered"), skip the notification entirely. The inbox should not become chatty.
- **Do not send the notification before the work is finished.** Push first, notify second.
- **Ask before sending** if the user hasn't explicitly opted in to post-workflow notifications. Sending email is a side effect visible to other humans; don't do it silently on tasks where the user hasn't asked for it.
Expand Down
178 changes: 178 additions & 0 deletions .claude/skills/send-one-off-email/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
---
name: send-one-off-email
description: Send a one-off email to one or more individual recipients via the SchemaVaults mail-server `/api/send` route, using either the `sendEmail()` helper or — for ad-hoc shell invocations — the `schemavaults-send-email send` CLI from `@schemavaults/send-email`. Use when you need to fire off a single notification to specific email addresses (not a mailing list), e.g. a manual heads-up to a colleague, an end-of-task email to the user, a quick smoke test of an API key, or a `bash`/cron one-liner. For mailing-list audiences, use the `send-email-to-mailing-list` skill instead.
---

# Send a One-Off Email

This skill teaches Claude how to send a single email to one or more individual recipients. The fastest path is the `schemavaults-send-email send` CLI (one shell command, no script file). For sends embedded inside application code, use the `sendEmail()` helper directly.

## When to use this skill

- A manual notification to specific email addresses (e.g. "let me email so-and-so the result").
- An ad-hoc smoke test of `SCHEMAVAULTS_MAIL_API_KEY` or a template's `template_props` shape.
- A `bash`, `cron`, or CI step that needs to fire a single email.
- Application code that sends transactional email to one or more recipients.

Do **not** use this skill for:

- Sending to a mailing list audience -- use the `send-email-to-mailing-list` skill (it forbids `to`/`cc`/`bcc` and resolves the audience from a mailing list UUID).
- Discovering which templates exist -- use the `list-email-templates` skill first.
- Client-side / browser code -- the API key is a secret.
- Sends to more than 50 recipients in a single call (the mail-server caps each send at 50; for larger audiences use a mailing list).

## Prerequisites

1. **Install the package** (provides both the helper and the CLI):
```bash
bun add @schemavaults/send-email
# or: npm install @schemavaults/send-email
```

2. **Set the API key** in the environment:
- `SCHEMAVAULTS_MAIL_API_KEY` -- bearer token starting with `svlts_mail_pk_`.

3. **Optional:** `SCHEMAVAULTS_APP_ENVIRONMENT` = `"production"` (default) | `"development"` | `"staging"`. Only set when explicitly targeting a non-prod mail-server.

## Usage -- CLI (preferred for one-off sends)

The package ships a `schemavaults-send-email` binary. Invoke the `send` subcommand for individual recipients:

```bash
# Single recipient, raw text/html (both required)
bunx schemavaults-send-email send \
--to alice@example.com \
--subject "Heads up: deploy at 3pm" \
--text "Deploy window starts at 15:00 UTC." \
--html "<p>Deploy window starts at <strong>15:00 UTC</strong>.</p>"

# Multiple recipients (--to is repeatable / variadic)
bunx schemavaults-send-email send \
--to alice@example.com --to bob@example.com \
--cc carol@example.com \
--subject "..." --text "..." --html "..."

# Template-based
bunx schemavaults-send-email send \
--to alice@example.com \
--subject "Welcome aboard" \
--template-id welcome-email \
--template-props '{"name":"Alice"}'

# Long bodies: read from files
bunx schemavaults-send-email send \
--to alice@example.com \
--subject "weekly digest" \
--text-file /tmp/digest.txt \
--html-file /tmp/digest.html

# Or supply the full request body as JSON
bunx schemavaults-send-email send --body-file /tmp/payload.json
```

(Use `npx` instead of `bunx` if `bun` is unavailable.) The CLI exits `0` on a successful 200 response and non-zero with a one-line error otherwise. Run `bunx schemavaults-send-email send --help` for the full flag reference.

### Globally-applicable flags

These work on every subcommand, before the subcommand name:

- `--api-key <key>` -- override `SCHEMAVAULTS_MAIL_API_KEY`.
- `--environment <env>` -- override `SCHEMAVAULTS_APP_ENVIRONMENT` (`production` | `development` | `staging`).

Example:

```bash
bunx schemavaults-send-email --environment development send \
--to me@example.com --subject "dev smoke" \
--template-id welcome-email --template-props '{"name":"me"}'
```

## Usage -- `sendEmail()` helper (for application code)

When the send is embedded in a TypeScript/JavaScript codepath, import the helper. Same auth conventions, same body schema:

```ts
import { sendEmail } from "@schemavaults/send-email";

// Template-based
await sendEmail({
body: {
to: "alice@example.com",
subject: "Welcome aboard",
message: {
template_id: "welcome-email",
template_props: { name: "Alice" },
},
},
});

// Raw text/html (both required)
await sendEmail({
body: {
to: ["alice@example.com", "bob@example.com"],
cc: "carol@example.com",
subject: "Heads up: deploy at 3pm",
message: {
text: "Deploy window starts at 15:00 UTC.",
html: "<p>Deploy window starts at <strong>15:00 UTC</strong>.</p>",
},
},
});
```

Escape user-supplied values before embedding them in `html` if they can contain `<` / `>` / `&` -- the mail-server does not sanitize this for you.

## Request body shape

```ts
type OneOffEmailBody = {
to: string | string[]; // 1-50 recipient email(s)
subject: string;
message:
| { template_id: string; template_props?: unknown }
| { text: string; html: string };
from?: string; // defaults to the mail-server's configured sender
replyTo?: string;
cc?: string | string[]; // 1-50
bcc?: string | string[]; // 1-50
};

// Helper call signature:
type ISendEmailOpts = {
body: OneOffEmailBody;
bearerToken?: string; // override SCHEMAVAULTS_MAIL_API_KEY
environment?: "production" | "development" | "staging";
};
```

## Error handling

The CLI prints the error message and exits non-zero. The helper throws on any non-200 response -- wrap in `try/catch` whenever a failed send must not break the caller's flow. Common failure modes:

| Error | Cause |
| --- | --- |
| `Failed to load API key from environment variable 'SCHEMAVAULTS_MAIL_API_KEY'` | Env var not set (or empty string). |
| `Bad request body to send email with!` | Body fails Zod validation -- typically a missing `subject`, missing `text`/`html` pair, an invalid email in `to`/`cc`/`bcc`, or unknown fields. |
| `Invalid or revoked API key.` (HTTP 401) | API key is wrong, expired, or revoked. |
| `Failed to parse request body!` (HTTP 400) | Server-side Zod parsing failed; usually a template `template_props` shape mismatch. |
| `Provide either --template-id, or both of --text/--html …` (CLI only) | Neither a template ID nor a complete raw body was supplied. |

## Cautions

- **Treat recipient lists as PII.** Don't paste them into chat logs or commit them to source.
- **Ask before sending** if the user hasn't explicitly opted in. Email is a side effect visible to other humans.
- **Prefer mailing lists** for any audience that grows or churns -- managing a recipient list inline doesn't scale.
- **Don't loop the CLI/helper to fan out** beyond 50 recipients per send -- create a mailing list instead and use the `send-email-to-mailing-list` skill.

## Reference

- CLI source: `node_modules/@schemavaults/send-email/dist/cli.js` (bundled, runs under Node).
- Helper source: `node_modules/@schemavaults/send-email/dist/send-email.{d.ts,js}`.
- Body schema: `node_modules/@schemavaults/send-email/dist/send-email-request-body-schema.{d.ts,js}` (the same `createSendEmailRequestBodySchema` Zod schema used by both client and mail-server).

## Adding this skill to another project

1. Copy this file into the target project's `.claude/skills/` folder.
2. Install the package: `bun add @schemavaults/send-email`.
3. Set `SCHEMAVAULTS_MAIL_API_KEY` in the project's secret store (`.env.local` for local dev; hosting provider's secret store for prod).
4. Commit the skill file. The next Claude Code session in that repo will discover it automatically.
Loading