vipcare 0.6.2 → 0.6.4
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/bin/vip.js +10 -2
- package/lib/card.js +7 -1
- package/lib/fetchers/search.js +16 -2
- package/lib/templates.js +14 -3
- package/package.json +1 -1
package/bin/vip.js
CHANGED
|
@@ -71,6 +71,9 @@ function gatherData(person) {
|
|
|
71
71
|
rawParts.push('=== Web Search Results ===');
|
|
72
72
|
person.rawSnippets.forEach((snippet, i) => {
|
|
73
73
|
if (!snippet.trim()) return; // skip empty
|
|
74
|
+
if (snippet.trim().length < 30) return; // too short to be useful
|
|
75
|
+
const lines = snippet.trim().split('\n');
|
|
76
|
+
if (lines.length >= 2 && lines[0].trim() === lines[1].trim()) return; // title == body
|
|
74
77
|
rawParts.push(snippet);
|
|
75
78
|
const url = person.otherUrls?.[i] || '';
|
|
76
79
|
if (url) searchEntries.push({ url, snippet });
|
|
@@ -82,9 +85,13 @@ function gatherData(person) {
|
|
|
82
85
|
const results = searchPerson(person.name);
|
|
83
86
|
for (const r of results) {
|
|
84
87
|
if (!r.body?.trim() && !r.title?.trim()) continue; // skip empty
|
|
85
|
-
|
|
88
|
+
const snippet = `${r.title}\n${r.body}`;
|
|
89
|
+
if (snippet.trim().length < 30) continue; // too short to be useful
|
|
90
|
+
if (r.title?.trim() === r.body?.trim()) continue; // title == body (junk category)
|
|
91
|
+
if (!r.body?.trim() || r.body.trim().length < 20) continue; // no real body content
|
|
92
|
+
rawParts.push(snippet);
|
|
86
93
|
if (!sources.includes(r.url)) sources.push(r.url);
|
|
87
|
-
searchEntries.push({ url: r.url, snippet
|
|
94
|
+
searchEntries.push({ url: r.url, snippet });
|
|
88
95
|
}
|
|
89
96
|
}
|
|
90
97
|
|
|
@@ -508,6 +515,7 @@ program.command('youtube-search')
|
|
|
508
515
|
|
|
509
516
|
// --- card ---
|
|
510
517
|
program.command('card')
|
|
518
|
+
.alias('open-cards')
|
|
511
519
|
.description('Generate and serve H5 baseball card page')
|
|
512
520
|
.option('-o, --output <path>', 'Output HTML file', path.join(os.homedir(), '.vip', 'cards', 'index.html'))
|
|
513
521
|
.option('-p, --port <port>', 'Server port', '3000')
|
package/lib/card.js
CHANGED
|
@@ -19,8 +19,11 @@ export function generateCards(profiles, outputPath) {
|
|
|
19
19
|
outputPath = path.join(os.homedir(), '.vip', 'cards', 'index.html');
|
|
20
20
|
}
|
|
21
21
|
const cards = [];
|
|
22
|
+
const seenSlugs = new Set();
|
|
22
23
|
|
|
23
24
|
for (const p of profiles) {
|
|
25
|
+
if (seenSlugs.has(p.slug)) continue; // skip duplicates
|
|
26
|
+
seenSlugs.add(p.slug);
|
|
24
27
|
const content = loadProfile(p.slug);
|
|
25
28
|
if (!content) continue;
|
|
26
29
|
|
|
@@ -372,7 +375,10 @@ function openPanel(index) {
|
|
|
372
375
|
\${card.dos?.length || card.donts?.length ? \`<div class="do-dont">\${card.dos?.length ? \`<div class="do-box"><strong>✅ Do</strong><br>\${card.dos.join('<br>')}</div>\` : ''}\${card.donts?.length ? \`<div class="dont-box"><strong>❌ Don't</strong><br>\${card.donts.join('<br>')}</div>\` : ''}</div>\` : ''}
|
|
373
376
|
\${card.gifts?.length ? \`<p style="font-size:0.85em">🎁 \${card.gifts.join(', ')}</p>\` : ''}
|
|
374
377
|
|
|
375
|
-
\${card.
|
|
378
|
+
\${card.allies?.length || card.rivals?.length ? \`<h2>Relationships</h2>
|
|
379
|
+
\${card.allies?.length ? \`<div style="margin-bottom:8px">\${card.allies.map(a => \`<p style="font-size:0.85em;color:#166534">👍 <strong>\${a.name || a}</strong>\${a.reason ? \` — \${a.reason}\` : ''}</p>\`).join('')}</div>\` : ''}
|
|
380
|
+
\${card.rivals?.length ? \`<div>\${card.rivals.map(r => \`<p style="font-size:0.85em;color:#991b1b">👎 <strong>\${r.name || r}</strong>\${r.reason ? \` — \${r.reason}\` : ''}</p>\`).join('')}</div>\` : ''}
|
|
381
|
+
\` : ''}
|
|
376
382
|
|
|
377
383
|
\${card.key_quotes?.length ? \`<h2>Quotes</h2>\${card.key_quotes.slice(0,4).map(q => \`<p style="font-size:0.8em;color:#475569;margin:3px 0">"\${q}"</p>\`).join('')}\` : ''}
|
|
378
384
|
|
package/lib/fetchers/search.js
CHANGED
|
@@ -76,8 +76,15 @@ function searchViaDdgApi(query) {
|
|
|
76
76
|
if (data.RelatedTopics) {
|
|
77
77
|
for (const topic of data.RelatedTopics) {
|
|
78
78
|
if (topic.Text && topic.FirstURL) {
|
|
79
|
+
// Skip DDG category pages (e.g. duckduckgo.com/c/SpaceX_people)
|
|
80
|
+
if (topic.FirstURL.includes('duckduckgo.com/c/')) continue;
|
|
81
|
+
const title = topic.Text.substring(0, 80);
|
|
82
|
+
// Skip entries where title and body are identical (just a category name)
|
|
83
|
+
if (title === topic.Text && topic.Text.length < 20) continue;
|
|
84
|
+
// Skip entries with body too short to be useful
|
|
85
|
+
if (topic.Text.length < 20) continue;
|
|
79
86
|
results.push({
|
|
80
|
-
title
|
|
87
|
+
title,
|
|
81
88
|
url: topic.FirstURL,
|
|
82
89
|
body: topic.Text,
|
|
83
90
|
});
|
|
@@ -86,8 +93,15 @@ function searchViaDdgApi(query) {
|
|
|
86
93
|
if (topic.Topics) {
|
|
87
94
|
for (const sub of topic.Topics) {
|
|
88
95
|
if (sub.Text && sub.FirstURL) {
|
|
96
|
+
// Skip DDG category pages
|
|
97
|
+
if (sub.FirstURL.includes('duckduckgo.com/c/')) continue;
|
|
98
|
+
const subTitle = sub.Text.substring(0, 80);
|
|
99
|
+
// Skip entries where title and body are identical
|
|
100
|
+
if (subTitle === sub.Text && sub.Text.length < 20) continue;
|
|
101
|
+
// Skip entries with body too short to be useful
|
|
102
|
+
if (sub.Text.length < 20) continue;
|
|
89
103
|
results.push({
|
|
90
|
-
title:
|
|
104
|
+
title: subTitle,
|
|
91
105
|
url: sub.FirstURL,
|
|
92
106
|
body: sub.Text,
|
|
93
107
|
});
|
package/lib/templates.js
CHANGED
|
@@ -65,8 +65,18 @@ synthesize an actionable profile focused on HOW to work with this person.
|
|
|
65
65
|
|
|
66
66
|
---
|
|
67
67
|
|
|
68
|
-
##
|
|
69
|
-
|
|
68
|
+
## Relationships & Alliances
|
|
69
|
+
|
|
70
|
+
**Allies / People they like:**
|
|
71
|
+
- {Person 1} — {why: shared values, mutual support, public praise, partnership, etc.}
|
|
72
|
+
- {Person 2} — {why}
|
|
73
|
+
|
|
74
|
+
**Rivals / People they clash with:**
|
|
75
|
+
- {Person 1} — {why: public disagreement, competitive tension, ideological difference, etc.}
|
|
76
|
+
- {Person 2} — {why}
|
|
77
|
+
|
|
78
|
+
**Key relationships:**
|
|
79
|
+
- {Notable relationship with context — mentor, investor, partner, adversary}
|
|
70
80
|
|
|
71
81
|
---
|
|
72
82
|
|
|
@@ -165,7 +175,8 @@ IMPORTANT: After the profile, output a JSON metadata block in EXACTLY this forma
|
|
|
165
175
|
"dos": ["{do1}", "{do2}", "{do3}"],
|
|
166
176
|
"donts": ["{dont1}", "{dont2}", "{dont3}"],
|
|
167
177
|
"gifts": ["{gift idea 1}", "{gift idea 2}"],
|
|
168
|
-
"
|
|
178
|
+
"allies": [{"name": "{person}", "reason": "{why they like them}"}],
|
|
179
|
+
"rivals": [{"name": "{person}", "reason": "{why they clash}"}],
|
|
169
180
|
"twitter_handle": "{handle without @}"
|
|
170
181
|
}
|
|
171
182
|
-->`;
|