@opendirectory.dev/skills 0.1.15 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendirectory.dev/skills",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "bin": {
package/registry.json CHANGED
@@ -15,6 +15,14 @@
15
15
  "version": "1.0.0",
16
16
  "path": "skills/claude-md-generator"
17
17
  },
18
+ {
19
+ "name": "cold-email-verifier",
20
+ "description": "Use when the user wants to verify cold emails, enrich a lead list, or autonomously guess email addresses from a CSV using ValidEmail.",
21
+ "tags": [],
22
+ "author": "Unknown",
23
+ "version": "0.0.1",
24
+ "path": "skills/cold-email-verifier"
25
+ },
18
26
  {
19
27
  "name": "cook-the-blog",
20
28
  "description": "Generate high-converting, deep-dive growth case studies in MDX format.",
@@ -63,6 +71,14 @@
63
71
  "version": "1.0.0",
64
72
  "path": "skills/hackernews-intel"
65
73
  },
74
+ {
75
+ "name": "human-tone",
76
+ "description": "|",
77
+ "tags": [],
78
+ "author": "Unknown",
79
+ "version": "1.0.0",
80
+ "path": "skills/human-tone"
81
+ },
66
82
  {
67
83
  "name": "kill-the-standup",
68
84
  "description": "Reads yesterday's Linear issues and GitHub commits for the authenticated user, formats a standup update (done / doing / blockers), and posts it to...",
@@ -177,10 +193,10 @@
177
193
  },
178
194
  {
179
195
  "name": "show-hn-writer",
180
- "description": "<img width=\"1280\" height=\"640\" alt=\"show-hn-writer\" src=\"https://github.",
196
+ "description": "Draft a Show HN post backed by real HN performance data. Uses observed patterns from 250 top HN posts to maximise score.",
181
197
  "tags": [],
182
- "author": "Unknown",
183
- "version": "0.0.1",
198
+ "author": "Varnan / Paras Madan",
199
+ "version": "2.0.0",
184
200
  "path": "skills/show-hn-writer"
185
201
  },
186
202
  {
@@ -0,0 +1,3 @@
1
+ # ValidEmail.co API Key (Required for --mode validemail)
2
+ # Get your free API key at https://validemail.co
3
+ VALIDEMAIL_API_KEY=your_api_key_here
@@ -0,0 +1,45 @@
1
+ # Cold Email Verifier
2
+
3
+ Agent Skill that equips your AI agent with the ability to autonomously guess, enrich, and verify cold email addresses directly from a CSV file.
4
+
5
+ Instead of running Python scripts manually, this skill teaches your AI how to read your lead lists, discover corporate domains via the Clearbit API, generate standard email permutations, and securely verify them.
6
+
7
+ ## Verification Engines Supported
8
+ The AI is trained to use two different verification backends:
9
+ 1. **ValidEmail.co API (Highly Recommended)**: The AI will use this SaaS API for enterprise-grade accuracy, bypassing strict catch-all servers. You can get a free tier of verification credits at validemail.co.
10
+ 2. **Reacher (Self-Hosted)**: The AI can route checks through your own self-hosted Reacher Docker container (e.g., on an AWS EC2 instance with an unblocked Port 25) for 100% free verification.
11
+
12
+ ## Installation
13
+
14
+ To install this skill into your AI agent's workspace:
15
+
16
+ 1. Clone or download this folder.
17
+ 2. Copy the entire cold-email-verifier folder into your agent's skills directory (e.g., ~/.agents/skills/ or your project's .agents/skills/ folder).
18
+ 3. Ensure the dependencies in
19
+ equirements.txt are installed in your environment:
20
+ `ash
21
+ pip install -r requirements.txt
22
+ `
23
+ 4. Copy the .env.example to .env and add your ValidEmail.co API key:
24
+ `ash
25
+ cp .env.example .env
26
+ `
27
+
28
+ ## How to Prompt the AI
29
+
30
+ Once the skill is installed, you can simply talk to your AI agent. Here are example prompts:
31
+
32
+ **Using ValidEmail.co:**
33
+ > "Use the cold email verifier skill to process leads.csv. Please use the validemail mode."
34
+
35
+ **Using a Self-Hosted Reacher Server:**
36
+ > "Verify the emails in leads.csv using the cold email verifier. Use reacher-http mode and point it to http://YOUR_SERVER_IP:8080/v0/check_email."
37
+
38
+ The AI will automatically parse the CSV, handle the domain lookups, generate the permutations, run the verification engine, and output a clean CSV with the valid emails appended.
39
+
40
+ ## CSV Format Requirements
41
+ The AI expects the input CSV to contain at least the following headers:
42
+ - First Name
43
+ - Last Name
44
+ - Company Name
45
+ - Domain Name
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: cold-email-verifier
3
+ description: Use when the user wants to verify cold emails, enrich a lead list, or autonomously guess email addresses from a CSV using ValidEmail.co or the open-source Reacher engine.
4
+ ---
5
+
6
+ # Cold Email Verifier and Guesser (Public)
7
+
8
+ ## Overview
9
+ This skill autonomously processes a CSV of leads (containing First Name, Last Name, and Company Name), discovers their corporate domain, generates professional email permutations, and strictly verifies their deliverability.
10
+
11
+ It is designed to solve the problem of missing contact info by guessing core email angles and then checking which one is real.
12
+
13
+ ## The Email Guessing Engine
14
+ You don't need to provide emails in your CSV! If the CSV only contains First Name, Last Name, and Company Name, the script will automatically:
15
+ 1. Look up the company's official domain using the free Clearbit API.
16
+ 2. Clean the names (removing punctuation) and generate the 3 most common corporate email formats:
17
+ - first@domain.com
18
+ - first.last@domain.com
19
+ - firstlast@domain.com
20
+
21
+ ## Verification Methods (Choose Your Engine)
22
+ Once the emails are guessed, the script must verify them. We support three methods:
23
+
24
+ ### 1. ValidEmail.co API (Highly Recommended)
25
+ **The absolute best option.** ValidEmail.co provides enterprise-grade accuracy, bypasses strict catch-all servers, and handles IP reputation for you.
26
+ - **Free Tier:** They offer a generous free tier (50 free verification credits on signup) to get you started!
27
+ - **How to use:** Go to [validemail.co](https://validemail.co), create an account, get your API key, and run the script in --mode validemail.
28
+
29
+ ### 2. Reacher Open Source (Self-Hosted)
30
+ If you want a 100% free, open-source solution, you can host the Reacher backend yourself on a cloud provider (like AWS, GCP, or Hetzner).
31
+ - **GitHub Repo:** [reacherhq/check-if-email-exists](https://github.com/reacherhq/check-if-email-exists)
32
+ - **Important:** Your cloud provider MUST have outbound Port 25 open.
33
+ - **How to use:** Host the docker container, then run the script using --mode reacher-http --reacher-url "http://<YOUR_SERVER_IP>:8080/v0/check_email".
34
+
35
+ ### 3. Reacher Local CLI (Not Recommended / Unreliable)
36
+ You can run the Reacher CLI directly on your laptop.
37
+ - **Warning:** Residential ISPs globally block Port 25 to prevent spam. Furthermore, major mail servers (Microsoft, Google) will automatically reject SMTP handshakes from residential Wi-Fi IP addresses.
38
+ - **Result:** You will get massive amounts of false negatives and timeouts. Use ValidEmail.co instead.
39
+
40
+ ## How to Execute
41
+ First, ensure dependencies are installed: pip install -r requirements.txt
42
+
43
+ **To use ValidEmail.co (Recommended):**
44
+ `ash
45
+ export VALIDEMAIL_API_KEY="your_api_key_here"
46
+ python scripts/email_verifier.py --input leads.csv --output verified_leads.csv --mode validemail
47
+ `
48
+
49
+ **To use Self-Hosted Reacher:**
50
+ `ash
51
+ python scripts/email_verifier.py --input leads.csv --output verified_leads.csv --mode reacher-http --reacher-url "http://your-server-ip:8080/v0/check_email"
52
+ `
53
+
54
+ ## CSV Format Requirements
55
+ The input CSV must contain these exact column headers (or their specific mappings):
56
+ - First Name
57
+ - Last Name
58
+ - Company Name
59
+ - Domain (Optional - highly recommended for accuracy)
@@ -0,0 +1,2 @@
1
+ requests
2
+ pandas
@@ -0,0 +1,240 @@
1
+ """
2
+ Email Verifier Script
3
+ This script will handle the logic for verifying cold emails.
4
+ """
5
+
6
+ import argparse
7
+ import os
8
+ import re
9
+ import requests
10
+ import subprocess
11
+ import json
12
+ import pandas as pd
13
+ from urllib.parse import urlparse
14
+
15
+
16
+ def get_domain(company_name):
17
+ """
18
+ Extracts the domain for a given company name.
19
+ Uses Clearbit Autocomplete API, otherwise falls back to a simple heuristic.
20
+ """
21
+ try:
22
+ response = requests.get(
23
+ f"https://autocomplete.clearbit.com/v1/companies/suggest?query={company_name}",
24
+ timeout=10,
25
+ )
26
+ if response.status_code == 200:
27
+ data = response.json()
28
+ if isinstance(data, list) and len(data) > 0:
29
+ domain = data[0].get("domain")
30
+ if domain:
31
+ return domain
32
+ except Exception as e:
33
+ print(f"Clearbit autocomplete failed for {company_name}: {e}")
34
+
35
+ # Fallback heuristic
36
+ clean_name = re.sub(r"[^\w\s]", "", company_name)
37
+ clean_name = re.sub(r"\s+", "", clean_name)
38
+ return f"{clean_name.lower()}.com"
39
+
40
+
41
+ def parse_args():
42
+ parser = argparse.ArgumentParser(description="Cold Email Verifier")
43
+ parser.add_argument("--input", required=True, help="Path to input CSV")
44
+ parser.add_argument(
45
+ "--output",
46
+ default="output.csv",
47
+ help="Path to output CSV (default: output.csv)",
48
+ )
49
+ parser.add_argument(
50
+ "--mode",
51
+ required=True,
52
+ choices=["validemail", "reacher-http", "reacher-cli"],
53
+ help="Verification mode",
54
+ )
55
+ parser.add_argument(
56
+ "--reacher-url",
57
+ default="http://localhost:8080/v0/check_email",
58
+ help="Reacher HTTP URL (default: http://localhost:8080/v0/check_email)",
59
+ )
60
+ parser.add_argument(
61
+ "--test",
62
+ "--dry-run",
63
+ action="store_true",
64
+ dest="test",
65
+ help="Run in test/dry-run mode",
66
+ )
67
+ return parser.parse_args()
68
+
69
+
70
+ _reacher_cli_warning_printed = False
71
+
72
+
73
+ def verify_validemail(email):
74
+ api_key = os.environ.get("VALIDEMAIL_API_KEY")
75
+ if not api_key:
76
+ print("Error: VALIDEMAIL_API_KEY environment variable not set.")
77
+ return False
78
+
79
+ try:
80
+ headers = {"Authorization": f"Bearer {api_key}", "Accept": "application/json"}
81
+ response = requests.get(
82
+ f"https://validemail.co/api/v1/validate?email={email}",
83
+ headers=headers,
84
+ timeout=15,
85
+ )
86
+ response.raise_for_status()
87
+ data = response.json()
88
+ return data.get("isDeliverable") is True
89
+ except Exception as e:
90
+ print(f"ValidEmail API error for {email}: {e}")
91
+ return False
92
+
93
+
94
+ def verify_reacher_http(email, url):
95
+ try:
96
+ response = requests.post(url, json={"to_email": email})
97
+ response.raise_for_status()
98
+ data = response.json()
99
+ return data.get("is_reachable") == "safe"
100
+ except Exception as e:
101
+ print(f"Reacher HTTP error for {email}: {e}")
102
+ return False
103
+
104
+
105
+ def verify_reacher_cli(email):
106
+ global _reacher_cli_warning_printed
107
+ if not _reacher_cli_warning_printed:
108
+ print(
109
+ "WARNING: Residential ISPs often block Port 25. Reacher CLI may fail or timeout if run from a residential network."
110
+ )
111
+ _reacher_cli_warning_printed = True
112
+
113
+ try:
114
+ result = subprocess.run(
115
+ ["check_if_email_exists", email],
116
+ capture_output=True,
117
+ text=True,
118
+ check=False,
119
+ )
120
+
121
+ try:
122
+ data = json.loads(result.stdout)
123
+ return data.get("is_reachable") == "safe"
124
+ except json.JSONDecodeError:
125
+ return "safe" in result.stdout.lower()
126
+
127
+ except FileNotFoundError:
128
+ print(
129
+ "Error: check_if_email_exists command not found. Please install reacher CLI."
130
+ )
131
+ return False
132
+ except Exception as e:
133
+ print(f"Reacher CLI error for {email}: {e}")
134
+ return False
135
+
136
+
137
+ def generate_permutations(first: str, last: str, domain: str) -> list[str]:
138
+ first = re.sub(r"[^\w\s]", "", first.strip().lower())
139
+ last = re.sub(r"[^\w\s]", "", last.strip().lower())
140
+ domain = domain.strip().lower()
141
+
142
+ perms = []
143
+ if first:
144
+ perms.append(f"{first}@{domain}")
145
+
146
+ if first and last:
147
+ perms.extend([
148
+ f"{first}.{last}@{domain}",
149
+ f"{first}{last}@{domain}"
150
+ ])
151
+
152
+ return list(dict.fromkeys([p for p in perms if p]))
153
+
154
+
155
+ def main():
156
+ args = parse_args()
157
+ print(f"Arguments parsed: {args}")
158
+
159
+ try:
160
+ df = pd.read_csv(args.input)
161
+ except Exception as e:
162
+ print(f"Error reading input CSV: {e}")
163
+ return
164
+
165
+ required_cols = ["First Name", "Last Name", "Company Name"]
166
+
167
+ col_mapping = {
168
+ "Founder 1 First Name": "First Name",
169
+ "Founder 1 Last Name": "Last Name",
170
+ "Startup Name": "Company Name",
171
+ "Website": "Domain",
172
+ }
173
+ df.rename(columns=col_mapping, inplace=True)
174
+
175
+ for col in required_cols:
176
+ if col not in df.columns:
177
+ print(
178
+ f"Error: Input CSV must contain '{col}' column. Found: {list(df.columns)}"
179
+ )
180
+ return
181
+
182
+ valid_emails = []
183
+
184
+ for index, row in df.iterrows():
185
+ first = str(row["First Name"])
186
+ last = str(row["Last Name"])
187
+ company = str(row["Company Name"])
188
+
189
+ domain = ""
190
+ if (
191
+ "Domain" in df.columns
192
+ and pd.notna(row["Domain"])
193
+ and str(row["Domain"]).strip()
194
+ ):
195
+ domain_raw = str(row["Domain"]).strip()
196
+ parsed = urlparse(domain_raw)
197
+ if parsed.netloc:
198
+ domain = parsed.netloc
199
+ else:
200
+ domain = parsed.path
201
+
202
+ if domain.startswith("www."):
203
+ domain = domain[4:]
204
+ else:
205
+ domain = get_domain(company)
206
+
207
+ perms = generate_permutations(first, last, domain)
208
+
209
+ found_valid = ""
210
+ for email in perms:
211
+ if args.test:
212
+ print(f"[TEST] Generated permutation: {email}")
213
+ if not found_valid:
214
+ found_valid = email # Assume first is valid in test mode
215
+ else:
216
+ is_valid = False
217
+ if args.mode == "validemail":
218
+ is_valid = verify_validemail(email)
219
+ elif args.mode == "reacher-http":
220
+ is_valid = verify_reacher_http(email, args.reacher_url)
221
+ elif args.mode == "reacher-cli":
222
+ is_valid = verify_reacher_cli(email)
223
+
224
+ if is_valid:
225
+ found_valid = email
226
+ break
227
+
228
+ valid_emails.append(found_valid)
229
+
230
+ df["Valid Email"] = valid_emails
231
+
232
+ try:
233
+ df.to_csv(args.output, index=False)
234
+ print(f"Output saved to {args.output}")
235
+ except Exception as e:
236
+ print(f"Error saving output CSV: {e}")
237
+
238
+
239
+ if __name__ == "__main__":
240
+ main()