@opendirectory.dev/skills 0.1.0

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.
Files changed (212) hide show
  1. package/.claude/skills/claude-md-generator/.env.example +7 -0
  2. package/.claude/skills/claude-md-generator/README.md +78 -0
  3. package/.claude/skills/claude-md-generator/SKILL.md +248 -0
  4. package/.claude/skills/claude-md-generator/evals/evals.json +35 -0
  5. package/.claude/skills/claude-md-generator/references/section-guide.md +175 -0
  6. package/dist/e2e.test.d.ts +1 -0
  7. package/dist/e2e.test.js +62 -0
  8. package/dist/fs-adapters.d.ts +4 -0
  9. package/dist/fs-adapters.js +101 -0
  10. package/dist/fs-adapters.test.d.ts +1 -0
  11. package/dist/fs-adapters.test.js +108 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +211 -0
  14. package/dist/transformers.d.ts +6 -0
  15. package/dist/transformers.js +2 -0
  16. package/package.json +25 -0
  17. package/registry.json +226 -0
  18. package/skills/blog-cover-image-cli/.github/workflows/publish.yml +19 -0
  19. package/skills/blog-cover-image-cli/LICENSE +15 -0
  20. package/skills/blog-cover-image-cli/README.md +126 -0
  21. package/skills/blog-cover-image-cli/SKILL.md +7 -0
  22. package/skills/blog-cover-image-cli/agent-skill/blog-cover-generator/README.md +30 -0
  23. package/skills/blog-cover-image-cli/agent-skill/blog-cover-generator/SKILL.md +72 -0
  24. package/skills/blog-cover-image-cli/bin/cli.js +226 -0
  25. package/skills/blog-cover-image-cli/examples/100x_UX_Research_AI_Agent.png +0 -0
  26. package/skills/blog-cover-image-cli/examples/Firecrawl-supabase-bolt.png +0 -0
  27. package/skills/blog-cover-image-cli/examples/Git-City_Case_study_Cover_Image.jpg +0 -0
  28. package/skills/blog-cover-image-cli/examples/THE DISTRIBUTION LAYER (2).png +0 -0
  29. package/skills/blog-cover-image-cli/examples/canva-perplexity-duolingo-cover-image.png +0 -0
  30. package/skills/blog-cover-image-cli/examples/gamma-mistral-veed.png +0 -0
  31. package/skills/blog-cover-image-cli/examples/server-survival-case-study-cover-image(1).png +0 -0
  32. package/skills/blog-cover-image-cli/examples/viral-meme-automation.png +0 -0
  33. package/skills/blog-cover-image-cli/index.js +2 -0
  34. package/skills/blog-cover-image-cli/package-lock.json +2238 -0
  35. package/skills/blog-cover-image-cli/package.json +37 -0
  36. package/skills/blog-cover-image-cli/src/geminiGenerator.js +126 -0
  37. package/skills/blog-cover-image-cli/src/imageValidator.js +54 -0
  38. package/skills/blog-cover-image-cli/src/logoFetcher.js +86 -0
  39. package/skills/claude-md-generator/.env.example +7 -0
  40. package/skills/claude-md-generator/README.md +78 -0
  41. package/skills/claude-md-generator/SKILL.md +254 -0
  42. package/skills/claude-md-generator/evals/evals.json +35 -0
  43. package/skills/claude-md-generator/references/section-guide.md +175 -0
  44. package/skills/cook-the-blog/README.md +86 -0
  45. package/skills/cook-the-blog/SKILL.md +130 -0
  46. package/skills/dependency-update-bot/.env.example +13 -0
  47. package/skills/dependency-update-bot/README.md +101 -0
  48. package/skills/dependency-update-bot/SKILL.md +376 -0
  49. package/skills/dependency-update-bot/evals/evals.json +45 -0
  50. package/skills/dependency-update-bot/references/changelog-patterns.md +201 -0
  51. package/skills/docs-from-code/.env.example +13 -0
  52. package/skills/docs-from-code/README.md +97 -0
  53. package/skills/docs-from-code/SKILL.md +160 -0
  54. package/skills/docs-from-code/evals/evals.json +29 -0
  55. package/skills/docs-from-code/references/extraction-guide.md +174 -0
  56. package/skills/docs-from-code/references/output-template.md +135 -0
  57. package/skills/docs-from-code/scripts/extract_py.py +238 -0
  58. package/skills/docs-from-code/scripts/extract_ts.ts +284 -0
  59. package/skills/docs-from-code/scripts/package.json +18 -0
  60. package/skills/explain-this-pr/README.md +74 -0
  61. package/skills/explain-this-pr/SKILL.md +130 -0
  62. package/skills/explain-this-pr/evals/evals.json +35 -0
  63. package/skills/google-trends-api-skills/README.md +78 -0
  64. package/skills/google-trends-api-skills/SKILL.md +7 -0
  65. package/skills/google-trends-api-skills/google-trends-api/SKILL.md +163 -0
  66. package/skills/google-trends-api-skills/google-trends-api/references/api-responses.md +188 -0
  67. package/skills/google-trends-api-skills/google-trends-api/scripts/discover_keywords.py +344 -0
  68. package/skills/google-trends-api-skills/seo-keyword-research/SKILL.md +205 -0
  69. package/skills/google-trends-api-skills/seo-keyword-research/references/keyword-placement-guide.md +89 -0
  70. package/skills/google-trends-api-skills/seo-keyword-research/references/tech-blog-examples.md +207 -0
  71. package/skills/google-trends-api-skills/seo-keyword-research/scripts/blog_seo_research.py +373 -0
  72. package/skills/hackernews-intel/.env.example +33 -0
  73. package/skills/hackernews-intel/README.md +161 -0
  74. package/skills/hackernews-intel/SKILL.md +156 -0
  75. package/skills/hackernews-intel/evals/evals.json +35 -0
  76. package/skills/hackernews-intel/package.json +15 -0
  77. package/skills/hackernews-intel/scripts/monitor-hn.js +258 -0
  78. package/skills/kill-the-standup/.env.example +22 -0
  79. package/skills/kill-the-standup/README.md +84 -0
  80. package/skills/kill-the-standup/SKILL.md +169 -0
  81. package/skills/kill-the-standup/evals/evals.json +35 -0
  82. package/skills/kill-the-standup/references/standup-format.md +102 -0
  83. package/skills/linkedin-post-generator/.env.example +14 -0
  84. package/skills/linkedin-post-generator/README.md +107 -0
  85. package/skills/linkedin-post-generator/SKILL.md +228 -0
  86. package/skills/linkedin-post-generator/evals/evals.json +35 -0
  87. package/skills/linkedin-post-generator/references/linkedin-format.md +216 -0
  88. package/skills/linkedin-post-generator/references/output-template.md +154 -0
  89. package/skills/llms-txt-generator/.env.example +18 -0
  90. package/skills/llms-txt-generator/README.md +142 -0
  91. package/skills/llms-txt-generator/SKILL.md +176 -0
  92. package/skills/llms-txt-generator/evals/evals.json +35 -0
  93. package/skills/llms-txt-generator/references/llms-txt-spec.md +88 -0
  94. package/skills/llms-txt-generator/references/output-template.md +76 -0
  95. package/skills/llms-txt-generator/test-output/genzcareer.in/llms.txt +31 -0
  96. package/skills/luma-attendees-scraper/README.md +170 -0
  97. package/skills/luma-attendees-scraper/SKILL.md +7 -0
  98. package/skills/luma-attendees-scraper/luma_attendees_export.js +223 -0
  99. package/skills/meeting-brief-generator/.env.example +21 -0
  100. package/skills/meeting-brief-generator/README.md +90 -0
  101. package/skills/meeting-brief-generator/SKILL.md +275 -0
  102. package/skills/meeting-brief-generator/evals/evals.json +35 -0
  103. package/skills/meeting-brief-generator/references/brief-format.md +114 -0
  104. package/skills/meeting-brief-generator/references/output-template.md +150 -0
  105. package/skills/meta-ads-skill/README.md +100 -0
  106. package/skills/meta-ads-skill/SKILL.md +7 -0
  107. package/skills/meta-ads-skill/meta-ads-skill/SKILL.md +41 -0
  108. package/skills/meta-ads-skill/meta-ads-skill/references/report_templates.md +47 -0
  109. package/skills/meta-ads-skill/meta-ads-skill/references/workflows.md +51 -0
  110. package/skills/meta-ads-skill/meta-ads-skill/scripts/auth_check.py +22 -0
  111. package/skills/meta-ads-skill/meta-ads-skill/scripts/formatters.py +46 -0
  112. package/skills/newsletter-digest/.env.example +20 -0
  113. package/skills/newsletter-digest/README.md +147 -0
  114. package/skills/newsletter-digest/SKILL.md +221 -0
  115. package/skills/newsletter-digest/evals/evals.json +35 -0
  116. package/skills/newsletter-digest/feeds.json +7 -0
  117. package/skills/newsletter-digest/package.json +15 -0
  118. package/skills/newsletter-digest/references/digest-format.md +123 -0
  119. package/skills/newsletter-digest/references/output-template.md +136 -0
  120. package/skills/newsletter-digest/scripts/fetch-feeds.js +141 -0
  121. package/skills/newsletter-digest/scripts/ghost-publish.js +147 -0
  122. package/skills/noise2blog/.env.example +16 -0
  123. package/skills/noise2blog/README.md +107 -0
  124. package/skills/noise2blog/SKILL.md +229 -0
  125. package/skills/noise2blog/evals/evals.json +35 -0
  126. package/skills/noise2blog/references/blog-format.md +188 -0
  127. package/skills/noise2blog/references/output-template.md +184 -0
  128. package/skills/outreach-sequence-builder/.env.example +12 -0
  129. package/skills/outreach-sequence-builder/README.md +108 -0
  130. package/skills/outreach-sequence-builder/SKILL.md +248 -0
  131. package/skills/outreach-sequence-builder/evals/evals.json +36 -0
  132. package/skills/outreach-sequence-builder/references/output-template.md +171 -0
  133. package/skills/outreach-sequence-builder/references/sequence-format.md +167 -0
  134. package/skills/outreach-sequence-builder/references/signal-playbook.md +117 -0
  135. package/skills/position-me/README.md +71 -0
  136. package/skills/position-me/SKILL.md +7 -0
  137. package/skills/position-me/position-me/SKILL.md +50 -0
  138. package/skills/position-me/position-me/references/EVALUATION_SOP.md +40 -0
  139. package/skills/position-me/position-me/references/REPORT_TEMPLATE.md +58 -0
  140. package/skills/position-me/position-me/scripts/extract_links.py +49 -0
  141. package/skills/pr-description-writer/README.md +81 -0
  142. package/skills/pr-description-writer/SKILL.md +141 -0
  143. package/skills/pr-description-writer/evals/evals.json +35 -0
  144. package/skills/pr-description-writer/references/pr-format-guide.md +145 -0
  145. package/skills/producthunt-launch-kit/.env.example +7 -0
  146. package/skills/producthunt-launch-kit/README.md +95 -0
  147. package/skills/producthunt-launch-kit/SKILL.md +380 -0
  148. package/skills/producthunt-launch-kit/evals/evals.json +35 -0
  149. package/skills/producthunt-launch-kit/references/copy-rules.md +124 -0
  150. package/skills/reddit-icp-monitor/.env.example +16 -0
  151. package/skills/reddit-icp-monitor/README.md +117 -0
  152. package/skills/reddit-icp-monitor/SKILL.md +271 -0
  153. package/skills/reddit-icp-monitor/evals/evals.json +40 -0
  154. package/skills/reddit-icp-monitor/references/icp-format.md +131 -0
  155. package/skills/reddit-icp-monitor/references/reply-rules.md +110 -0
  156. package/skills/reddit-post-engine/.env.example +13 -0
  157. package/skills/reddit-post-engine/README.md +103 -0
  158. package/skills/reddit-post-engine/SKILL.md +303 -0
  159. package/skills/reddit-post-engine/evals/evals.json +35 -0
  160. package/skills/reddit-post-engine/references/subreddit-playbook.md +156 -0
  161. package/skills/schema-markup-generator/.env.example +19 -0
  162. package/skills/schema-markup-generator/README.md +114 -0
  163. package/skills/schema-markup-generator/SKILL.md +192 -0
  164. package/skills/schema-markup-generator/evals/evals.json +35 -0
  165. package/skills/schema-markup-generator/references/json-ld-spec.md +263 -0
  166. package/skills/schema-markup-generator/references/output-template.md +556 -0
  167. package/skills/show-hn-writer/.env.example +14 -0
  168. package/skills/show-hn-writer/README.md +88 -0
  169. package/skills/show-hn-writer/SKILL.md +303 -0
  170. package/skills/show-hn-writer/evals/evals.json +35 -0
  171. package/skills/show-hn-writer/references/hn-rules.md +74 -0
  172. package/skills/show-hn-writer/references/title-formulas.md +93 -0
  173. package/skills/stargazer/README.md +79 -0
  174. package/skills/stargazer/SKILL.md +7 -0
  175. package/skills/stargazer/stargazer-skill/SKILL.md +58 -0
  176. package/skills/stargazer/stargazer-skill/assets/.env.example +18 -0
  177. package/skills/stargazer/stargazer-skill/scripts/convert_to_csv.py +63 -0
  178. package/skills/stargazer/stargazer-skill/scripts/count_emails.py +52 -0
  179. package/skills/stargazer/stargazer-skill/scripts/stargazer_deep_extractor.py +450 -0
  180. package/skills/tweet-thread-from-blog/.env.example +14 -0
  181. package/skills/tweet-thread-from-blog/README.md +109 -0
  182. package/skills/tweet-thread-from-blog/SKILL.md +177 -0
  183. package/skills/tweet-thread-from-blog/evals/evals.json +35 -0
  184. package/skills/tweet-thread-from-blog/references/output-template.md +193 -0
  185. package/skills/tweet-thread-from-blog/references/thread-format.md +107 -0
  186. package/skills/twitter-GTM-find-skill/README.md +43 -0
  187. package/skills/twitter-GTM-find-skill/SKILL.md +7 -0
  188. package/skills/twitter-GTM-find-skill/twitter-GTM-find/SKILL.md +37 -0
  189. package/skills/twitter-GTM-find-skill/twitter-GTM-find/references/icp-checklist.md +35 -0
  190. package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/package.json +23 -0
  191. package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/run_pipeline.sh +8 -0
  192. package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/debug.ts +23 -0
  193. package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/extractor.ts +79 -0
  194. package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/icp-filter.ts +87 -0
  195. package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/index.ts +94 -0
  196. package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/src/scraper.ts +41 -0
  197. package/skills/twitter-GTM-find-skill/twitter-GTM-find/scripts/tsconfig.json +13 -0
  198. package/skills/yc-intent-radar-skill/README.md +39 -0
  199. package/skills/yc-intent-radar-skill/SKILL.md +7 -0
  200. package/skills/yc-intent-radar-skill/yc-jobs-scraper/SKILL.md +59 -0
  201. package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/auth.js +29 -0
  202. package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/db.js +62 -0
  203. package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/export_radar_candidates.js +40 -0
  204. package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/package-lock.json +1525 -0
  205. package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/package.json +12 -0
  206. package/skills/yc-intent-radar-skill/yc-jobs-scraper/scripts/scraper.js +217 -0
  207. package/src/e2e.test.ts +35 -0
  208. package/src/fs-adapters.test.ts +91 -0
  209. package/src/fs-adapters.ts +65 -0
  210. package/src/index.ts +182 -0
  211. package/src/transformers.ts +6 -0
  212. package/tsconfig.json +8 -0
@@ -0,0 +1,170 @@
1
+ # Luma Attendees Scraper
2
+
3
+ <img width="1280" height="640" alt="luma-attendees-scraper-cover" src="https://github.com/user-attachments/assets/44acd3d4-8c4d-4c8a-823b-96cd6585e5ae" />
4
+
5
+ Browser-console script to export attendee data from a Luma event into CSV.
6
+
7
+ [luma-attendees-scraper.webm](https://github.com/user-attachments/assets/d324ee4a-8006-44ae-9cee-dca414ef03e9)
8
+
9
+ ## Note
10
+
11
+ For a test run, set:
12
+
13
+ ```js
14
+ sampleLimit: 5,
15
+ ```
16
+
17
+ This exports a small sample so you can verify the output columns first.
18
+
19
+ If the sample looks correct, switch to:
20
+
21
+ ```js
22
+ sampleLimit: Infinity,
23
+ ```
24
+
25
+ This exports all available attendees for the event.
26
+
27
+ The script:
28
+
29
+ - works from the browser console
30
+ - accepts any Luma event URL
31
+ - fetches the event's internal `event_api_id` automatically
32
+ - exports attendee data from Luma's paginated guest API
33
+ - includes public social handles already present in the guest payload
34
+
35
+ ## Files
36
+
37
+ - `luma_attendees_export.js`: main browser-console script
38
+
39
+ ## Requirements
40
+
41
+ - You must be logged into Luma in your browser.
42
+ - Your account must have permission to view the event attendee list.
43
+ - The script should be run on `luma.com` while your authenticated Luma session is active.
44
+
45
+ If Luma returns `403` with a message like `You don't have access to this event.`, the problem is account access, not the script.
46
+
47
+ ## How To Use
48
+
49
+ 1. Open the target Luma event page in your browser.
50
+ 2. Open DevTools Console.
51
+ 3. If Chrome blocks paste in the console, type `allow pasting` and press Enter.
52
+ 4. Open `luma_attendees_export.js` from this repo.
53
+ 5. Edit the `CONFIG` block at the top if needed.
54
+ 6. Paste the full script into the browser console and run it.
55
+ 7. Wait for the CSV download.
56
+
57
+ ## Recommended Flow
58
+
59
+ ### 1. Verify on a small sample first
60
+
61
+ Set:
62
+
63
+ ```js
64
+ sampleLimit: 5,
65
+ ```
66
+
67
+ This exports only the first 5 attendees and is the safest way to confirm the output columns look right.
68
+
69
+ ### 2. Run the full export
70
+
71
+ After the sample looks correct, switch to:
72
+
73
+ ```js
74
+ sampleLimit: Infinity,
75
+ ```
76
+
77
+ This exports the full attendee list.
78
+
79
+ ## Config
80
+
81
+ The script starts with:
82
+
83
+ ```js
84
+ const CONFIG = {
85
+ eventUrl: window.location.href,
86
+ sampleLimit: 5,
87
+ pageSize: 100,
88
+ requestDelayMs: 150,
89
+ apiBase: "https://api2.luma.com",
90
+ outputFileName: "",
91
+ };
92
+ ```
93
+
94
+ ### Config fields
95
+
96
+ - `eventUrl`: Luma event URL to export. Default is the current page URL.
97
+ - `sampleLimit`: number of attendees to export. Use `5` for testing or `Infinity` for all attendees.
98
+ - `pageSize`: API page size. `100` is a good default.
99
+ - `requestDelayMs`: delay between paginated API requests.
100
+ - `apiBase`: Luma API base URL.
101
+ - `outputFileName`: optional custom CSV filename. Leave empty to auto-generate one.
102
+
103
+ ## CSV Columns
104
+
105
+ The export includes:
106
+
107
+ - `Event Name`
108
+ - `Event URL`
109
+ - `Event API ID`
110
+ - `Section`
111
+ - `Name`
112
+ - `First Name`
113
+ - `Last Name`
114
+ - `Bio`
115
+ - `Profile URL`
116
+ - `Username`
117
+ - `API ID`
118
+ - `Instagram`
119
+ - `X`
120
+ - `TikTok`
121
+ - `LinkedIn`
122
+ - `Website`
123
+ - `YouTube`
124
+ - `Avatar URL`
125
+ - `Verified`
126
+ - `Timezone`
127
+ - `Last Online At`
128
+ - `Tickets Registered`
129
+
130
+ ## Why This Works Better Than DOM Scraping
131
+
132
+ Many Luma event pages no longer render the full attendee list directly in the page DOM. Older console scripts that scrape `a[href^="/user/"]` only capture whatever is currently visible on the page, which may be one attendee or a very small subset.
133
+
134
+ This script uses the paginated guest API that Luma itself calls when loading attendee data, so it can export the full list when your account has access.
135
+
136
+ ## Data Trust Model
137
+
138
+ - Attendee rows are exported from Luma's backend guest response.
139
+ - Social fields are read from the guest payload itself when available.
140
+ - The script does not invent people, create fake profiles, or synthesize attendee data.
141
+ - If a field is missing from Luma's response, the CSV cell is left blank.
142
+
143
+ ## Troubleshooting
144
+
145
+ ### `403 You don't have access to this event.`
146
+
147
+ Your Luma account cannot access that event's guest list. Use an account that can view the attendees.
148
+
149
+ ### CSV downloads but values are blank
150
+
151
+ This usually means the guest payload shape has changed. Check the `Sample guest:` object printed in the console and update the field mapping.
152
+
153
+ ### Only a few attendees exported
154
+
155
+ Make sure `sampleLimit` is not still set to a small number like `5`.
156
+
157
+ ### No download starts
158
+
159
+ Some browsers block downloads from DevTools-triggered scripts until user interaction is allowed. Re-run after enabling downloads for the site.
160
+
161
+ ## Suggested Usage Pattern
162
+
163
+ 1. Run with `sampleLimit: 5`
164
+ 2. Inspect the CSV
165
+ 3. Switch to `sampleLimit: Infinity`
166
+ 4. Run again for the full export
167
+
168
+ ## Disclaimer
169
+
170
+ Use this responsibly and only for events and attendee lists your account is authorized to access.
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: luma-attendees-scraper
3
+ description: A skill for luma-attendees-scraper
4
+ author: OpenDirectory
5
+ version: 1.0.0
6
+ ---
7
+
@@ -0,0 +1,223 @@
1
+ (async function () {
2
+ const CONFIG = {
3
+ eventUrl: window.location.href,
4
+ sampleLimit: 5,
5
+ pageSize: 100,
6
+ requestDelayMs: 150,
7
+ apiBase: "https://api2.luma.com",
8
+ outputFileName: "",
9
+ };
10
+
11
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
12
+
13
+ function normalizeHandleUrl(value, baseUrl) {
14
+ if (!value) return "";
15
+ if (/^https?:\/\//i.test(value)) return value;
16
+ if (value.startsWith("/")) return `${baseUrl}${value}`;
17
+ return `${baseUrl}/${value.replace(/^@/, "")}`;
18
+ }
19
+
20
+ function getProfileUrl(user) {
21
+ if (!user) return "";
22
+ if (user.profile_url) return user.profile_url;
23
+ if (user.user_url) return user.user_url;
24
+ if (user.url) return user.url;
25
+ if (user.username) return `https://luma.com/user/${user.username}`;
26
+ return "";
27
+ }
28
+
29
+ function csvEscape(value) {
30
+ return `"${String(value ?? "").replace(/"/g, '""')}"`;
31
+ }
32
+
33
+ function slugify(value) {
34
+ return String(value || "luma-event")
35
+ .toLowerCase()
36
+ .replace(/[^a-z0-9]+/g, "-")
37
+ .replace(/^-+|-+$/g, "")
38
+ .slice(0, 80);
39
+ }
40
+
41
+ function extractEventApiId(html) {
42
+ const patterns = [
43
+ /"event_api_id":"(evt-[^"]+)"/,
44
+ /"api_id":"(evt-[^"]+)"/,
45
+ /event_api_id\\":\\"(evt-[^"]+)/,
46
+ /api_id\\":\\"(evt-[^"]+)/,
47
+ ];
48
+
49
+ for (const pattern of patterns) {
50
+ const match = html.match(pattern);
51
+ if (match) return match[1];
52
+ }
53
+
54
+ throw new Error("Could not find event_api_id in the event page HTML.");
55
+ }
56
+
57
+ function extractEventName(html) {
58
+ const titleMatch = html.match(/<title>(.*?)<\/title>/i);
59
+ if (titleMatch) {
60
+ return titleMatch[1]
61
+ .replace(/\s*[|·-]\s*Luma.*$/i, "")
62
+ .replace(/\s+/g, " ")
63
+ .trim();
64
+ }
65
+
66
+ const namePatterns = [
67
+ /"name":"([^"]+)"/,
68
+ /"event_name":"([^"]+)"/,
69
+ ];
70
+
71
+ for (const pattern of namePatterns) {
72
+ const match = html.match(pattern);
73
+ if (match) return match[1];
74
+ }
75
+
76
+ return "luma-event";
77
+ }
78
+
79
+ async function fetchJson(url) {
80
+ const response = await fetch(url, {
81
+ credentials: "include",
82
+ headers: {
83
+ Accept: "application/json",
84
+ "Content-Type": "application/json",
85
+ },
86
+ });
87
+
88
+ if (!response.ok) {
89
+ const text = await response.text();
90
+ throw new Error(`Request failed (${response.status}): ${text}`);
91
+ }
92
+
93
+ return response.json();
94
+ }
95
+
96
+ async function fetchEventContext(eventUrl) {
97
+ const response = await fetch(eventUrl, { credentials: "include" });
98
+ if (!response.ok) {
99
+ throw new Error(`Failed to load event page (${response.status})`);
100
+ }
101
+
102
+ const html = await response.text();
103
+ return {
104
+ eventApiId: extractEventApiId(html),
105
+ eventName: extractEventName(html),
106
+ eventUrl,
107
+ };
108
+ }
109
+
110
+ async function fetchGuests(eventApiId, limit) {
111
+ const guests = [];
112
+ let cursor = null;
113
+
114
+ while (true) {
115
+ const url = new URL(`${CONFIG.apiBase}/event/get-guest-list`);
116
+ url.searchParams.set("event_api_id", eventApiId);
117
+ url.searchParams.set("pagination_limit", String(CONFIG.pageSize));
118
+ if (cursor) url.searchParams.set("pagination_cursor", cursor);
119
+
120
+ const data = await fetchJson(url.toString());
121
+ const entries = Array.isArray(data.entries) ? data.entries : [];
122
+
123
+ guests.push(...entries);
124
+ console.log(`Fetched ${guests.length} guests so far`);
125
+
126
+ if (guests.length >= limit) {
127
+ return guests.slice(0, limit);
128
+ }
129
+
130
+ if (!data.has_more || !data.next_cursor) {
131
+ return guests;
132
+ }
133
+
134
+ cursor = data.next_cursor;
135
+ await sleep(CONFIG.requestDelayMs);
136
+ }
137
+ }
138
+
139
+ function buildRows(eventContext, guests) {
140
+ const rows = [[
141
+ "Event Name",
142
+ "Event URL",
143
+ "Event API ID",
144
+ "Section",
145
+ "Name",
146
+ "First Name",
147
+ "Last Name",
148
+ "Bio",
149
+ "Profile URL",
150
+ "Username",
151
+ "API ID",
152
+ "Instagram",
153
+ "X",
154
+ "TikTok",
155
+ "LinkedIn",
156
+ "Website",
157
+ "YouTube",
158
+ "Avatar URL",
159
+ "Verified",
160
+ "Timezone",
161
+ "Last Online At",
162
+ "Tickets Registered",
163
+ ]];
164
+
165
+ for (const guest of guests) {
166
+ const user = guest.user || {};
167
+
168
+ rows.push([
169
+ eventContext.eventName,
170
+ eventContext.eventUrl,
171
+ eventContext.eventApiId,
172
+ guest.section_label || "",
173
+ user.name || "",
174
+ user.first_name || "",
175
+ user.last_name || "",
176
+ (user.bio_short || "").replace(/\s+/g, " ").trim(),
177
+ getProfileUrl(user),
178
+ user.username || "",
179
+ user.api_id || guest.api_id || "",
180
+ normalizeHandleUrl(user.instagram_handle, "https://instagram.com"),
181
+ normalizeHandleUrl(user.twitter_handle, "https://x.com"),
182
+ normalizeHandleUrl(user.tiktok_handle, "https://www.tiktok.com/@"),
183
+ normalizeHandleUrl(user.linkedin_handle, "https://www.linkedin.com"),
184
+ user.website || "",
185
+ normalizeHandleUrl(user.youtube_handle, "https://www.youtube.com/@"),
186
+ user.avatar_url || "",
187
+ user.is_verified ? "true" : "false",
188
+ user.timezone || "",
189
+ user.last_online_at || "",
190
+ guest.num_tickets_registered || 0,
191
+ ]);
192
+ }
193
+
194
+ return rows;
195
+ }
196
+
197
+ function downloadCsv(rows, fileName) {
198
+ const csv = rows.map((row) => row.map(csvEscape).join(",")).join("\n");
199
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
200
+ const url = URL.createObjectURL(blob);
201
+ const anchor = document.createElement("a");
202
+ anchor.href = url;
203
+ anchor.download = fileName;
204
+ document.body.appendChild(anchor);
205
+ anchor.click();
206
+ document.body.removeChild(anchor);
207
+ URL.revokeObjectURL(url);
208
+ }
209
+
210
+ const eventContext = await fetchEventContext(CONFIG.eventUrl);
211
+ const limit = Number.isFinite(CONFIG.sampleLimit) ? CONFIG.sampleLimit : Infinity;
212
+ const guests = await fetchGuests(eventContext.eventApiId, limit);
213
+ const rows = buildRows(eventContext, guests);
214
+ const fileName =
215
+ CONFIG.outputFileName ||
216
+ `${slugify(eventContext.eventName)}-${Number.isFinite(limit) ? `sample-${limit}` : "all"}-attendees.csv`;
217
+
218
+ console.log("Event context:", eventContext);
219
+ console.log("Guests exported:", guests.length);
220
+ console.log("Sample guest:", guests[0] || null);
221
+
222
+ downloadCsv(rows, fileName);
223
+ })();
@@ -0,0 +1,21 @@
1
+ # meeting-brief-generator — Environment Variables
2
+ # =================================================
3
+ # Tavily and Gemini are required. Notion is optional.
4
+
5
+ # Required: Tavily API key for company research
6
+ # Get it: app.tavily.com, API Keys section
7
+ TAVILY_API_KEY=your_tavily_api_key_here
8
+
9
+ # Required: Google Gemini API key for brief synthesis
10
+ # Get it: aistudio.google.com, Get API key
11
+ GEMINI_API_KEY=your_gemini_api_key_here
12
+
13
+ # Optional: Notion integration token for saving briefs
14
+ # Get it: notion.so/my-integrations, Create new integration, copy Internal Integration Token
15
+ # You must share your target database with this integration in Notion
16
+ NOTION_TOKEN=secret_your_notion_token_here
17
+
18
+ # Optional: Notion database ID where briefs are saved
19
+ # Get it: open the database in Notion, copy the ID from the URL
20
+ # URL format: notion.so/workspace/{DATABASE_ID}?v=...
21
+ NOTION_DATABASE_ID=your_notion_database_id_here
@@ -0,0 +1,90 @@
1
+ # meeting-brief-generator
2
+
3
+ <img width="1280" height="640" alt="meeting-brief-generator" src="https://github.com/user-attachments/assets/30026bc4-657a-4ce9-8c0e-4dd2654783f8" />
4
+
5
+
6
+ Walk into every sales or business development call prepared. Give the skill a company name and it runs targeted research, synthesizes a 1-page brief, and optionally saves it to Notion.
7
+
8
+ ## What It Does
9
+
10
+ - Runs 6-8 targeted Tavily searches covering company overview, recent news, tech stack, product, competitors, funding, and contact background
11
+ - Synthesizes results into a structured 1-page brief using Gemini
12
+ - Every claim cites a source URL from the research
13
+ - Optionally saves the brief to a Notion database
14
+ - Handles low-data companies by marking gaps instead of inventing content
15
+
16
+ ## Requirements
17
+
18
+ | Requirement | Purpose | How to Set Up |
19
+ |------------|---------|--------------|
20
+ | Tavily API key | Company research | app.tavily.com, API Keys |
21
+ | Gemini API key | Brief synthesis | aistudio.google.com, Get API key |
22
+ | Notion token (optional) | Saving briefs | notion.so/my-integrations |
23
+
24
+ ## Setup
25
+
26
+ ```bash
27
+ cp .env.example .env
28
+ ```
29
+
30
+ Fill in:
31
+ - `TAVILY_API_KEY` (required)
32
+ - `GEMINI_API_KEY` (required)
33
+ - `NOTION_TOKEN` and `NOTION_DATABASE_ID` (optional, for saving briefs)
34
+
35
+ ## How to Use
36
+
37
+ Basic brief with company only:
38
+
39
+ ```
40
+ "Prepare me for a meeting with Stripe next Tuesday"
41
+ "Generate a meeting brief for Vercel"
42
+ "Research Acme Corp before my call tomorrow"
43
+ ```
44
+
45
+ With contact and meeting type:
46
+
47
+ ```
48
+ "Prepare a discovery call brief for Linear. I'm meeting with the VP Engineering, Jordan Lee."
49
+ "Create a pre-call brief for Notion. Demo call on April 20."
50
+ ```
51
+
52
+ Save to Notion:
53
+
54
+ ```
55
+ "Generate a meeting brief for Figma and save it to Notion"
56
+ ```
57
+
58
+ ## Brief Sections
59
+
60
+ | Section | Content |
61
+ |---------|---------|
62
+ | Company Snapshot | What they do, size, funding stage, HQ |
63
+ | Recent News | Last 30 days, source URLs |
64
+ | Decision Maker | Name, title, background (if contact provided) |
65
+ | Tech Stack Signals | Tools spotted in job posts, blog, GitHub |
66
+ | Competitive Context | Who they compete with and how |
67
+ | Talking Points | Because/mention/to formula, 3-5 bullets |
68
+ | Open Questions | Company-specific discovery questions |
69
+
70
+ ## Output Format
71
+
72
+ One page, under 400 words. Every claim has a source URL. Talking points follow the format: "Because [finding from research], mention [point] to [goal]."
73
+
74
+ ## Project Structure
75
+
76
+ ```
77
+ meeting-brief-generator/
78
+ ├── SKILL.md
79
+ ├── README.md
80
+ ├── .env.example
81
+ ├── evals/
82
+ │ └── evals.json
83
+ └── references/
84
+ ├── brief-format.md
85
+ └── output-template.md
86
+ ```
87
+
88
+ ## License
89
+
90
+ MIT