50c 2.6.0 → 2.8.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/lib/index.js +26 -2
- package/lib/packs/grabr.js +443 -0
- package/lib/packs/prompt_engine.js +365 -0
- package/lib/packs.js +21 -1
- package/package.json +7 -7
package/lib/index.js
CHANGED
|
@@ -22,12 +22,16 @@ const whm = require('./packs/whm');
|
|
|
22
22
|
const cf = require('./packs/cf');
|
|
23
23
|
const wp = require('./packs/wp');
|
|
24
24
|
const ux = require('./packs/ux');
|
|
25
|
+
const promptEngine = require('./packs/prompt_engine');
|
|
26
|
+
const grabr = require('./packs/grabr');
|
|
25
27
|
|
|
26
28
|
// Tool name mappings by pack
|
|
27
29
|
const TOOL_PACKS = {
|
|
28
30
|
beacon: ['hints', 'hints_plus', 'roast', 'quick_vibe', 'one_liner', 'name_it', 'price_it', 'compute', 'ide_conversation', 'learning_stats'],
|
|
29
31
|
labs: ['genius', 'mind_opener', 'idea_fold', 'agent_autopsy', 'prompt_fortress', 'context_health', 'context_compress', 'context_extract', 'context_reposition'],
|
|
30
|
-
labs_plus: ['bcalc', 'genius_plus', 'bcalc_why', 'discovery_collision', 'cvi_loop', 'cvi_verify', 'chaos_fingerprint', 'resonance', 'prime_residue', 'echo_sequence', 'conversation_diagnostic', 'handoff']
|
|
32
|
+
labs_plus: ['bcalc', 'genius_plus', 'bcalc_why', 'discovery_collision', 'cvi_loop', 'cvi_verify', 'chaos_fingerprint', 'resonance', 'prime_residue', 'echo_sequence', 'conversation_diagnostic', 'handoff'],
|
|
33
|
+
prompt_engine: ['prompt_extract', 'prompt_phases', 'prompt_refine', 'prompt_expand', 'prompt_categorize'],
|
|
34
|
+
grabr: ['grabr_scrape', 'grabr_contact', 'grabr_wayback', 'grabr_sitemap', 'grabr_batch', 'grabr_intel']
|
|
31
35
|
};
|
|
32
36
|
|
|
33
37
|
// Get all available tools based on enabled packs
|
|
@@ -53,6 +57,8 @@ async function getTools() {
|
|
|
53
57
|
if (config.packs.cf) tools.push(...cf.CF_TOOLS);
|
|
54
58
|
if (config.packs.wp) tools.push(...wp.WP_TOOLS);
|
|
55
59
|
if (config.packs.ux) tools.push(...ux.UX_TOOLS);
|
|
60
|
+
if (config.packs.prompt_engine) tools.push(...promptEngine.PROMPT_ENGINE_TOOLS);
|
|
61
|
+
if (config.packs.grabr) tools.push(...grabr.GRABR_TOOLS);
|
|
56
62
|
|
|
57
63
|
// ENTERPRISE tier
|
|
58
64
|
if (config.packs.labs_plus) tools.push(...labsPlus.LABS_PLUS_TOOLS);
|
|
@@ -101,6 +107,22 @@ async function handleTool(name, args = {}) {
|
|
|
101
107
|
return labs.handleTool(name, args);
|
|
102
108
|
}
|
|
103
109
|
|
|
110
|
+
// Prompt Engine tools (PRO)
|
|
111
|
+
if (TOOL_PACKS.prompt_engine.includes(name)) {
|
|
112
|
+
if (!config.packs.prompt_engine) {
|
|
113
|
+
return { error: 'Requires Pro tier ($99/mo). Enable prompt_engine pack or upgrade at sales.50c.ai/50c-pro/' };
|
|
114
|
+
}
|
|
115
|
+
return promptEngine.handleTool(name, args);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Grabr tools (PRO)
|
|
119
|
+
if (TOOL_PACKS.grabr.includes(name) || name.startsWith('grabr_')) {
|
|
120
|
+
if (!config.packs.grabr) {
|
|
121
|
+
return { error: 'Requires Pro tier ($99/mo). Enable grabr pack or upgrade at sales.50c.ai/50c-pro/' };
|
|
122
|
+
}
|
|
123
|
+
return grabr.handleTool(name, args);
|
|
124
|
+
}
|
|
125
|
+
|
|
104
126
|
// Labs+ tools (ENTERPRISE)
|
|
105
127
|
if (TOOL_PACKS.labs_plus.includes(name)) {
|
|
106
128
|
if (!config.packs.labs_plus) {
|
|
@@ -171,5 +193,7 @@ module.exports = {
|
|
|
171
193
|
packs,
|
|
172
194
|
beacon,
|
|
173
195
|
labs,
|
|
174
|
-
labsPlus
|
|
196
|
+
labsPlus,
|
|
197
|
+
promptEngine,
|
|
198
|
+
grabr
|
|
175
199
|
};
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 50c Grabr Pack - PRO Tier
|
|
3
|
+
* Web scraping + contact extraction + Wayback Machine
|
|
4
|
+
* Designed for: Lead gen, OSINT, competitive intel, domain recovery
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { apiRequest } = require('../config');
|
|
8
|
+
|
|
9
|
+
// URL validation regex
|
|
10
|
+
const URL_REGEX = /^https?:\/\/[^\s/$.?#].[^\s]*$/i;
|
|
11
|
+
const DOMAIN_REGEX = /^[a-zA-Z0-9][-a-zA-Z0-9]*(\.[a-zA-Z0-9][-a-zA-Z0-9]*)+$/;
|
|
12
|
+
|
|
13
|
+
// Contact extraction patterns (from Grabr)
|
|
14
|
+
const EMAIL_REGEX = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
|
|
15
|
+
const PHONE_PATTERNS = [
|
|
16
|
+
/\+1[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
17
|
+
/\b1[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
18
|
+
/\b\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const SOCIAL_PATTERNS = {
|
|
22
|
+
twitter: /https?:\/\/(www\.)?(twitter\.com|x\.com)\/[a-zA-Z0-9_]+/gi,
|
|
23
|
+
facebook: /https?:\/\/(www\.)?facebook\.com\/[a-zA-Z0-9.]+/gi,
|
|
24
|
+
linkedin: /https?:\/\/(www\.)?linkedin\.com\/(in|company)\/[a-zA-Z0-9-]+/gi,
|
|
25
|
+
instagram: /https?:\/\/(www\.)?instagram\.com\/[a-zA-Z0-9_.]+/gi,
|
|
26
|
+
youtube: /https?:\/\/(www\.)?youtube\.com\/(c|channel|user)\/[a-zA-Z0-9_-]+/gi,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Invalid email patterns to filter
|
|
30
|
+
const INVALID_EMAIL_PATTERNS = [
|
|
31
|
+
/\.(png|jpg|jpeg|gif|svg|webp)$/i,
|
|
32
|
+
/example\.com/i,
|
|
33
|
+
/test\.com/i,
|
|
34
|
+
/sentry\.io/i,
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function isValidEmail(email) {
|
|
38
|
+
const lower = email.toLowerCase();
|
|
39
|
+
for (const pattern of INVALID_EMAIL_PATTERNS) {
|
|
40
|
+
if (pattern.test(lower)) return false;
|
|
41
|
+
}
|
|
42
|
+
const parts = email.split('@');
|
|
43
|
+
if (parts.length !== 2) return false;
|
|
44
|
+
const [local, domain] = parts;
|
|
45
|
+
if (!local || !domain || !domain.includes('.')) return false;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function extractEmails(text) {
|
|
50
|
+
const matches = text.match(EMAIL_REGEX) || [];
|
|
51
|
+
return [...new Set(matches.filter(isValidEmail).map(e => e.toLowerCase()))];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function extractPhones(text) {
|
|
55
|
+
const phones = new Set();
|
|
56
|
+
for (const pattern of PHONE_PATTERNS) {
|
|
57
|
+
const matches = text.matchAll(pattern);
|
|
58
|
+
for (const match of matches) {
|
|
59
|
+
let digits = match[0].replace(/\D/g, '');
|
|
60
|
+
if (digits.length === 11 && digits[0] === '1') digits = digits.slice(1);
|
|
61
|
+
if (digits.length === 10) {
|
|
62
|
+
// Validate area code
|
|
63
|
+
if (digits[0] === '0' || digits[0] === '1') continue;
|
|
64
|
+
if (digits[3] === '0' || digits[3] === '1') continue;
|
|
65
|
+
if (/^(\d)\1+$/.test(digits)) continue; // All same digits
|
|
66
|
+
const formatted = `+1 (${digits.slice(0,3)}) ${digits.slice(3,6)}-${digits.slice(6)}`;
|
|
67
|
+
phones.add(formatted);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return [...phones];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function extractSocialLinks(text) {
|
|
75
|
+
const social = {};
|
|
76
|
+
for (const [platform, pattern] of Object.entries(SOCIAL_PATTERNS)) {
|
|
77
|
+
const matches = text.match(pattern);
|
|
78
|
+
if (matches && matches.length > 0) {
|
|
79
|
+
// Filter out share/intent links
|
|
80
|
+
const valid = matches.filter(url =>
|
|
81
|
+
!url.includes('/share') &&
|
|
82
|
+
!url.includes('/intent') &&
|
|
83
|
+
!url.includes('/sharer')
|
|
84
|
+
);
|
|
85
|
+
if (valid.length > 0) social[platform] = valid[0];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return social;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function extractAddress(text) {
|
|
92
|
+
const addressRegex = /(\d+\s+[A-Za-z\s]+(?:Street|St|Avenue|Ave|Road|Rd|Boulevard|Blvd|Lane|Ln|Drive|Dr|Court|Ct|Way|Place|Pl)\.?),?\s*([A-Za-z\s]+),?\s*([A-Z]{2})\s*(\d{5})/gi;
|
|
93
|
+
const match = addressRegex.exec(text);
|
|
94
|
+
if (match) {
|
|
95
|
+
return {
|
|
96
|
+
street: match[1].trim(),
|
|
97
|
+
city: match[2].trim(),
|
|
98
|
+
state: match[3],
|
|
99
|
+
zip: match[4]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Tool implementations
|
|
106
|
+
async function grabrScrape(url, depth = 1) {
|
|
107
|
+
if (!URL_REGEX.test(url)) {
|
|
108
|
+
return { error: 'Invalid URL format' };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
// Use 50c page_fetch via API
|
|
113
|
+
const result = await apiRequest('page_fetch', { url });
|
|
114
|
+
if (result.error) return { error: result.error };
|
|
115
|
+
|
|
116
|
+
const html = result.content || result.text || '';
|
|
117
|
+
|
|
118
|
+
const contacts = {
|
|
119
|
+
emails: extractEmails(html),
|
|
120
|
+
phones: extractPhones(html),
|
|
121
|
+
address: extractAddress(html),
|
|
122
|
+
social: extractSocialLinks(html)
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Deep scrape - follow contact/about pages
|
|
126
|
+
if (depth >= 2 && contacts.emails.length === 0) {
|
|
127
|
+
const contactLinks = html.match(/href=["']([^"']*(?:contact|about)[^"']*)["']/gi) || [];
|
|
128
|
+
for (const linkMatch of contactLinks.slice(0, 2)) {
|
|
129
|
+
const href = linkMatch.match(/href=["']([^"']+)["']/)?.[1];
|
|
130
|
+
if (href) {
|
|
131
|
+
try {
|
|
132
|
+
const fullUrl = href.startsWith('http') ? href : new URL(href, url).href;
|
|
133
|
+
const subResult = await apiRequest('page_fetch', { url: fullUrl });
|
|
134
|
+
if (subResult.content) {
|
|
135
|
+
contacts.emails.push(...extractEmails(subResult.content));
|
|
136
|
+
contacts.phones.push(...extractPhones(subResult.content));
|
|
137
|
+
if (!contacts.address) contacts.address = extractAddress(subResult.content);
|
|
138
|
+
Object.assign(contacts.social, extractSocialLinks(subResult.content));
|
|
139
|
+
}
|
|
140
|
+
} catch (e) { /* skip broken links */ }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Dedupe
|
|
144
|
+
contacts.emails = [...new Set(contacts.emails)];
|
|
145
|
+
contacts.phones = [...new Set(contacts.phones)];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
success: true,
|
|
150
|
+
url,
|
|
151
|
+
depth,
|
|
152
|
+
contacts,
|
|
153
|
+
meta: {
|
|
154
|
+
title: (html.match(/<title>([^<]+)<\/title>/i) || [])[1] || null,
|
|
155
|
+
description: (html.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["']/i) || [])[1] || null
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return { error: e.message || 'Scrape failed' };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function grabrContact(content) {
|
|
164
|
+
if (!content || typeof content !== 'string') {
|
|
165
|
+
return { error: 'Content required' };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
emails: extractEmails(content),
|
|
171
|
+
phones: extractPhones(content),
|
|
172
|
+
address: extractAddress(content),
|
|
173
|
+
social: extractSocialLinks(content)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function grabrWayback(url, years = 5) {
|
|
178
|
+
if (!URL_REGEX.test(url)) {
|
|
179
|
+
return { error: 'Invalid URL format' };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const snapshots = [];
|
|
184
|
+
const currentYear = new Date().getFullYear();
|
|
185
|
+
const startYear = currentYear - years;
|
|
186
|
+
const months = [1, 6]; // Check Jan and June
|
|
187
|
+
|
|
188
|
+
for (let year = currentYear; year >= startYear && snapshots.length < 10; year--) {
|
|
189
|
+
for (const month of months) {
|
|
190
|
+
if (snapshots.length >= 10) break;
|
|
191
|
+
const timestamp = `${year}${String(month).padStart(2, '0')}01`;
|
|
192
|
+
const checkUrl = `https://archive.org/wayback/available?url=${encodeURIComponent(url)}×tamp=${timestamp}`;
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const resp = await fetch(checkUrl, {
|
|
196
|
+
headers: { 'User-Agent': '50c-grabr/1.0' },
|
|
197
|
+
signal: AbortSignal.timeout(10000)
|
|
198
|
+
});
|
|
199
|
+
if (resp.ok) {
|
|
200
|
+
const data = await resp.json();
|
|
201
|
+
if (data.archived_snapshots?.closest?.available) {
|
|
202
|
+
const snap = data.archived_snapshots.closest;
|
|
203
|
+
if (!snapshots.find(s => s.timestamp === snap.timestamp)) {
|
|
204
|
+
snapshots.push({
|
|
205
|
+
timestamp: snap.timestamp,
|
|
206
|
+
url: snap.url,
|
|
207
|
+
date: `${snap.timestamp.slice(0,4)}-${snap.timestamp.slice(4,6)}-${snap.timestamp.slice(6,8)}`
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch (e) { /* skip failed checks */ }
|
|
213
|
+
|
|
214
|
+
// Rate limit
|
|
215
|
+
await new Promise(r => setTimeout(r, 500));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
success: true,
|
|
221
|
+
url,
|
|
222
|
+
snapshots,
|
|
223
|
+
oldest: snapshots[snapshots.length - 1]?.date || null,
|
|
224
|
+
newest: snapshots[0]?.date || null
|
|
225
|
+
};
|
|
226
|
+
} catch (e) {
|
|
227
|
+
return { error: e.message || 'Wayback lookup failed' };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function grabrSitemap(url) {
|
|
232
|
+
// Normalize to sitemap URL
|
|
233
|
+
let sitemapUrl = url;
|
|
234
|
+
if (!url.includes('sitemap')) {
|
|
235
|
+
const base = url.replace(/\/$/, '');
|
|
236
|
+
sitemapUrl = `${base}/sitemap.xml`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const result = await apiRequest('page_fetch', { url: sitemapUrl });
|
|
241
|
+
if (result.error) {
|
|
242
|
+
// Try robots.txt fallback
|
|
243
|
+
const robotsUrl = url.replace(/\/$/, '') + '/robots.txt';
|
|
244
|
+
const robotsResult = await apiRequest('page_fetch', { url: robotsUrl });
|
|
245
|
+
if (robotsResult.content) {
|
|
246
|
+
const sitemapMatch = robotsResult.content.match(/Sitemap:\s*(\S+)/i);
|
|
247
|
+
if (sitemapMatch) {
|
|
248
|
+
const altResult = await apiRequest('page_fetch', { url: sitemapMatch[1] });
|
|
249
|
+
if (altResult.content) {
|
|
250
|
+
result.content = altResult.content;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (!result.content) {
|
|
257
|
+
return { error: 'Sitemap not found' };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Parse sitemap XML
|
|
261
|
+
const urls = [];
|
|
262
|
+
const locMatches = result.content.matchAll(/<loc>([^<]+)<\/loc>/gi);
|
|
263
|
+
for (const match of locMatches) {
|
|
264
|
+
urls.push(match[1]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
success: true,
|
|
269
|
+
sitemapUrl,
|
|
270
|
+
urls: urls.slice(0, 500), // Cap at 500
|
|
271
|
+
total: urls.length
|
|
272
|
+
};
|
|
273
|
+
} catch (e) {
|
|
274
|
+
return { error: e.message || 'Sitemap parse failed' };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function grabrBatch(urls, delayMs = 1000) {
|
|
279
|
+
if (!Array.isArray(urls) || urls.length === 0) {
|
|
280
|
+
return { error: 'URLs array required' };
|
|
281
|
+
}
|
|
282
|
+
if (urls.length > 10) {
|
|
283
|
+
return { error: 'Maximum 10 URLs per batch' };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const results = [];
|
|
287
|
+
for (const url of urls) {
|
|
288
|
+
const result = await grabrScrape(url, 1);
|
|
289
|
+
results.push({ url, ...result });
|
|
290
|
+
|
|
291
|
+
// Rate limit between requests
|
|
292
|
+
if (delayMs > 0) {
|
|
293
|
+
await new Promise(r => setTimeout(r, Math.max(delayMs, 500)));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
success: true,
|
|
299
|
+
processed: results.length,
|
|
300
|
+
results
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function grabrIntel(domain) {
|
|
305
|
+
if (!DOMAIN_REGEX.test(domain)) {
|
|
306
|
+
return { error: 'Invalid domain format' };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const url = `https://${domain}`;
|
|
310
|
+
|
|
311
|
+
// Parallel fetch main info
|
|
312
|
+
const [scrapeResult, sitemapResult, waybackResult] = await Promise.all([
|
|
313
|
+
grabrScrape(url, 2),
|
|
314
|
+
grabrSitemap(url).catch(() => ({ urls: [], total: 0 })),
|
|
315
|
+
grabrWayback(url, 3).catch(() => ({ snapshots: [] }))
|
|
316
|
+
]);
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
success: true,
|
|
320
|
+
domain,
|
|
321
|
+
contacts: scrapeResult.contacts || {},
|
|
322
|
+
meta: scrapeResult.meta || {},
|
|
323
|
+
pages: {
|
|
324
|
+
total: sitemapResult.total || 0,
|
|
325
|
+
sample: (sitemapResult.urls || []).slice(0, 10)
|
|
326
|
+
},
|
|
327
|
+
history: {
|
|
328
|
+
snapshots: (waybackResult.snapshots || []).length,
|
|
329
|
+
oldest: waybackResult.oldest,
|
|
330
|
+
newest: waybackResult.newest
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Tool definitions for MCP
|
|
336
|
+
const GRABR_TOOLS = [
|
|
337
|
+
{
|
|
338
|
+
name: 'grabr_scrape',
|
|
339
|
+
description: 'Deep scrape URL for contacts (email, phone, address) + social links. $0.05',
|
|
340
|
+
inputSchema: {
|
|
341
|
+
type: 'object',
|
|
342
|
+
properties: {
|
|
343
|
+
url: { type: 'string', description: 'URL to scrape' },
|
|
344
|
+
depth: { type: 'number', description: 'Scrape depth 1-3 (default 1)', default: 1 }
|
|
345
|
+
},
|
|
346
|
+
required: ['url']
|
|
347
|
+
},
|
|
348
|
+
cost: 0.05,
|
|
349
|
+
tier: 'pro'
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: 'grabr_contact',
|
|
353
|
+
description: 'Extract contacts from HTML/text content. $0.02',
|
|
354
|
+
inputSchema: {
|
|
355
|
+
type: 'object',
|
|
356
|
+
properties: {
|
|
357
|
+
content: { type: 'string', description: 'HTML or text to extract from' }
|
|
358
|
+
},
|
|
359
|
+
required: ['content']
|
|
360
|
+
},
|
|
361
|
+
cost: 0.02,
|
|
362
|
+
tier: 'pro'
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: 'grabr_wayback',
|
|
366
|
+
description: 'Get Wayback Machine snapshots for URL. $0.02',
|
|
367
|
+
inputSchema: {
|
|
368
|
+
type: 'object',
|
|
369
|
+
properties: {
|
|
370
|
+
url: { type: 'string', description: 'URL to check' },
|
|
371
|
+
years: { type: 'number', description: 'Years to look back (default 5)', default: 5 }
|
|
372
|
+
},
|
|
373
|
+
required: ['url']
|
|
374
|
+
},
|
|
375
|
+
cost: 0.02,
|
|
376
|
+
tier: 'pro'
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: 'grabr_sitemap',
|
|
380
|
+
description: 'Parse sitemap.xml and return all page URLs. $0.02',
|
|
381
|
+
inputSchema: {
|
|
382
|
+
type: 'object',
|
|
383
|
+
properties: {
|
|
384
|
+
url: { type: 'string', description: 'Site URL or sitemap URL' }
|
|
385
|
+
},
|
|
386
|
+
required: ['url']
|
|
387
|
+
},
|
|
388
|
+
cost: 0.02,
|
|
389
|
+
tier: 'pro'
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: 'grabr_batch',
|
|
393
|
+
description: 'Scrape up to 10 URLs with rate limiting. $0.10',
|
|
394
|
+
inputSchema: {
|
|
395
|
+
type: 'object',
|
|
396
|
+
properties: {
|
|
397
|
+
urls: { type: 'array', items: { type: 'string' }, description: 'URLs to scrape (max 10)' },
|
|
398
|
+
delayMs: { type: 'number', description: 'Delay between requests in ms (min 500)', default: 1000 }
|
|
399
|
+
},
|
|
400
|
+
required: ['urls']
|
|
401
|
+
},
|
|
402
|
+
cost: 0.10,
|
|
403
|
+
tier: 'pro'
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: 'grabr_intel',
|
|
407
|
+
description: 'Full domain intel: contacts, pages, social, history. $0.08',
|
|
408
|
+
inputSchema: {
|
|
409
|
+
type: 'object',
|
|
410
|
+
properties: {
|
|
411
|
+
domain: { type: 'string', description: 'Domain name (e.g., example.com)' }
|
|
412
|
+
},
|
|
413
|
+
required: ['domain']
|
|
414
|
+
},
|
|
415
|
+
cost: 0.08,
|
|
416
|
+
tier: 'pro'
|
|
417
|
+
}
|
|
418
|
+
];
|
|
419
|
+
|
|
420
|
+
async function handleTool(name, args) {
|
|
421
|
+
try {
|
|
422
|
+
switch (name) {
|
|
423
|
+
case 'grabr_scrape':
|
|
424
|
+
return await grabrScrape(args.url, args.depth || 1);
|
|
425
|
+
case 'grabr_contact':
|
|
426
|
+
return await grabrContact(args.content);
|
|
427
|
+
case 'grabr_wayback':
|
|
428
|
+
return await grabrWayback(args.url, args.years || 5);
|
|
429
|
+
case 'grabr_sitemap':
|
|
430
|
+
return await grabrSitemap(args.url);
|
|
431
|
+
case 'grabr_batch':
|
|
432
|
+
return await grabrBatch(args.urls, args.delayMs || 1000);
|
|
433
|
+
case 'grabr_intel':
|
|
434
|
+
return await grabrIntel(args.domain);
|
|
435
|
+
default:
|
|
436
|
+
return { error: `Unknown grabr tool: ${name}` };
|
|
437
|
+
}
|
|
438
|
+
} catch (e) {
|
|
439
|
+
return { error: e.message || 'Tool execution failed' };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
module.exports = { GRABR_TOOLS, handleTool };
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 50c Prompt Engine Pack - PRO Tier
|
|
3
|
+
* Harvested from CrazyBuild: extractConceptDetails + 4-phase prompt generation
|
|
4
|
+
* Available at: 50c.ai/prompt-engine
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { apiRequest } = require('../config');
|
|
8
|
+
|
|
9
|
+
// Feature patterns for concept extraction
|
|
10
|
+
const FEATURE_PATTERNS = [
|
|
11
|
+
// Healthcare/Medical
|
|
12
|
+
/(?:patient\s+portal|medical\s+records|EHR|EMR|HIPAA|health\s+records|appointment\s+booking|telemedicine|virtual\s+consultation|prescription|diagnosis)/gi,
|
|
13
|
+
// Standard features
|
|
14
|
+
/(?:booking|appointment|scheduling|calendar|reservation)/gi,
|
|
15
|
+
/(?:payment|checkout|purchase|shopping\s+cart|billing|invoice)/gi,
|
|
16
|
+
/(?:user\s+account|profile|authentication|login|registration|sign\s+up)/gi,
|
|
17
|
+
/(?:search|filter|find|browse|lookup)/gi,
|
|
18
|
+
/(?:dashboard|analytics|reporting|metrics|insights)/gi,
|
|
19
|
+
/(?:messaging|chat|communication|contact|notifications)/gi,
|
|
20
|
+
/(?:upload|file|image|media|document|attachment)/gi,
|
|
21
|
+
/(?:review|rating|feedback|testimonial|comment)/gi,
|
|
22
|
+
/(?:blog|news|article|post|content)/gi,
|
|
23
|
+
/(?:gallery|portfolio|showcase|catalog)/gi,
|
|
24
|
+
// Business/professional
|
|
25
|
+
/(?:CRM|customer\s+management|lead\s+tracking|sales\s+pipeline)/gi,
|
|
26
|
+
/(?:inventory|stock|warehouse|catalog\s+management)/gi,
|
|
27
|
+
/(?:workflow|automation|process|task\s+management)/gi,
|
|
28
|
+
/(?:collaboration|team|sharing|permissions)/gi,
|
|
29
|
+
/(?:export|import|integration|API)/gi
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// Capability patterns
|
|
33
|
+
const CAPABILITY_PATTERNS = [
|
|
34
|
+
/(?:allows?\s+(?:users?|patients?|customers?|clients?)\s+to\s+)([^.]{10,80})/gi,
|
|
35
|
+
/(?:enables?\s+(?:users?|patients?|customers?|clients?)\s+to\s+)([^.]{10,80})/gi,
|
|
36
|
+
/(?:provides?\s+)([^.]{10,80})/gi,
|
|
37
|
+
/(?:includes?\s+)([^.]{10,80})/gi,
|
|
38
|
+
/(?:features?\s+)([^.]{10,80})/gi,
|
|
39
|
+
/(?:supports?\s+)([^.]{10,80})/gi,
|
|
40
|
+
/(?:manages?\s+)([^.]{10,80})/gi
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Domain-specific terms
|
|
44
|
+
const TERM_PATTERNS = [
|
|
45
|
+
/\b[A-Z]{2,}(?:\s+[A-Z]{2,})*\b/g,
|
|
46
|
+
/\b(?:HIPAA|GDPR|SOC2|PCI|ISO|WCAG)\b/gi,
|
|
47
|
+
/\b(?:telemedicine|telehealth|virtual\s+care|remote\s+consultation)\b/gi,
|
|
48
|
+
/\b(?:e-commerce|marketplace|B2B|B2C|SaaS)\b/gi,
|
|
49
|
+
/\b(?:real-time|live|instant|automated|AI-powered|ML-based)\b/gi
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// Audience patterns
|
|
53
|
+
const AUDIENCE_PATTERNS = [
|
|
54
|
+
/(?:for|targeting|serving|helping)\s+([a-z\s]+?)(?:\.|,|\s+who|\s+seeking)/i,
|
|
55
|
+
/(?:designed\s+for|built\s+for|created\s+for)\s+([a-z\s]+?)(?:\.|,|\s+who|\s+to)/i,
|
|
56
|
+
/(?:patients?|doctors?|physicians?|nurses?|healthcare\s+professionals?|clinicians?|medical\s+staff)/gi,
|
|
57
|
+
/(?:customers?|clients?|users?|members?|subscribers?|students?|teachers?)/gi
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
// App type categories
|
|
61
|
+
const APP_CATEGORIES = {
|
|
62
|
+
'saas': { keywords: ['subscription', 'platform', 'dashboard', 'analytics', 'team'], niche: 'B2B Software' },
|
|
63
|
+
'ecommerce': { keywords: ['shop', 'cart', 'payment', 'product', 'checkout'], niche: 'Online Retail' },
|
|
64
|
+
'marketplace': { keywords: ['vendor', 'seller', 'buyer', 'listing', 'auction'], niche: 'Two-Sided Platform' },
|
|
65
|
+
'healthcare': { keywords: ['patient', 'medical', 'health', 'clinic', 'doctor'], niche: 'HealthTech' },
|
|
66
|
+
'fintech': { keywords: ['payment', 'banking', 'investment', 'finance', 'trading'], niche: 'Financial Services' },
|
|
67
|
+
'edtech': { keywords: ['course', 'learning', 'student', 'teacher', 'education'], niche: 'Education' },
|
|
68
|
+
'social': { keywords: ['profile', 'feed', 'follow', 'share', 'community'], niche: 'Social Network' },
|
|
69
|
+
'productivity': { keywords: ['task', 'project', 'todo', 'workflow', 'calendar'], niche: 'Business Tools' },
|
|
70
|
+
'content': { keywords: ['blog', 'article', 'post', 'media', 'cms'], niche: 'Content Management' },
|
|
71
|
+
'booking': { keywords: ['appointment', 'reservation', 'schedule', 'booking', 'availability'], niche: 'Scheduling' }
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extract concept details from natural language description
|
|
76
|
+
* Core pattern harvested from CrazyBuild
|
|
77
|
+
*/
|
|
78
|
+
function extractConceptDetails(concept, projectTitle = '') {
|
|
79
|
+
// Extract business name
|
|
80
|
+
const businessMatch = concept.match(/([A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\s+(?:website|business|company|into|from|platform|app|system)/);
|
|
81
|
+
const businessName = businessMatch?.[1] || projectTitle || 'App';
|
|
82
|
+
|
|
83
|
+
// Extract features
|
|
84
|
+
const features = [];
|
|
85
|
+
FEATURE_PATTERNS.forEach(pattern => {
|
|
86
|
+
const matches = concept.match(pattern);
|
|
87
|
+
if (matches) {
|
|
88
|
+
matches.forEach(match => {
|
|
89
|
+
const normalized = match.toLowerCase().trim();
|
|
90
|
+
if (!features.includes(normalized)) features.push(normalized);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Extract capabilities
|
|
96
|
+
const capabilities = [];
|
|
97
|
+
CAPABILITY_PATTERNS.forEach(pattern => {
|
|
98
|
+
const matches = concept.matchAll(pattern);
|
|
99
|
+
for (const match of matches) {
|
|
100
|
+
const cap = match[1].trim();
|
|
101
|
+
if (cap.length >= 10 && cap.length <= 80 && !cap.includes('\n')) {
|
|
102
|
+
capabilities.push(cap);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Extract domain terms
|
|
108
|
+
const terms = [];
|
|
109
|
+
TERM_PATTERNS.forEach(pattern => {
|
|
110
|
+
const matches = concept.match(pattern);
|
|
111
|
+
if (matches) {
|
|
112
|
+
matches.forEach(match => {
|
|
113
|
+
const normalized = match.toUpperCase().trim();
|
|
114
|
+
if (!terms.includes(normalized)) terms.push(normalized);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Extract target audience
|
|
120
|
+
let audience = 'users';
|
|
121
|
+
for (const pattern of AUDIENCE_PATTERNS) {
|
|
122
|
+
const match = concept.match(pattern);
|
|
123
|
+
if (match && match[1] && match[1].trim().length > 3) {
|
|
124
|
+
audience = match[1].trim();
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Detect app type
|
|
130
|
+
const conceptLower = concept.toLowerCase();
|
|
131
|
+
let appType = 'general';
|
|
132
|
+
let appNiche = 'Web Application';
|
|
133
|
+
let maxScore = 0;
|
|
134
|
+
|
|
135
|
+
for (const [type, config] of Object.entries(APP_CATEGORIES)) {
|
|
136
|
+
const score = config.keywords.filter(kw => conceptLower.includes(kw)).length;
|
|
137
|
+
if (score > maxScore) {
|
|
138
|
+
maxScore = score;
|
|
139
|
+
appType = type;
|
|
140
|
+
appNiche = config.niche;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
businessName,
|
|
146
|
+
features: features.slice(0, 12),
|
|
147
|
+
capabilities: capabilities.slice(0, 10),
|
|
148
|
+
terms: terms.slice(0, 8),
|
|
149
|
+
audience,
|
|
150
|
+
appType,
|
|
151
|
+
appNiche,
|
|
152
|
+
confidence: Math.min(maxScore / 3, 1)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Generate 4-phase prompt structure
|
|
158
|
+
* Phase 1: Foundation & Setup
|
|
159
|
+
* Phase 2: Core Features
|
|
160
|
+
* Phase 3: User Experience
|
|
161
|
+
* Phase 4: Launch & Scale
|
|
162
|
+
*/
|
|
163
|
+
function generatePhases(concept, details) {
|
|
164
|
+
const { businessName, features, capabilities, audience, appType, appNiche } = details;
|
|
165
|
+
|
|
166
|
+
return [
|
|
167
|
+
{
|
|
168
|
+
phase: 1,
|
|
169
|
+
title: 'Foundation & Setup',
|
|
170
|
+
focus: 'Architecture, authentication, database schema',
|
|
171
|
+
content: `Build the foundation for ${businessName}:
|
|
172
|
+
- Set up project structure (React + Hono + Cloudflare Workers)
|
|
173
|
+
- Design database schema for ${appNiche}
|
|
174
|
+
- Implement authentication system
|
|
175
|
+
- Create base API routes
|
|
176
|
+
- Set up deployment pipeline
|
|
177
|
+
|
|
178
|
+
Target: ${audience}
|
|
179
|
+
App Type: ${appType}
|
|
180
|
+
Key Features: ${features.slice(0, 4).join(', ')}`,
|
|
181
|
+
suggestedTools: ['GenXis Auth', 'GenXis CRUD', 'Database Schema Generator']
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
phase: 2,
|
|
185
|
+
title: 'Core Features',
|
|
186
|
+
focus: 'Primary functionality and business logic',
|
|
187
|
+
content: `Implement core features:
|
|
188
|
+
${features.map((f, i) => `${i + 1}. ${f}`).join('\n')}
|
|
189
|
+
|
|
190
|
+
Capabilities:
|
|
191
|
+
${capabilities.slice(0, 5).map((c, i) => `- ${c}`).join('\n')}
|
|
192
|
+
|
|
193
|
+
Database operations, API endpoints, and business logic for each feature.`,
|
|
194
|
+
suggestedTools: ['GenXis CRUD Builder', 'GenXis Search', 'GenXis File Upload']
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
phase: 3,
|
|
198
|
+
title: 'User Experience',
|
|
199
|
+
focus: 'UI/UX, responsive design, accessibility',
|
|
200
|
+
content: `Design the user experience for ${audience}:
|
|
201
|
+
- Responsive layout (mobile-first)
|
|
202
|
+
- Component library setup
|
|
203
|
+
- Form validation and error handling
|
|
204
|
+
- Loading states and optimistic updates
|
|
205
|
+
- Accessibility (WCAG 2.1 AA)
|
|
206
|
+
|
|
207
|
+
Style: Modern, clean, ${appType === 'healthcare' ? 'trustworthy' : 'professional'}
|
|
208
|
+
Theme: Dark mode with light mode toggle`,
|
|
209
|
+
suggestedTools: ['GenXis UI Kit', 'GenXis Analytics', 'GenXis Performance']
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
phase: 4,
|
|
213
|
+
title: 'Launch & Scale',
|
|
214
|
+
focus: 'Deployment, monitoring, optimization',
|
|
215
|
+
content: `Prepare for production:
|
|
216
|
+
- Performance optimization
|
|
217
|
+
- Security hardening
|
|
218
|
+
- Error monitoring (Sentry)
|
|
219
|
+
- Analytics integration
|
|
220
|
+
- SEO optimization
|
|
221
|
+
- Documentation
|
|
222
|
+
|
|
223
|
+
Compliance: ${details.terms.filter(t => ['HIPAA', 'GDPR', 'SOC2', 'PCI'].includes(t)).join(', ') || 'Standard web security'}`,
|
|
224
|
+
suggestedTools: ['GenXis Security', 'GenXis Analytics', 'GenXis Email']
|
|
225
|
+
}
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// API calls
|
|
230
|
+
async function promptExtract(concept, title = '') {
|
|
231
|
+
const details = extractConceptDetails(concept, title);
|
|
232
|
+
return { success: true, ...details };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function promptPhases(concept, title = '') {
|
|
236
|
+
const details = extractConceptDetails(concept, title);
|
|
237
|
+
const phases = generatePhases(concept, details);
|
|
238
|
+
return { success: true, details, phases };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function promptRefine(concept, feedback) {
|
|
242
|
+
return apiRequest('POST', '/tools/prompt_refine', { concept, feedback });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function promptExpand(idea) {
|
|
246
|
+
return apiRequest('POST', '/tools/prompt_expand', { idea });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function promptCategorize(concept) {
|
|
250
|
+
const details = extractConceptDetails(concept);
|
|
251
|
+
return {
|
|
252
|
+
success: true,
|
|
253
|
+
appType: details.appType,
|
|
254
|
+
appNiche: details.appNiche,
|
|
255
|
+
confidence: details.confidence,
|
|
256
|
+
suggestedCategory: APP_CATEGORIES[details.appType]?.niche || 'General'
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Tool definitions
|
|
261
|
+
const PROMPT_ENGINE_TOOLS = [
|
|
262
|
+
{
|
|
263
|
+
name: 'prompt_extract',
|
|
264
|
+
description: 'Extract features, capabilities, audience from natural language. $0.02',
|
|
265
|
+
inputSchema: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
properties: {
|
|
268
|
+
concept: { type: 'string', description: 'Natural language app description' },
|
|
269
|
+
title: { type: 'string', description: 'Optional project title' }
|
|
270
|
+
},
|
|
271
|
+
required: ['concept']
|
|
272
|
+
},
|
|
273
|
+
cost: 0.02,
|
|
274
|
+
tier: 'pro'
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: 'prompt_phases',
|
|
278
|
+
description: 'Generate 4-phase development prompts from concept. $0.05',
|
|
279
|
+
inputSchema: {
|
|
280
|
+
type: 'object',
|
|
281
|
+
properties: {
|
|
282
|
+
concept: { type: 'string', description: 'Natural language app description' },
|
|
283
|
+
title: { type: 'string', description: 'Optional project title' }
|
|
284
|
+
},
|
|
285
|
+
required: ['concept']
|
|
286
|
+
},
|
|
287
|
+
cost: 0.05,
|
|
288
|
+
tier: 'pro'
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: 'prompt_refine',
|
|
292
|
+
description: 'Refine prompts with conversational feedback. $0.08',
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: 'object',
|
|
295
|
+
properties: {
|
|
296
|
+
concept: { type: 'string', description: 'Current concept/prompt' },
|
|
297
|
+
feedback: { type: 'string', description: 'User feedback to incorporate' }
|
|
298
|
+
},
|
|
299
|
+
required: ['concept', 'feedback']
|
|
300
|
+
},
|
|
301
|
+
cost: 0.08,
|
|
302
|
+
tier: 'pro'
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: 'prompt_expand',
|
|
306
|
+
description: 'Expand short idea into detailed concept. $0.10',
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: 'object',
|
|
309
|
+
properties: {
|
|
310
|
+
idea: { type: 'string', description: 'Short idea (1-2 sentences)' }
|
|
311
|
+
},
|
|
312
|
+
required: ['idea']
|
|
313
|
+
},
|
|
314
|
+
cost: 0.10,
|
|
315
|
+
tier: 'pro'
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: 'prompt_categorize',
|
|
319
|
+
description: 'Auto-detect app type and niche. $0.01',
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: 'object',
|
|
322
|
+
properties: {
|
|
323
|
+
concept: { type: 'string', description: 'App concept to categorize' }
|
|
324
|
+
},
|
|
325
|
+
required: ['concept']
|
|
326
|
+
},
|
|
327
|
+
cost: 0.01,
|
|
328
|
+
tier: 'pro'
|
|
329
|
+
}
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
// Handle tool calls
|
|
333
|
+
async function handleTool(name, args) {
|
|
334
|
+
try {
|
|
335
|
+
switch (name) {
|
|
336
|
+
case 'prompt_extract':
|
|
337
|
+
return await promptExtract(args.concept, args.title);
|
|
338
|
+
case 'prompt_phases':
|
|
339
|
+
return await promptPhases(args.concept, args.title);
|
|
340
|
+
case 'prompt_refine':
|
|
341
|
+
return await promptRefine(args.concept, args.feedback);
|
|
342
|
+
case 'prompt_expand':
|
|
343
|
+
return await promptExpand(args.idea);
|
|
344
|
+
case 'prompt_categorize':
|
|
345
|
+
return await promptCategorize(args.concept);
|
|
346
|
+
default:
|
|
347
|
+
return { error: `Unknown prompt_engine tool: ${name}` };
|
|
348
|
+
}
|
|
349
|
+
} catch (e) {
|
|
350
|
+
return { error: e.message };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
module.exports = {
|
|
355
|
+
promptExtract,
|
|
356
|
+
promptPhases,
|
|
357
|
+
promptRefine,
|
|
358
|
+
promptExpand,
|
|
359
|
+
promptCategorize,
|
|
360
|
+
extractConceptDetails,
|
|
361
|
+
generatePhases,
|
|
362
|
+
PROMPT_ENGINE_TOOLS,
|
|
363
|
+
handleTool,
|
|
364
|
+
APP_CATEGORIES
|
|
365
|
+
};
|
package/lib/packs.js
CHANGED
|
@@ -73,6 +73,13 @@ const PACKS = {
|
|
|
73
73
|
tier: 'pro',
|
|
74
74
|
highlights: ['ux_list_blocks', 'ux_ab_compare', 'ux_a11y_check']
|
|
75
75
|
},
|
|
76
|
+
prompt_engine: {
|
|
77
|
+
name: 'prompt_engine',
|
|
78
|
+
description: 'Prompt engineering - extract, phases, refine, expand',
|
|
79
|
+
tools: 5,
|
|
80
|
+
tier: 'pro',
|
|
81
|
+
highlights: ['prompt_extract', 'prompt_phases', 'prompt_expand', 'prompt_categorize']
|
|
82
|
+
},
|
|
76
83
|
librarian: {
|
|
77
84
|
name: 'librarian',
|
|
78
85
|
description: 'Digital asset manager - domains, bookmarks, CSV, folders, writing style',
|
|
@@ -80,6 +87,13 @@ const PACKS = {
|
|
|
80
87
|
tier: 'pro',
|
|
81
88
|
highlights: ['domains_expiring', 'writing_draft', 'bookmarks_prune', 'csv_clean']
|
|
82
89
|
},
|
|
90
|
+
grabr: {
|
|
91
|
+
name: 'grabr',
|
|
92
|
+
description: 'Web scraping - contacts, social links, Wayback, sitemaps',
|
|
93
|
+
tools: 6,
|
|
94
|
+
tier: 'pro',
|
|
95
|
+
highlights: ['grabr_scrape', 'grabr_intel', 'grabr_wayback', 'grabr_batch']
|
|
96
|
+
},
|
|
83
97
|
|
|
84
98
|
// === ENTERPRISE TIER ($499/mo) ===
|
|
85
99
|
labs_plus: {
|
|
@@ -184,6 +198,12 @@ const TOOL_TIERS = {
|
|
|
184
198
|
ux_a11y_check: 'pro',
|
|
185
199
|
ux_color_palette: 'pro',
|
|
186
200
|
ux_contrast_check: 'pro',
|
|
201
|
+
// Prompt Engine tools
|
|
202
|
+
prompt_extract: 'pro',
|
|
203
|
+
prompt_phases: 'pro',
|
|
204
|
+
prompt_refine: 'pro',
|
|
205
|
+
prompt_expand: 'pro',
|
|
206
|
+
prompt_categorize: 'pro',
|
|
187
207
|
// Librarian tools
|
|
188
208
|
bookmarks_prune: 'pro',
|
|
189
209
|
bookmarks_organize: 'pro',
|
|
@@ -395,7 +415,7 @@ const PACK_TOOLS = [
|
|
|
395
415
|
inputSchema: {
|
|
396
416
|
type: 'object',
|
|
397
417
|
properties: {
|
|
398
|
-
pack: { type: 'string', description: 'Pack name', enum: ['beacon', 'labs', 'labs_plus', 'whm', 'cf', 'wp', 'ux', 'librarian'] }
|
|
418
|
+
pack: { type: 'string', description: 'Pack name', enum: ['beacon', 'labs', 'labs_plus', 'whm', 'cf', 'wp', 'ux', 'prompt_engine', 'librarian'] }
|
|
399
419
|
},
|
|
400
420
|
required: ['pack']
|
|
401
421
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "50c",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "AI toolkit with
|
|
3
|
+
"version": "2.8.0",
|
|
4
|
+
"description": "AI toolkit with grabr web scraping. One install, 111+ tools.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"50c": "./bin/50c.js"
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"llm",
|
|
13
13
|
"tools",
|
|
14
14
|
"genius",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"prompt-engine",
|
|
16
|
+
"grabr",
|
|
17
|
+
"scraper",
|
|
18
|
+
"wayback",
|
|
17
19
|
"bcalc",
|
|
18
20
|
"vault",
|
|
19
21
|
"cloudflare",
|
|
20
22
|
"whm",
|
|
21
23
|
"cpanel",
|
|
22
24
|
"wordpress",
|
|
23
|
-
"librarian"
|
|
24
|
-
"bookmarks",
|
|
25
|
-
"csv"
|
|
25
|
+
"librarian"
|
|
26
26
|
],
|
|
27
27
|
"author": "genxis.com",
|
|
28
28
|
"license": "MIT",
|