rss-agent-discovery 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 ADDED
@@ -0,0 +1,258 @@
1
+ # RSS Agent Discovery
2
+
3
+ AI agent-focused RSS feed discovery tool. JSON-only output to stdout, errors to stderr.
4
+
5
+ ## Why This Tool?
6
+
7
+ Existing RSS discovery tools (`rss-url-finder`, `rss-finder`) were built for human developers. They output human-readable text and don't validate that feeds actually exist.
8
+
9
+ This tool is designed for **AI agents** (Claude, Cursor, GPT):
10
+ - JSON-only output to stdout (machine-parseable)
11
+ - Errors to stderr (separated channel)
12
+ - Semantic exit codes (0=found, 1=none, 2=error)
13
+ - Fast (10s default timeout, parallel scanning)
14
+ - Discovery-only (returns feed URLs, doesn't parse content)
15
+ - Finds feeds AI agents miss
16
+
17
+ **Proof:** Cursor fails to find `https://vercel.com/atom`. This tool succeeds.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install
23
+ npm run build
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### Basic usage:
29
+ ```bash
30
+ npm start https://vercel.com
31
+ ```
32
+
33
+ Or via CLI (after `npm link`):
34
+ ```bash
35
+ rss-discover https://vercel.com
36
+ ```
37
+
38
+ ### Multiple URLs (parallel processing):
39
+ ```bash
40
+ npm start https://vercel.com https://news.ycombinator.com https://stripe.com
41
+ ```
42
+
43
+ ### Parse with jq:
44
+ ```bash
45
+ npm start https://vercel.com | jq '.results[0].feeds'
46
+ ```
47
+
48
+ ### Custom timeout:
49
+ ```bash
50
+ npm start --timeout 15000 https://example.com
51
+ ```
52
+
53
+ ### Skip blog scanning:
54
+ ```bash
55
+ npm start --skip-blogs https://example.com
56
+ ```
57
+
58
+ ### Show help:
59
+ ```bash
60
+ npm start --help
61
+ ```
62
+
63
+ ### Run tests:
64
+ ```bash
65
+ npm test
66
+ ```
67
+
68
+ ## Output Schema
69
+
70
+ ```json
71
+ {
72
+ "success": true,
73
+ "results": [
74
+ {
75
+ "url": "https://vercel.com",
76
+ "feeds": [
77
+ {
78
+ "url": "https://vercel.com/atom",
79
+ "title": "atom",
80
+ "type": "atom"
81
+ }
82
+ ],
83
+ "error": null
84
+ }
85
+ ]
86
+ }
87
+ ```
88
+
89
+ ## Exit Codes
90
+
91
+ - `0` - One or more feeds found
92
+ - `1` - No feeds found
93
+ - `2` - Error occurred
94
+
95
+ ## Features
96
+
97
+ - Discovers RSS/Atom feeds from HTML `<link>` tags
98
+ - Tests common paths (`/rss.xml`, `/atom`, `/feed`, etc.)
99
+ - Scans blog subdirectories (`/blog`, `/news`, `/articles`)
100
+ - Parallel processing for multiple URLs
101
+ - Deduplicates feeds across all sources
102
+ - Validates feeds actually exist and return XML
103
+ - JSON-only output to stdout
104
+ - Errors to stderr
105
+ - 10s default timeout per URL (configurable)
106
+
107
+ ## Examples
108
+
109
+ ### Find feeds for Vercel:
110
+ ```bash
111
+ npm start https://vercel.com
112
+ ```
113
+
114
+ Output:
115
+ ```json
116
+ {
117
+ "success": true,
118
+ "results": [
119
+ {
120
+ "url": "https://vercel.com",
121
+ "feeds": [
122
+ {"url": "https://vercel.com/atom", "title": "atom", "type": "atom"}
123
+ ],
124
+ "error": null
125
+ }
126
+ ]
127
+ }
128
+ ```
129
+
130
+ ### Check exit code:
131
+ ```bash
132
+ npm start https://vercel.com 2>/dev/null
133
+ echo $? # Outputs: 0
134
+ ```
135
+
136
+ ### No feeds found:
137
+ ```bash
138
+ npm start https://example.com 2>/dev/null
139
+ echo $? # Outputs: 1
140
+ ```
141
+
142
+ ### Parallel scan:
143
+ ```bash
144
+ npm start https://vercel.com https://news.ycombinator.com | jq
145
+ ```
146
+
147
+ ## Integration Example
148
+
149
+ ### Claude AI:
150
+ ```python
151
+ import subprocess
152
+ import json
153
+
154
+ result = subprocess.run(
155
+ ['node', 'dist/find-rss-feeds.js', 'https://vercel.com'],
156
+ capture_output=True,
157
+ text=True
158
+ )
159
+
160
+ data = json.loads(result.stdout)
161
+ feeds = data['results'][0]['feeds']
162
+ exit_code = result.returncode
163
+ ```
164
+
165
+ ### Shell script:
166
+ ```bash
167
+ #!/bin/bash
168
+ result=$(node dist/find-rss-feeds.js "$1" 2>/dev/null)
169
+ exit_code=$?
170
+
171
+ if [ $exit_code -eq 0 ]; then
172
+ echo "Found feeds:"
173
+ echo "$result" | jq '.results[0].feeds'
174
+ fi
175
+ ```
176
+
177
+ ## AI Agent Integration
178
+
179
+ This tool is designed to work seamlessly with AI coding agents:
180
+
181
+ ### Opencode / Claude Code
182
+ ```bash
183
+ # Opencode can call this tool directly
184
+ npx rss-agent-discovery https://example.com | jq '.results[0].feeds[].url'
185
+ ```
186
+
187
+ ### Cursor
188
+ Cursor can integrate this as a custom tool:
189
+ ```json
190
+ {
191
+ "name": "rss_discovery",
192
+ "command": "npx rss-agent-discovery {url}",
193
+ "description": "Discover RSS feeds from a website"
194
+ }
195
+ ```
196
+
197
+ ### GitHub Copilot
198
+ ```javascript
199
+ // Use in GitHub Actions or workflows
200
+ const { execSync } = require('child_process');
201
+ const result = JSON.parse(
202
+ execSync('npx rss-agent-discovery https://github.com/blog').toString()
203
+ );
204
+ ```
205
+
206
+ ### Custom MCP Server
207
+ ```typescript
208
+ // Build a Model Context Protocol server
209
+ import { spawn } from 'child_process';
210
+
211
+ async function discoverRSS(url: string) {
212
+ const proc = spawn('npx', ['rss-agent-discovery', url]);
213
+ const chunks: Buffer[] = [];
214
+
215
+ for await (const chunk of proc.stdout) {
216
+ chunks.push(chunk);
217
+ }
218
+
219
+ return JSON.parse(Buffer.concat(chunks).toString());
220
+ }
221
+ ```
222
+
223
+ ### Why AI Agents Need This Tool
224
+
225
+ AI agents (Claude, Cursor, GPT-4) struggle with RSS discovery because:
226
+ - They rely on web search which may miss feeds
227
+ - They don't systematically parse HTML `<link>` tags
228
+ - They give up after trying 2-3 common paths
229
+ - They can't validate feeds actually return XML
230
+
231
+ This tool solves all of those problems with structured JSON output.
232
+
233
+ ## Development
234
+
235
+ ### Build:
236
+ ```bash
237
+ npm run build
238
+ ```
239
+
240
+ ### Test:
241
+ ```bash
242
+ npm test
243
+ ```
244
+
245
+ ### Lint:
246
+ ```bash
247
+ tsc --noEmit
248
+ ```
249
+
250
+ ## Project Structure
251
+
252
+ - `find-rss-feeds.ts` - TypeScript source
253
+ - `dist/` - Compiled JavaScript
254
+ - `package.json` - Dependencies and scripts
255
+
256
+ ## License
257
+
258
+ MIT
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=find-rss-feeds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-rss-feeds.d.ts","sourceRoot":"","sources":["../find-rss-feeds.ts"],"names":[],"mappings":""}
@@ -0,0 +1,345 @@
1
+ import * as cheerio from 'cheerio';
2
+ const cliOptions = {
3
+ help: false,
4
+ skipBlogs: false,
5
+ maxBlogs: 5,
6
+ customBlogPaths: null,
7
+ timeout: 10000
8
+ };
9
+ const BLOG_KEYWORDS = [
10
+ 'blog', 'news', 'articles', 'posts', 'updates',
11
+ 'journal', 'insights', 'stories', 'press', 'medium',
12
+ 'substack', 'the-edge', 'engineering-blog', 'dev',
13
+ 'engineering', 'developers', 'community'
14
+ ];
15
+ const COMMON_BLOG_PATHS = [
16
+ '/blog',
17
+ '/news',
18
+ '/articles',
19
+ '/posts',
20
+ '/updates',
21
+ '/journal',
22
+ '/insights',
23
+ '/stories',
24
+ '/press',
25
+ '/medium',
26
+ '/substack',
27
+ '/the-edge',
28
+ '/engineering-blog',
29
+ '/engineering',
30
+ '/developers',
31
+ '/dev',
32
+ '/community'
33
+ ];
34
+ function parseArgs(args) {
35
+ const urls = [];
36
+ for (let i = 0; i < args.length; i++) {
37
+ const arg = args[i];
38
+ if (arg === '--help' || arg === '-h') {
39
+ cliOptions.help = true;
40
+ }
41
+ else if (arg === '--no-blogs' || arg === '--skip-blogs') {
42
+ cliOptions.skipBlogs = true;
43
+ }
44
+ else if (arg === '--max-blogs') {
45
+ cliOptions.maxBlogs = parseInt(args[++i], 10);
46
+ }
47
+ else if (arg === '--blog-paths') {
48
+ cliOptions.customBlogPaths = args[++i].split(',').map(p => p.trim());
49
+ }
50
+ else if (arg === '--timeout') {
51
+ cliOptions.timeout = parseInt(args[++i], 10);
52
+ }
53
+ else if (arg.startsWith('http://') || arg.startsWith('https://')) {
54
+ urls.push(arg);
55
+ }
56
+ }
57
+ return urls;
58
+ }
59
+ function showHelp() {
60
+ console.log(`
61
+ Usage: node find-rss-feeds.js [options] <URL1> <URL2> ...
62
+
63
+ Discover RSS feeds from websites for AI agent consumption. JSON-only output.
64
+
65
+ Arguments:
66
+ URL(s) One or more website URLs to scan for RSS feeds
67
+
68
+ Options:
69
+ --no-blogs, --skip-blogs
70
+ Skip blog subdirectory scanning
71
+ --max-blogs <num> Maximum number of blog subdirectories to scan (default: 5)
72
+ --blog-paths <paths>
73
+ Comma-separated custom blog paths to try (e.g., '/blog,/news')
74
+ --timeout <ms> Timeout per URL in milliseconds (default: 10000)
75
+ --help, -h Show this help message
76
+
77
+ Exit codes:
78
+ 0 One or more feeds found
79
+ 1 No feeds found
80
+ 2 Error occurred
81
+
82
+ Examples:
83
+ node find-rss-feeds.js https://example.com
84
+ node find-rss-feeds.js https://site1.com https://site2.com
85
+ node find-rss-feeds.js --timeout 15000 https://example.com
86
+ node find-rss-feeds.js --max-blogs 3 https://example.com
87
+ node find-rss-feeds.js --blog-paths '/blog,/updates' https://example.com | jq
88
+
89
+ Output schema:
90
+ {
91
+ "success": true,
92
+ "results": [
93
+ {
94
+ "url": "https://example.com",
95
+ "feeds": [
96
+ {"url": "https://example.com/atom", "title": "Blog", "type": "atom"}
97
+ ],
98
+ "error": null
99
+ }
100
+ ]
101
+ }
102
+ `);
103
+ }
104
+ function extractBlogLinksFromHTML(html, baseUrl) {
105
+ const $ = cheerio.load(html);
106
+ const blogPaths = new Set();
107
+ const linkTextAndUrls = [];
108
+ $('a[href]').each((i, el) => {
109
+ const href = $(el).attr('href');
110
+ const text = $(el).text().toLowerCase().trim();
111
+ if (href) {
112
+ try {
113
+ const url = new URL(href, baseUrl);
114
+ if (url.origin === new URL(baseUrl).origin) {
115
+ linkTextAndUrls.push({
116
+ text: text,
117
+ path: url.pathname,
118
+ href: url.href
119
+ });
120
+ }
121
+ }
122
+ catch (e) {
123
+ }
124
+ }
125
+ });
126
+ const seenPaths = new Set();
127
+ for (const link of linkTextAndUrls) {
128
+ if (seenPaths.has(link.path))
129
+ continue;
130
+ const lowerText = link.text.toLowerCase();
131
+ const lowerPath = link.path.toLowerCase();
132
+ const foundKeyword = BLOG_KEYWORDS.find(keyword => lowerText.includes(keyword) ||
133
+ lowerPath.includes(keyword));
134
+ if (foundKeyword) {
135
+ if (link.path.startsWith('/') && link.path !== '/') {
136
+ const parts = link.path.split('/').filter(Boolean);
137
+ if (parts.length >= 1 && parts.length <= 3) {
138
+ const blogPath = '/' + parts[0];
139
+ if (!seenPaths.has(blogPath)) {
140
+ blogPaths.add(blogPath);
141
+ seenPaths.add(blogPath);
142
+ seenPaths.add(link.path);
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ return Array.from(blogPaths);
149
+ }
150
+ function discoverBlogSubdirectories(baseUrl, html = null) {
151
+ let blogPaths = [];
152
+ if (html) {
153
+ blogPaths = extractBlogLinksFromHTML(html, baseUrl);
154
+ }
155
+ if (blogPaths.length === 0 || cliOptions.customBlogPaths) {
156
+ const pathsToUse = cliOptions.customBlogPaths ? cliOptions.customBlogPaths : COMMON_BLOG_PATHS;
157
+ for (const path of pathsToUse) {
158
+ if (!blogPaths.includes(path)) {
159
+ blogPaths.push(path);
160
+ }
161
+ }
162
+ }
163
+ return blogPaths.slice(0, cliOptions.maxBlogs);
164
+ }
165
+ async function scanURLForFeeds(url) {
166
+ const discoveredFeeds = new Map();
167
+ try {
168
+ const res = await fetch(url, {
169
+ headers: { 'User-Agent': 'rss-agent-discovery/1.0' }
170
+ });
171
+ if (!res.ok) {
172
+ throw new Error(`Failed to fetch ${url}: ${res.status}`);
173
+ }
174
+ const html = await res.text();
175
+ const $ = cheerio.load(html);
176
+ $('link[rel="alternate"]').each((i, el) => {
177
+ const type = $(el).attr('type');
178
+ if (type && (type.includes('rss+xml') || type.includes('atom+xml'))) {
179
+ let href = $(el).attr('href');
180
+ const title = $(el).attr('title') || 'RSS Feed';
181
+ if (href) {
182
+ try {
183
+ const fullHref = new URL(href, url).href;
184
+ const feedType = type.includes('atom') ? 'atom' : 'rss';
185
+ if (!discoveredFeeds.has(fullHref)) {
186
+ discoveredFeeds.set(fullHref, { url: fullHref, title, type: feedType });
187
+ }
188
+ }
189
+ catch {
190
+ }
191
+ }
192
+ }
193
+ });
194
+ const commonPaths = [
195
+ 'rss.xml', 'feed.xml', 'rss', 'atom', 'atom.xml', 'index.xml',
196
+ 'feeds/rss.xml', 'feed', 'rss/feed.xml'
197
+ ];
198
+ for (const path of commonPaths) {
199
+ try {
200
+ const candidateUrl = new URL(path, url).href;
201
+ if (!discoveredFeeds.has(candidateUrl)) {
202
+ const type = path.includes('atom') ? 'atom' : 'rss';
203
+ discoveredFeeds.set(candidateUrl, {
204
+ url: candidateUrl,
205
+ title: candidateUrl.split('/').pop() || 'RSS Feed',
206
+ type
207
+ });
208
+ }
209
+ }
210
+ catch {
211
+ }
212
+ }
213
+ const validFeeds = [];
214
+ for (const [feedUrl, feed] of Array.from(discoveredFeeds)) {
215
+ try {
216
+ const res = await fetch(feedUrl, {
217
+ headers: { 'User-Agent': 'rss-agent-discovery/1.0' }
218
+ });
219
+ if (res.ok) {
220
+ const contentType = res.headers.get('content-type') || '';
221
+ const text = await res.text();
222
+ if ((text.includes('<rss') || text.includes('<feed')) &&
223
+ (contentType.includes('xml') || contentType.includes('rss'))) {
224
+ validFeeds.push(feed);
225
+ }
226
+ }
227
+ }
228
+ catch {
229
+ }
230
+ }
231
+ return { html, feeds: validFeeds };
232
+ }
233
+ catch (e) {
234
+ console.error(`Error scanning ${url}: ${e.message}`);
235
+ return { html: null, feeds: [] };
236
+ }
237
+ }
238
+ async function findRSSFeeds(baseUrl) {
239
+ const timeoutMs = cliOptions.timeout;
240
+ const timeoutPromise = new Promise((_, reject) => {
241
+ setTimeout(() => reject(new Error('Timeout')), timeoutMs);
242
+ });
243
+ try {
244
+ const scanResult = await Promise.race([
245
+ scanURLForFeeds(baseUrl),
246
+ timeoutPromise
247
+ ]);
248
+ const allFeeds = new Map();
249
+ scanResult.feeds.forEach(feed => {
250
+ if (!allFeeds.has(feed.url)) {
251
+ allFeeds.set(feed.url, feed);
252
+ }
253
+ });
254
+ if (!cliOptions.skipBlogs && scanResult.html) {
255
+ const blogPaths = discoverBlogSubdirectories(baseUrl, scanResult.html);
256
+ if (blogPaths.length > 0) {
257
+ const blogURLs = blogPaths.map(path => {
258
+ try {
259
+ return new URL(path, baseUrl).href;
260
+ }
261
+ catch {
262
+ return null;
263
+ }
264
+ }).filter((url) => url !== null);
265
+ const blogResults = await Promise.allSettled(blogURLs.map(blogUrl => Promise.race([
266
+ scanURLForFeeds(blogUrl),
267
+ timeoutPromise
268
+ ])));
269
+ blogResults.forEach((result) => {
270
+ if (result.status === 'fulfilled') {
271
+ result.value.feeds.forEach((feed) => {
272
+ if (!allFeeds.has(feed.url)) {
273
+ allFeeds.set(feed.url, feed);
274
+ }
275
+ });
276
+ }
277
+ });
278
+ }
279
+ }
280
+ return {
281
+ url: baseUrl,
282
+ feeds: Array.from(allFeeds.values()),
283
+ error: null
284
+ };
285
+ }
286
+ catch (e) {
287
+ const errorMessage = e.message;
288
+ console.error(`Error processing ${baseUrl}: ${errorMessage}`);
289
+ return {
290
+ url: baseUrl,
291
+ feeds: [],
292
+ error: errorMessage
293
+ };
294
+ }
295
+ }
296
+ async function main() {
297
+ const args = process.argv.slice(2);
298
+ const urls = parseArgs(args);
299
+ if (cliOptions.help) {
300
+ showHelp();
301
+ return 2;
302
+ }
303
+ if (urls.length === 0) {
304
+ console.error('Error: No URLs provided. Use --help for usage information.');
305
+ return 2;
306
+ }
307
+ const results = [];
308
+ const scanResults = await Promise.all(urls.map(url => findRSSFeeds(url)));
309
+ let totalFeedsFound = 0;
310
+ let hasError = false;
311
+ for (const result of scanResults) {
312
+ results.push(result);
313
+ if (result.error) {
314
+ hasError = true;
315
+ }
316
+ else {
317
+ totalFeedsFound += result.feeds.length;
318
+ }
319
+ }
320
+ const output = {
321
+ success: !hasError,
322
+ results: results
323
+ };
324
+ console.log(JSON.stringify(output, null, 2));
325
+ if (hasError) {
326
+ return 2;
327
+ }
328
+ else if (totalFeedsFound === 0) {
329
+ return 1;
330
+ }
331
+ else {
332
+ return 0;
333
+ }
334
+ }
335
+ main().then(exitCode => {
336
+ process.exit(exitCode);
337
+ }).catch(e => {
338
+ console.error(JSON.stringify({
339
+ success: false,
340
+ error: e.message,
341
+ results: []
342
+ }, null, 2));
343
+ process.exit(2);
344
+ });
345
+ //# sourceMappingURL=find-rss-feeds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-rss-feeds.js","sourceRoot":"","sources":["../find-rss-feeds.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "rss-agent-discovery",
3
+ "version": "0.1.0",
4
+ "description": "AI agent-focused RSS feed discovery. JSON output, semantic exit codes.",
5
+ "main": "dist/find-rss-feeds.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "rss-agent-discovery": "dist/find-rss-feeds.js",
9
+ "rss-discover": "dist/find-rss-feeds.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/find-rss-feeds.js",
17
+ "dev": "tsc && node dist/find-rss-feeds.js",
18
+ "test": "npm run build && node dist/find-rss-feeds.js https://vercel.com https://news.ycombinator.com | jq",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "rss",
23
+ "atom",
24
+ "feed",
25
+ "discovery",
26
+ "ai",
27
+ "agent",
28
+ "cli",
29
+ "json"
30
+ ],
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "cheerio": "^1.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/cheerio": "^0.22.35",
37
+ "@types/node": "^25.0.10",
38
+ "typescript": "^5.9.3"
39
+ }
40
+ }