nod-shout 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.
- package/README.md +82 -0
- package/TASK-AGENT-POSTS.md +112 -0
- package/assets/shout-default.svg +5 -0
- package/bin/shout +68 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/ai.d.ts +13 -0
- package/dist/lib/ai.d.ts.map +1 -0
- package/dist/lib/ai.js +135 -0
- package/dist/lib/ai.js.map +1 -0
- package/dist/lib/content-filter.d.ts +74 -0
- package/dist/lib/content-filter.d.ts.map +1 -0
- package/dist/lib/content-filter.js +188 -0
- package/dist/lib/content-filter.js.map +1 -0
- package/dist/lib/context-extractor.d.ts +39 -0
- package/dist/lib/context-extractor.d.ts.map +1 -0
- package/dist/lib/context-extractor.js +170 -0
- package/dist/lib/context-extractor.js.map +1 -0
- package/dist/lib/match-engine.d.ts +31 -0
- package/dist/lib/match-engine.d.ts.map +1 -0
- package/dist/lib/match-engine.js +322 -0
- package/dist/lib/match-engine.js.map +1 -0
- package/dist/lib/metadata.d.ts +7 -0
- package/dist/lib/metadata.d.ts.map +1 -0
- package/dist/lib/metadata.js +311 -0
- package/dist/lib/metadata.js.map +1 -0
- package/dist/lib/skills.d.ts +3 -0
- package/dist/lib/skills.d.ts.map +1 -0
- package/dist/lib/skills.js +20 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/lib/supabase.d.ts +2 -0
- package/dist/lib/supabase.d.ts.map +1 -0
- package/dist/lib/supabase.js +8 -0
- package/dist/lib/supabase.js.map +1 -0
- package/dist/tools/collections.d.ts +3 -0
- package/dist/tools/collections.d.ts.map +1 -0
- package/dist/tools/collections.js +142 -0
- package/dist/tools/collections.js.map +1 -0
- package/dist/tools/intros.d.ts +3 -0
- package/dist/tools/intros.d.ts.map +1 -0
- package/dist/tools/intros.js +483 -0
- package/dist/tools/intros.js.map +1 -0
- package/dist/tools/links.d.ts +3 -0
- package/dist/tools/links.d.ts.map +1 -0
- package/dist/tools/links.js +424 -0
- package/dist/tools/links.js.map +1 -0
- package/dist/tools/posts.d.ts +3 -0
- package/dist/tools/posts.d.ts.map +1 -0
- package/dist/tools/posts.js +212 -0
- package/dist/tools/posts.js.map +1 -0
- package/dist/tools/settings.d.ts +3 -0
- package/dist/tools/settings.d.ts.map +1 -0
- package/dist/tools/settings.js +45 -0
- package/dist/tools/settings.js.map +1 -0
- package/dist/tools/shout_agent_curate.d.ts +28 -0
- package/dist/tools/shout_agent_curate.d.ts.map +1 -0
- package/dist/tools/shout_agent_curate.js +80 -0
- package/dist/tools/shout_agent_curate.js.map +1 -0
- package/dist/tools/social.d.ts +3 -0
- package/dist/tools/social.d.ts.map +1 -0
- package/dist/tools/social.js +169 -0
- package/dist/tools/social.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +24 -0
- package/quick-test.ts +22 -0
- package/regenerate-summaries.ts +111 -0
- package/save-jeffries-shout.ts +38 -0
- package/save-openai-shout.ts +35 -0
- package/save-prcarly.ts +46 -0
- package/save-shout.ts +35 -0
- package/save-techcrunch-shout.ts +59 -0
- package/save-zdnet-shout.ts +36 -0
- package/skills/collection-routing/SKILL.md +34 -0
- package/skills/link-summary/SKILL.md +53 -0
- package/skills/tagging-and-routing/SKILL.md +54 -0
- package/src/index.ts +32 -0
- package/src/lib/ai.ts +166 -0
- package/src/lib/content-filter.ts +258 -0
- package/src/lib/metadata.ts +353 -0
- package/src/lib/skills.ts +21 -0
- package/src/lib/supabase.ts +12 -0
- package/src/tools/collections.ts +182 -0
- package/src/tools/links.ts +524 -0
- package/src/tools/posts.ts +264 -0
- package/src/tools/settings.ts +55 -0
- package/src/tools/shout_agent_curate.ts +95 -0
- package/src/tools/social.ts +206 -0
- package/src/types.ts +66 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/supabase/migrations/001_initial_schema.sql +147 -0
- package/supabase/migrations/20260317010000_decouple_profiles_from_auth.sql +9 -0
- package/supabase/migrations/20260317020000_agent_curation.sql +10 -0
- package/supabase/migrations/20260320000000_agent_posts.sql +41 -0
- package/supabase/migrations/20260320120000_fix_draft_fk.sql +2 -0
- package/supabase/migrations/20260320130000_fix_identity.sql +17 -0
- package/supabase/migrations/20260320170000_intros.sql +118 -0
- package/test-model-comparison.ts +89 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import * as cheerio from "cheerio";
|
|
2
|
+
/**
|
|
3
|
+
* resolve a potentially relative url against a base url.
|
|
4
|
+
*/
|
|
5
|
+
function resolveUrl(candidate, baseUrl) {
|
|
6
|
+
if (!candidate)
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
return new URL(candidate, baseUrl).href;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function cleanText(value) {
|
|
16
|
+
if (!value)
|
|
17
|
+
return null;
|
|
18
|
+
const cleaned = value.replace(/\s+/g, " ").trim();
|
|
19
|
+
return cleaned || null;
|
|
20
|
+
}
|
|
21
|
+
function extractDomainSpecificBodyText($, url) {
|
|
22
|
+
if (url.includes("github.com")) {
|
|
23
|
+
return cleanText([
|
|
24
|
+
$("article.markdown-body").text(),
|
|
25
|
+
$("div[data-testid='repository-sidebar']").text(),
|
|
26
|
+
$("div.Layout-sidebar").text(),
|
|
27
|
+
].join(" "))?.slice(0, 1500) || null;
|
|
28
|
+
}
|
|
29
|
+
if (url.includes("anthropic.com")) {
|
|
30
|
+
return cleanText([
|
|
31
|
+
$("article").text(),
|
|
32
|
+
$("main").text(),
|
|
33
|
+
$("[data-testid='article-content']").text(),
|
|
34
|
+
].join(" "))?.slice(0, 1500) || null;
|
|
35
|
+
}
|
|
36
|
+
if (url.includes("simonwillison.net")) {
|
|
37
|
+
return cleanText([
|
|
38
|
+
$("article").text(),
|
|
39
|
+
$(".entry-content").text(),
|
|
40
|
+
$(".hentry").text(),
|
|
41
|
+
].join(" "))?.slice(0, 1500) || null;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* try to extract structured data from json-ld script tags.
|
|
47
|
+
* looks for Article, BlogPosting, NewsArticle, WebPage types.
|
|
48
|
+
*/
|
|
49
|
+
function extractJsonLd($) {
|
|
50
|
+
const result = { title: null, description: null, image: null, author: null, date: null };
|
|
51
|
+
const targetTypes = ["Article", "BlogPosting", "NewsArticle", "WebPage", "TechArticle"];
|
|
52
|
+
$('script[type="application/ld+json"]').each((_, el) => {
|
|
53
|
+
try {
|
|
54
|
+
const raw = $(el).html();
|
|
55
|
+
if (!raw)
|
|
56
|
+
return;
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
// handle both single objects and arrays
|
|
59
|
+
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
60
|
+
for (const item of items) {
|
|
61
|
+
// also check @graph arrays (common pattern)
|
|
62
|
+
const candidates = item["@graph"] ? [...item["@graph"], item] : [item];
|
|
63
|
+
for (const obj of candidates) {
|
|
64
|
+
const type = obj["@type"];
|
|
65
|
+
const types = Array.isArray(type) ? type : [type];
|
|
66
|
+
if (!types.some((t) => targetTypes.includes(t)))
|
|
67
|
+
continue;
|
|
68
|
+
if (!result.title && obj.headline)
|
|
69
|
+
result.title = obj.headline;
|
|
70
|
+
if (!result.title && obj.name)
|
|
71
|
+
result.title = obj.name;
|
|
72
|
+
if (!result.description && obj.description)
|
|
73
|
+
result.description = obj.description;
|
|
74
|
+
// image can be string, object, or array
|
|
75
|
+
if (!result.image) {
|
|
76
|
+
const img = obj.image;
|
|
77
|
+
if (typeof img === "string")
|
|
78
|
+
result.image = img;
|
|
79
|
+
else if (Array.isArray(img) && img.length > 0) {
|
|
80
|
+
result.image = typeof img[0] === "string" ? img[0] : img[0]?.url || null;
|
|
81
|
+
}
|
|
82
|
+
else if (img?.url) {
|
|
83
|
+
result.image = img.url;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// author can be string, object, or array
|
|
87
|
+
if (!result.author) {
|
|
88
|
+
const auth = obj.author;
|
|
89
|
+
if (typeof auth === "string")
|
|
90
|
+
result.author = auth;
|
|
91
|
+
else if (Array.isArray(auth) && auth.length > 0) {
|
|
92
|
+
result.author = typeof auth[0] === "string" ? auth[0] : auth[0]?.name || null;
|
|
93
|
+
}
|
|
94
|
+
else if (auth?.name) {
|
|
95
|
+
result.author = auth.name;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (!result.date) {
|
|
99
|
+
result.date = obj.datePublished || obj.dateCreated || null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// malformed json-ld, skip
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
/** patterns in image urls that suggest a generic default rather than real content */
|
|
111
|
+
const DEFAULT_IMAGE_PATTERNS = /\b(default|placeholder|logo|og-default|brand|fallback|generic|site-image)\b/i;
|
|
112
|
+
const SHOUT_DEFAULT_IMAGE = "https://ooykzbkcquvreeheaijy.supabase.co/storage/v1/object/public/public/shout/shout-default.svg";
|
|
113
|
+
/**
|
|
114
|
+
* try to find the first real content image from the page body.
|
|
115
|
+
* skips icons, avatars, tracking pixels, and tiny images.
|
|
116
|
+
*/
|
|
117
|
+
function extractFirstContentImage($, baseUrl) {
|
|
118
|
+
const skipSrcPatterns = /\b(avatar|icon|logo|emoji|badge|button|pixel|track|beacon|spacer|blank)\b/i;
|
|
119
|
+
// prefer images inside content containers, fall back to body
|
|
120
|
+
const contentSelectors = [
|
|
121
|
+
"article",
|
|
122
|
+
"main",
|
|
123
|
+
".post-content",
|
|
124
|
+
".entry-content",
|
|
125
|
+
".post-body",
|
|
126
|
+
".article-body",
|
|
127
|
+
];
|
|
128
|
+
let container = null;
|
|
129
|
+
for (const sel of contentSelectors) {
|
|
130
|
+
const el = $(sel);
|
|
131
|
+
if (el.length) {
|
|
132
|
+
container = el.first();
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!container)
|
|
137
|
+
container = $("body");
|
|
138
|
+
if (!container.length)
|
|
139
|
+
return null;
|
|
140
|
+
const imgs = container.find("img");
|
|
141
|
+
for (let i = 0; i < imgs.length; i++) {
|
|
142
|
+
const img = $(imgs[i]);
|
|
143
|
+
const src = img.attr("src") || img.attr("data-src") || null;
|
|
144
|
+
if (!src)
|
|
145
|
+
continue;
|
|
146
|
+
// skip tracking pixels and tiny images by attribute
|
|
147
|
+
const w = parseInt(img.attr("width") || "", 10);
|
|
148
|
+
const h = parseInt(img.attr("height") || "", 10);
|
|
149
|
+
if ((w > 0 && w < 100) || (h > 0 && h < 100))
|
|
150
|
+
continue;
|
|
151
|
+
if (w === 1 || h === 1)
|
|
152
|
+
continue;
|
|
153
|
+
// skip by src pattern
|
|
154
|
+
if (skipSrcPatterns.test(src))
|
|
155
|
+
continue;
|
|
156
|
+
const resolved = resolveUrl(src, baseUrl);
|
|
157
|
+
if (resolved)
|
|
158
|
+
return resolved;
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* detect if a url is a twitter/x.com tweet and extract metadata via fxtwitter api.
|
|
164
|
+
*/
|
|
165
|
+
async function extractTwitterMetadata(url) {
|
|
166
|
+
const tweetMatch = url.match(/(?:twitter\.com|x\.com)\/(\w+)\/status\/(\d+)/);
|
|
167
|
+
if (!tweetMatch)
|
|
168
|
+
return null;
|
|
169
|
+
const [, username, tweetId] = tweetMatch;
|
|
170
|
+
try {
|
|
171
|
+
const res = await fetch(`https://api.fxtwitter.com/${username}/status/${tweetId}`, {
|
|
172
|
+
signal: AbortSignal.timeout(10000),
|
|
173
|
+
});
|
|
174
|
+
if (!res.ok)
|
|
175
|
+
return null;
|
|
176
|
+
const data = await res.json();
|
|
177
|
+
const tweet = data.tweet;
|
|
178
|
+
if (!tweet)
|
|
179
|
+
return null;
|
|
180
|
+
const title = tweet.text || tweet.raw_text?.text || null;
|
|
181
|
+
const description = tweet.article?.preview_text || null;
|
|
182
|
+
const image_url = tweet.article?.cover_media?.media_info?.__typename === "ApiImage"
|
|
183
|
+
? null // article cover doesn't have direct url in this path
|
|
184
|
+
: tweet.media?.photos?.[0]?.url ||
|
|
185
|
+
tweet.media?.all?.[0]?.url ||
|
|
186
|
+
tweet.author?.avatar_url ||
|
|
187
|
+
null;
|
|
188
|
+
const author = tweet.author?.name ? `${tweet.author.name} (@${tweet.author.screen_name})` : null;
|
|
189
|
+
const date = tweet.created_at || null;
|
|
190
|
+
// use tweet text as body content for summarization
|
|
191
|
+
const bodyText = (tweet.text || tweet.raw_text?.text || "").slice(0, 1500) || null;
|
|
192
|
+
// if it's an article tweet, prefer article title
|
|
193
|
+
if (tweet.article?.title) {
|
|
194
|
+
return {
|
|
195
|
+
title: tweet.article.title,
|
|
196
|
+
description: description || title,
|
|
197
|
+
image_url,
|
|
198
|
+
author,
|
|
199
|
+
date,
|
|
200
|
+
bodyText,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return { title, description, image_url, author, date, bodyText };
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
console.error(`fxtwitter extraction error for ${url}:`, err);
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* fetch a url and extract og/meta tags for link preview data.
|
|
212
|
+
* falls back through: twitter api -> og -> twitter -> json-ld -> html tags -> domain-specific selectors.
|
|
213
|
+
*/
|
|
214
|
+
export async function extractMetadata(url) {
|
|
215
|
+
// try twitter/x.com specific extraction first
|
|
216
|
+
const twitterResult = await extractTwitterMetadata(url);
|
|
217
|
+
if (twitterResult)
|
|
218
|
+
return twitterResult;
|
|
219
|
+
try {
|
|
220
|
+
const response = await fetch(url, {
|
|
221
|
+
headers: {
|
|
222
|
+
"User-Agent": "Mozilla/5.0 (compatible; NodShout/0.1; +https://nod.social)",
|
|
223
|
+
Accept: "text/html,application/xhtml+xml",
|
|
224
|
+
},
|
|
225
|
+
signal: AbortSignal.timeout(10000),
|
|
226
|
+
});
|
|
227
|
+
if (!response.ok) {
|
|
228
|
+
console.error(`fetch failed for ${url}: ${response.status}`);
|
|
229
|
+
return { title: null, description: null, image_url: null, author: null, date: null, bodyText: null };
|
|
230
|
+
}
|
|
231
|
+
const html = await response.text();
|
|
232
|
+
const $ = cheerio.load(html);
|
|
233
|
+
// extract json-ld structured data
|
|
234
|
+
const jsonLd = extractJsonLd($);
|
|
235
|
+
// title: og -> twitter -> json-ld -> <title> -> first h1 -> domain-specific
|
|
236
|
+
let title = $('meta[property="og:title"]').attr("content") ||
|
|
237
|
+
$('meta[name="twitter:title"]').attr("content") ||
|
|
238
|
+
jsonLd.title ||
|
|
239
|
+
$("title").text().trim() ||
|
|
240
|
+
$("h1").first().text().trim() ||
|
|
241
|
+
null;
|
|
242
|
+
// simon willison's blog: specific fallback
|
|
243
|
+
if (!title && url.includes("simonwillison")) {
|
|
244
|
+
title =
|
|
245
|
+
$("h1.entry-title").text().trim() ||
|
|
246
|
+
$(".entry-title").text().trim() ||
|
|
247
|
+
$("article h1").text().trim() ||
|
|
248
|
+
$(".hentry h1").text().trim() ||
|
|
249
|
+
null;
|
|
250
|
+
}
|
|
251
|
+
// description: og -> standard meta -> twitter -> json-ld
|
|
252
|
+
const description = $('meta[property="og:description"]').attr("content") ||
|
|
253
|
+
$('meta[name="description"]').attr("content") ||
|
|
254
|
+
$('meta[name="twitter:description"]').attr("content") ||
|
|
255
|
+
jsonLd.description ||
|
|
256
|
+
null;
|
|
257
|
+
// image: og -> twitter:image -> json-ld -> apple-touch-icon -> favicon
|
|
258
|
+
const rawImage = $('meta[property="og:image"]').attr("content") ||
|
|
259
|
+
$('meta[name="twitter:image"]').attr("content") ||
|
|
260
|
+
$('meta[name="twitter:image:src"]').attr("content") ||
|
|
261
|
+
jsonLd.image ||
|
|
262
|
+
$('link[rel="apple-touch-icon"]').attr("href") ||
|
|
263
|
+
$('link[rel="apple-touch-icon-precomposed"]').attr("href") ||
|
|
264
|
+
$('link[rel="icon"][type="image/png"]').attr("href") ||
|
|
265
|
+
$('link[rel="icon"]').attr("href") ||
|
|
266
|
+
null;
|
|
267
|
+
// resolve relative image urls against the page url
|
|
268
|
+
let image_url = resolveUrl(rawImage, url);
|
|
269
|
+
// if no image found or it looks like a generic default, try first content image
|
|
270
|
+
// then apple-touch-icon, then shout branded default
|
|
271
|
+
if (!image_url || DEFAULT_IMAGE_PATTERNS.test(new URL(image_url).pathname)) {
|
|
272
|
+
const contentImage = extractFirstContentImage($, url);
|
|
273
|
+
if (contentImage) {
|
|
274
|
+
image_url = contentImage;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
// try apple-touch-icon (usually 152x152+, much better than tiny favicons)
|
|
278
|
+
const touchIcon = resolveUrl($('link[rel="apple-touch-icon"]').attr("href") ||
|
|
279
|
+
$('link[rel="apple-touch-icon-precomposed"]').attr("href") ||
|
|
280
|
+
null, url);
|
|
281
|
+
image_url = touchIcon || SHOUT_DEFAULT_IMAGE;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// author: meta -> article:author -> json-ld
|
|
285
|
+
const author = $('meta[name="author"]').attr("content") ||
|
|
286
|
+
$('meta[property="article:author"]').attr("content") ||
|
|
287
|
+
jsonLd.author ||
|
|
288
|
+
null;
|
|
289
|
+
// date: article:published_time -> meta date -> json-ld
|
|
290
|
+
const date = $('meta[property="article:published_time"]').attr("content") ||
|
|
291
|
+
$('meta[name="date"]').attr("content") ||
|
|
292
|
+
jsonLd.date ||
|
|
293
|
+
null;
|
|
294
|
+
// extract body text for AI summarization (first ~1500 chars of content)
|
|
295
|
+
const domainBodyText = extractDomainSpecificBodyText($, url);
|
|
296
|
+
const bodyEl = $("article").length ? $("article") : $("main").length ? $("main") : $("body");
|
|
297
|
+
const genericBodyText = bodyEl
|
|
298
|
+
.clone()
|
|
299
|
+
.find("script, style, nav, header, footer, aside, .sidebar, .comments")
|
|
300
|
+
.remove()
|
|
301
|
+
.end()
|
|
302
|
+
.text();
|
|
303
|
+
const bodyText = cleanText(domainBodyText || genericBodyText)?.slice(0, 1500) || null;
|
|
304
|
+
return { title, description, image_url, author, date, bodyText };
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
console.error(`metadata extraction error for ${url}:`, err);
|
|
308
|
+
return { title: null, description: null, image_url: null, author: null, date: null, bodyText: null };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/lib/metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAOlD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
const SKILLS_DIR = join(process.cwd(), "skills");
|
|
4
|
+
export function loadSkill(name) {
|
|
5
|
+
try {
|
|
6
|
+
return readFileSync(join(SKILLS_DIR, name, "SKILL.md"), "utf8");
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function loadSkills(names) {
|
|
13
|
+
return names
|
|
14
|
+
.map((name) => {
|
|
15
|
+
const content = loadSkill(name).trim();
|
|
16
|
+
return content ? `\n\n[skill:${name}]\n${content}` : "";
|
|
17
|
+
})
|
|
18
|
+
.join("");
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;AAEjD,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAe;IACxC,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,OAAO,OAAO,CAAC,CAAC,CAAC,cAAc,IAAI,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/lib/supabase.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,QAAQ,mFAAyC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createClient } from "@supabase/supabase-js";
|
|
2
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
3
|
+
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SERVICE_KEY;
|
|
4
|
+
if (!supabaseUrl || !supabaseKey) {
|
|
5
|
+
throw new Error("missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY environment variables");
|
|
6
|
+
}
|
|
7
|
+
export const supabase = createClient(supabaseUrl, supabaseKey);
|
|
8
|
+
//# sourceMappingURL=supabase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../src/lib/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;AAE9F,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../../src/tools/collections.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,QAiLxD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { supabase } from "../lib/supabase.js";
|
|
3
|
+
export function registerCollectionTools(server) {
|
|
4
|
+
// shout_create_collection
|
|
5
|
+
server.tool("shout_create_collection", "create a new collection to organize your shouts.", {
|
|
6
|
+
user_id: z.string().uuid().describe("the user's id"),
|
|
7
|
+
name: z.string().describe("collection name"),
|
|
8
|
+
description: z.string().optional().describe("collection description"),
|
|
9
|
+
auto_rules: z
|
|
10
|
+
.record(z.unknown())
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("auto-sort rules (e.g. tag-based routing)"),
|
|
13
|
+
}, async ({ user_id, name, description, auto_rules }) => {
|
|
14
|
+
// generate slug from name
|
|
15
|
+
const slug = name
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
18
|
+
.replace(/\s+/g, "-")
|
|
19
|
+
.replace(/-+/g, "-")
|
|
20
|
+
.trim();
|
|
21
|
+
const { data, error } = await supabase
|
|
22
|
+
.from("collections")
|
|
23
|
+
.insert({
|
|
24
|
+
user_id,
|
|
25
|
+
name,
|
|
26
|
+
description: description || null,
|
|
27
|
+
slug,
|
|
28
|
+
auto_rules: auto_rules || null,
|
|
29
|
+
})
|
|
30
|
+
.select()
|
|
31
|
+
.single();
|
|
32
|
+
if (error) {
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{ type: "text", text: `error creating collection: ${error.message}` },
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: "text",
|
|
43
|
+
text: `collection "${name}" created (slug: ${slug}, id: ${data.id})`,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
// shout_add_to_collection
|
|
49
|
+
server.tool("shout_add_to_collection", "add a shout to a collection by slug.", {
|
|
50
|
+
shout_id: z.string().uuid().describe("the shout id"),
|
|
51
|
+
user_id: z.string().uuid().describe("the user's id"),
|
|
52
|
+
collection_slug: z.string().describe("the collection slug to add to"),
|
|
53
|
+
}, async ({ shout_id, user_id, collection_slug }) => {
|
|
54
|
+
// resolve collection by slug for this user
|
|
55
|
+
const { data: col, error: colErr } = await supabase
|
|
56
|
+
.from("collections")
|
|
57
|
+
.select("id, name")
|
|
58
|
+
.eq("user_id", user_id)
|
|
59
|
+
.eq("slug", collection_slug)
|
|
60
|
+
.single();
|
|
61
|
+
if (colErr || !col) {
|
|
62
|
+
return {
|
|
63
|
+
content: [
|
|
64
|
+
{ type: "text", text: `collection "${collection_slug}" not found.` },
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// update the shout's collection_id
|
|
69
|
+
const { error } = await supabase
|
|
70
|
+
.from("shouts")
|
|
71
|
+
.update({ collection_id: col.id })
|
|
72
|
+
.eq("id", shout_id)
|
|
73
|
+
.eq("user_id", user_id);
|
|
74
|
+
if (error) {
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{ type: "text", text: `error adding to collection: ${error.message}` },
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{ type: "text", text: `shout ${shout_id} added to "${col.name}" (${collection_slug}).` },
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
// shout_remove_from_collection
|
|
88
|
+
server.tool("shout_remove_from_collection", "remove a shout from its collection (sets collection_id to null).", {
|
|
89
|
+
shout_id: z.string().uuid().describe("the shout id"),
|
|
90
|
+
user_id: z.string().uuid().describe("the user's id"),
|
|
91
|
+
}, async ({ shout_id, user_id }) => {
|
|
92
|
+
const { error } = await supabase
|
|
93
|
+
.from("shouts")
|
|
94
|
+
.update({ collection_id: null })
|
|
95
|
+
.eq("id", shout_id)
|
|
96
|
+
.eq("user_id", user_id);
|
|
97
|
+
if (error) {
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{ type: "text", text: `error removing from collection: ${error.message}` },
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{ type: "text", text: `shout ${shout_id} removed from its collection.` },
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
// shout_list_collections
|
|
111
|
+
server.tool("shout_list_collections", "list all your collections.", {
|
|
112
|
+
user_id: z.string().uuid().describe("the user's id"),
|
|
113
|
+
}, async ({ user_id }) => {
|
|
114
|
+
const { data, error } = await supabase
|
|
115
|
+
.from("collections")
|
|
116
|
+
.select("*")
|
|
117
|
+
.eq("user_id", user_id)
|
|
118
|
+
.order("created_at", { ascending: false });
|
|
119
|
+
if (error) {
|
|
120
|
+
return {
|
|
121
|
+
content: [
|
|
122
|
+
{ type: "text", text: `error listing collections: ${error.message}` },
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (!data || data.length === 0) {
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: "text", text: "no collections yet." }],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const lines = data.map((c, i) => `${i + 1}. ${c.name} (/${c.slug}) - ${c.description || "no description"} [${c.visibility}]`);
|
|
132
|
+
return {
|
|
133
|
+
content: [
|
|
134
|
+
{
|
|
135
|
+
type: "text",
|
|
136
|
+
text: `${data.length} collections:\n\n${lines.join("\n")}`,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=collections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.js","sourceRoot":"","sources":["../../src/tools/collections.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intros.d.ts","sourceRoot":"","sources":["../../src/tools/intros.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,QA0jBnD"}
|