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 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)}&timestamp=${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.6.0",
4
- "description": "AI toolkit with browser tools. One install, 100+ tools.",
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
- "agent-autopsy",
16
- "prompt-fortress",
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",