Command-line tool to send an email, or automate repetitive emails.
Built with TypeScript and nodemailer. Engine-like design — can be embedded in VS Code extensions, GUIs, or any Node.js application.
# Install dependencies
npm install
# Build TypeScript
npm run build
# Send a quick text email
sendEmail -t someone@example.com "Hello from sendEmail"
# Show full help
sendEmail -hRun the setup script to create config/accounts/_default.js from the template:
bash .github/scripts/setup.sh.github\scripts\setup.batThen edit config/accounts/_default.js with your credentials.
For Gmail, use an App Password (requires 2FA).
Caution
config/accounts/ is excluded from git — it contains real credentials.
Never run git add -f config/accounts/ — this bypasses .gitignore and will expose your credentials.
See config/README.md for full details.
npm run build# Unix/macOS
chmod +x bin/sendEmail.sh
ln -s $(pwd)/bin/sendEmail.sh /usr/local/bin/sendEmail
# Windows: add the bin/ directory to your PATHsendEmail [options] [arguments]
| Mode | Example |
|---|---|
| Raw (quick text) | sendEmail -t user@example.com "Message" |
| Normal (structured) | sendEmail --send-to user@example.com --subject "Hello" --message-html body.html |
| Repetitive (bulk) | sendEmail --config-email newsletter --email-list subscribers --force |
# Quick text email
sendEmail -t someone@example.com "Quick message"
# HTML email
sendEmail --send-to john@example.com --subject "Report" --message-html ./report.html
# Markdown email (auto-converted to HTML)
sendEmail --send-to team@company.com --subject "Update" --message-file ./update.md
# Configured email template
sendEmail --config-email billing --send-to client@example.com
# Bulk send to list
sendEmail --config-email newsletter --email-list subscribers --force
# Copy tool to a project (auto-creates config/accounts/_default.js via setup)
sendEmail --copy ./my-project # full tool [null:reproductive <tools>]
sendEmail --copy:config ./my-project # config/support types only [null:reproductive <config>]
# Show help
sendEmail -h
sendEmail -h options
sendEmail -h options:configurableRelative paths for --message-file, --message-html, and --message-text are resolved from your current working directory first; if no match is found, sendEmail falls back to its config/tool root.
sendEmail/
├── src/
│ ├── core/ # Core engine (interface-agnostic)
│ │ ├── engine.ts # EmailEngine class
│ │ ├── types.ts # All TypeScript interfaces
│ │ ├── config-loader.ts # Load configurations
│ │ ├── template-engine.ts # Template variable substitution
│ │ ├── list-processor.ts # Email list iteration
│ │ ├── attachment-loader.ts
│ │ └── validator.ts
│ ├── cli/ # CLI entry point
│ │ ├── index.ts # Main CLI runner
│ │ ├── parser.ts # Argument parsing
│ │ ├── help.ts # Help documentation
│ │ └── prompts.ts # Confirmation prompts
│ ├── utils/ # Utilities
│ │ ├── file-utils.ts
│ │ ├── logger.ts
│ │ ├── error-handler.ts
│ │ └── markdown-html.ts
│ └── tools/ # Tool scripts
│ ├── list-generator.ts # --new-list
│ ├── copy-tool.ts # --copy
│ └── test-runner.ts # --test
├── bin/
│ ├── sendEmail.js # Node.js entry (npm bin)
│ ├── sendEmail.sh # Unix/macOS wrapper
│ ├── sendEmail.cmd # Windows CMD wrapper
│ └── sendEmail.ps1 # PowerShell wrapper
├── config/
│ ├── _accounts/ # Template (versioned) — rename to accounts/ to activate
│ ├── accounts/ # !! git-ignored !! — your real credentials live here
│ │ ├── _default.js # REQUIRED default account
│ │ └── example.js
│ ├── emails/ # Configured email templates
│ │ ├── billing/
│ │ └── example/
│ └── globals/ # Reusable global templates
│ ├── footer/ # Global: 'footer'
│ │ └── billing/ # Nested global: 'footer/billing'
│ └── example/
├── lists/ # Email lists (.json)
├── attachments/ # Email attachments
├── img/ # Embedded images
├── tests/
│ ├── unit/ # Unit tests (vitest)
│ ├── mock/server/ # Mock SMTP server
│ └── logs/ # Test logs
└── docs/
├── CLI-OPTIONS.md # Full options reference
├── CLI-CHEATSHEET.md # Quick reference
├── API.md # Engine API documentation
└── EXAMPLES.md # Real-world examples
// New-style (recommended):
export const account = {
service: 'gmail',
auth: { user: 'your@gmail.com', pass: 'app-password' },
};
// Or with custom SMTP:
export const account = {
host: 'smtp.example.com',
port: 587,
secure: false,
auth: { user: 'user@example.com', pass: 'password' },
};{
"from": "_default",
"subject": "Billing Statement - {{contact.name}}",
"html": "billingStatement",
"globals": ["footer"]
}{
"email-list": [
{ "email": "alice@example.com", "name": "Alice" },
{ "email": "bob@example.com", "name": "Bob" }
]
}Use in subject lines, HTML templates, and configurable text:
| Variable | Value |
|---|---|
{{contact.name}} |
Contact's name |
{{contact.email}} |
Contact's email |
{{contact.<field>}} |
Any custom field |
{{date}} |
2026-02-19 |
{{date.formatted}} |
February 19, 2026 |
{{list.index}} |
Current index (bulk send) |
{{list.count}} |
Total recipients |
Legacy placeholders (CH-EMAILONLIST, CHANGE_SEND_TO) are also supported.
Reusable HTML/text snippets and attachments, stored in config/globals/<name>/.
Embed a global template inside any email HTML or text file:
{% global 'footer' %}When buildMessage() processes the template, the tag is replaced with the global's HTML (or text) content, and the global's attachments (from global.js) are automatically merged into the email.
Global folders can contain nested sub-globals:
config/globals/
footer/
billing/
global.js ← 'footer/billing'
html.htm ← optional HTML data
global.js ← 'footer'
html.htm ← optional HTML data
Reference a nested global:
{% global 'footer/billing' %}| File | Description |
|---|---|
global.js |
Required — declares globalAttachments array |
html.htm or html.html |
Optional — HTML content (root, strict naming) |
text.txt |
Optional — text content (root, strict naming) |
html/<anyFile> |
Optional — HTML content (subfolder, relaxed naming) |
data/<anyFile> |
Optional — text content (subfolder, relaxed naming) |
See docs/TEMPLATING.md for the full templating reference.
import { EmailEngine, createEngineConfig } from './dist/core/engine.js';
const engine = new EmailEngine(createEngineConfig(process.cwd()));
await engine.initialize();
const result = await engine.sendEmail({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Hello',
html: '<p>Hello, World!</p>',
});See docs/API.md for full API documentation.
| Command | Description |
|---|---|
npm run build |
Compile TypeScript to dist/ |
npm run build:watch |
Watch mode compilation |
npm run dev |
Run CLI directly with tsx (no build needed) |
npm test |
Run tests in watch mode |
npm run test:run |
Run all tests once |
sendEmail --test |
Run tests via CLI |
- CLI-OPTIONS.md — Complete options reference
- CLI-CHEATSHEET.md — Quick reference
- API.md — Engine API for developers
- EXAMPLES.md — Real-world usage examples
- TEMPLATING.md — Template variables, global tags, and config type system
- TYPES.md — Full type reference (option types, config categories, send modes)