vipcare 0.6.0 → 0.6.1

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/CLAUDE.md CHANGED
@@ -26,7 +26,6 @@ lib/
26
26
  templates.js — Profile templates
27
27
  tests/ — One test file per module
28
28
  profiles/ — Generated profile Markdown files
29
- web/ — Card HTML output
30
29
  skill/ — Claude Code slash command
31
30
  ```
32
31
 
package/lib/card.js CHANGED
@@ -234,6 +234,7 @@ header .count { color: #94a3b8; font-size: 0.85em; }
234
234
  <div class="main" id="main">
235
235
  <header>
236
236
  <h1>VIPCare</h1>
237
+ <input type="text" id="search" placeholder="Search by name or tag..." style="padding:8px 14px;border:1px solid #e2e8f0;border-radius:8px;font-size:0.9em;width:260px;outline:none;color:#1e293b;background:#fff;" onfocus="this.style.borderColor='#2563eb'" onblur="this.style.borderColor='#e2e8f0'">
237
238
  <span class="count" id="count"></span>
238
239
  </header>
239
240
  <div class="grid" id="grid"></div>
@@ -419,6 +420,15 @@ cards.sort((a, b) => (b.updated || '').localeCompare(a.updated || ''));
419
420
  const grid = document.getElementById('grid');
420
421
  grid.innerHTML = cards.map((card, i) => renderCard(card, i)).join('');
421
422
  document.getElementById('count').textContent = cards.length + ' contacts';
423
+
424
+ document.getElementById('search').addEventListener('input', (e) => {
425
+ const q = e.target.value.toLowerCase();
426
+ cards.forEach((card, i) => {
427
+ const el = document.getElementById('card-' + i);
428
+ const text = [card.name, card.company, card.title, ...(card.tags || [])].join(' ').toLowerCase();
429
+ el.style.display = text.includes(q) ? '' : 'none';
430
+ });
431
+ });
422
432
  </script>
423
433
  </body>
424
434
  </html>`;
@@ -12,8 +12,8 @@ function getBackend() {
12
12
  const config = loadConfig();
13
13
  if (config.ai_backend) return config.ai_backend.toLowerCase();
14
14
 
15
- if (checkTool('claude')) return 'claude-cli';
16
15
  if (process.env.ANTHROPIC_API_KEY || config.anthropic_api_key) return 'anthropic';
16
+ if (checkTool('claude')) return 'claude-cli';
17
17
  if (checkTool('gh') && copilotAvailable()) return 'copilot-cli';
18
18
 
19
19
  throw new Error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vipcare",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Auto-build VIP person profiles from Twitter/LinkedIn public data",
5
5
  "type": "module",
6
6
  "bin": {
package/web/index.html DELETED
@@ -1,229 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>VIPCare - Baseball Cards</title>
7
- <style>
8
- * { margin: 0; padding: 0; box-sizing: border-box; }
9
- html { scroll-behavior: smooth; }
10
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0; min-height: 100vh; padding: 20px; padding: max(20px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) max(20px, env(safe-area-inset-bottom)) max(20px, env(safe-area-inset-left)); }
11
- h1 { text-align: center; font-size: 1.8em; margin: 20px 0 30px; color: #38bdf8; }
12
- .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; max-width: 1200px; margin: 0 auto; }
13
-
14
- .card {
15
- background: linear-gradient(145deg, #1e293b, #334155);
16
- border-radius: 16px;
17
- padding: 24px;
18
- border: 1px solid #475569;
19
- cursor: pointer;
20
- transition: transform 0.2s, box-shadow 0.2s;
21
- position: relative;
22
- overflow: hidden;
23
- min-height: 44px;
24
- -webkit-tap-highlight-color: transparent;
25
- }
26
- .card:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(56,189,248,0.15); }
27
- .card:active { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(56,189,248,0.1); }
28
- .card::before {
29
- content: '';
30
- position: absolute;
31
- top: 0; left: 0; right: 0;
32
- height: 4px;
33
- background: linear-gradient(90deg, #38bdf8, #818cf8, #c084fc);
34
- }
35
-
36
- .card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
37
- .card-name { font-size: 1.4em; font-weight: 700; color: #f1f5f9; }
38
- .card-role { font-size: 0.85em; color: #94a3b8; margin-top: 2px; }
39
- .card-badges { display: flex; gap: 6px; }
40
- .badge { padding: 4px 10px; border-radius: 6px; font-size: 0.75em; font-weight: 700; min-height: 28px; display: inline-flex; align-items: center; }
41
- .badge-disc { background: #38bdf8; color: #0f172a; }
42
- .badge-mbti { background: #818cf8; color: #0f172a; }
43
-
44
- .card-quote { font-style: italic; color: #94a3b8; font-size: 0.8em; margin: 10px 0; padding: 8px 12px; border-left: 3px solid #475569; }
45
-
46
- .radar-container { display: flex; justify-content: center; margin: 16px 0; }
47
- .radar { width: 200px; height: 200px; max-width: 100%; }
48
-
49
- .tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; }
50
- .tag { background: #1e3a5f; color: #38bdf8; padding: 4px 12px; border-radius: 12px; font-size: 0.75em; min-height: 28px; display: inline-flex; align-items: center; }
51
-
52
- .expertise { margin: 10px 0; }
53
- .expertise-title { font-size: 0.75em; color: #64748b; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }
54
- .expertise-item { font-size: 0.8em; color: #cbd5e1; padding: 2px 0; }
55
- .superpower { color: #fbbf24; font-weight: 600; font-size: 0.85em; margin: 6px 0; }
56
-
57
- .tips { margin-top: 12px; border-top: 1px solid #475569; padding-top: 12px; }
58
- .tip-row { display: flex; gap: 4px; font-size: 0.8em; margin: 4px 0; color: #cbd5e1; min-height: 44px; align-items: center; }
59
- .tip-icon { width: 20px; text-align: center; }
60
- .tip-label { color: #64748b; min-width: 55px; }
61
-
62
- /* Modal */
63
- .modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.7); z-index: 100; justify-content: center; align-items: center; padding: 20px; }
64
- .modal-overlay.active { display: flex; }
65
- .modal {
66
- background: #1e293b; border-radius: 16px; max-width: 600px; width: 100%; max-height: 90vh; overflow-y: auto; padding: 32px;
67
- border: 1px solid #475569;
68
- -webkit-overflow-scrolling: touch;
69
- }
70
- .modal-close { float: right; background: none; border: none; color: #94a3b8; font-size: 1.5em; cursor: pointer; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
71
- .modal h2 { color: #38bdf8; margin: 16px 0 8px; font-size: 1.1em; }
72
- .modal p, .modal li { color: #cbd5e1; font-size: 0.9em; line-height: 1.6; }
73
- .modal ul { padding-left: 20px; }
74
-
75
- /* Mobile: screens < 480px */
76
- @media (max-width: 480px) {
77
- body { padding: max(12px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-right)) max(12px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left)); }
78
- h1 { font-size: 1.5em; margin: 12px 0 20px; }
79
- .grid { grid-template-columns: 1fr; gap: 16px; }
80
- .card { padding: 18px; }
81
- .card-name { font-size: 1.25em; }
82
- .card-role { font-size: 0.9em; }
83
- .card-quote { font-size: 0.85em; }
84
- .tip-row { font-size: 0.85em; }
85
- .radar { width: 180px; height: 180px; }
86
- .badge { font-size: 0.8em; padding: 5px 12px; }
87
- .tag { font-size: 0.8em; padding: 5px 14px; }
88
-
89
- .modal-overlay { padding: 0; align-items: stretch; }
90
- .modal { max-width: 100%; max-height: 100vh; height: 100%; border-radius: 0; padding: 20px; padding-top: max(20px, env(safe-area-inset-top)); padding-bottom: max(20px, env(safe-area-inset-bottom)); }
91
- .modal h2 { font-size: 1.15em; }
92
- .modal p, .modal li { font-size: 0.95em; line-height: 1.7; }
93
- }
94
- </style>
95
- </head>
96
- <body>
97
-
98
- <h1>VIPCare</h1>
99
- <div class="grid" id="grid"></div>
100
-
101
- <div class="modal-overlay" id="modal" onclick="if(event.target===this)closeModal()">
102
- <div class="modal" id="modal-content"></div>
103
- </div>
104
-
105
- <script>
106
- const cards = [{"name":"Compare JSON","title":"VP","company":"ACME","location":"","disc":"?","mbti":"?","scores":{},"tags":[],"icebreakers":[],"dos":[],"donts":[],"gifts":[],"expertise":[],"superpower":"","quote":"Test"},{"name":"Sam Altman","title":"","company":"","location":"","disc":"?","mbti":"?","scores":{},"tags":[],"icebreakers":[],"dos":[],"donts":[],"gifts":[],"expertise":[],"superpower":"","quote":""}];
107
-
108
- const SCORE_LABELS = {
109
- openness: 'Openness',
110
- conscientiousness: 'Conscientiousness',
111
- extraversion: 'Extraversion',
112
- agreeableness: 'Agreeableness',
113
- resilience: 'Resilience',
114
- decision_style: 'Decision',
115
- risk_appetite: 'Risk',
116
- communication: 'Communication',
117
- influence: 'Influence',
118
- leadership: 'Leadership'
119
- };
120
-
121
- function radarSvg(scores, size = 200) {
122
- const keys = Object.keys(SCORE_LABELS);
123
- const cx = size / 2, cy = size / 2, r = size * 0.38;
124
- const n = keys.length;
125
-
126
- let gridLines = '';
127
- for (let level = 1; level <= 5; level++) {
128
- const lr = r * level / 5;
129
- let pts = [];
130
- for (let i = 0; i < n; i++) {
131
- const angle = (Math.PI * 2 * i / n) - Math.PI / 2;
132
- pts.push(`${cx + lr * Math.cos(angle)},${cy + lr * Math.sin(angle)}`);
133
- }
134
- gridLines += `<polygon points="${pts.join(' ')}" fill="none" stroke="#334155" stroke-width="0.5"/>`;
135
- }
136
-
137
- let axes = '', labels = '', dataPoints = [];
138
- for (let i = 0; i < n; i++) {
139
- const angle = (Math.PI * 2 * i / n) - Math.PI / 2;
140
- const x = cx + r * Math.cos(angle);
141
- const y = cy + r * Math.sin(angle);
142
- axes += `<line x1="${cx}" y1="${cy}" x2="${x}" y2="${y}" stroke="#334155" stroke-width="0.5"/>`;
143
-
144
- const lx = cx + (r + 22) * Math.cos(angle);
145
- const ly = cy + (r + 22) * Math.sin(angle);
146
- const label = SCORE_LABELS[keys[i]] || keys[i];
147
- labels += `<text x="${lx}" y="${ly}" text-anchor="middle" dominant-baseline="middle" fill="#64748b" font-size="9">${label}</text>`;
148
-
149
- const val = (scores[keys[i]] || 0) / 5;
150
- const dx = cx + r * val * Math.cos(angle);
151
- const dy = cy + r * val * Math.sin(angle);
152
- dataPoints.push(`${dx},${dy}`);
153
- }
154
-
155
- const dataPolygon = `<polygon points="${dataPoints.join(' ')}" fill="rgba(56,189,248,0.2)" stroke="#38bdf8" stroke-width="1.5"/>`;
156
-
157
- return `<svg viewBox="0 0 ${size} ${size}" class="radar">${gridLines}${axes}${dataPolygon}${labels}</svg>`;
158
- }
159
-
160
- function renderCard(card, index) {
161
- const scores = card.scores || {};
162
- const radar = radarSvg(scores);
163
-
164
- return `
165
- <div class="card" onclick="openModal(${index})">
166
- <div class="card-header">
167
- <div>
168
- <div class="card-name">${card.name || 'Unknown'}</div>
169
- <div class="card-role">${card.title || ''}${card.company ? ' @ ' + card.company : ''}</div>
170
- </div>
171
- <div class="card-badges">
172
- <span class="badge badge-disc">${card.disc || '?'}</span>
173
- <span class="badge badge-mbti">${card.mbti || '?'}</span>
174
- </div>
175
- </div>
176
- ${card.quote ? `<div class="card-quote">"${card.quote.slice(0, 120)}${card.quote.length > 120 ? '...' : ''}"</div>` : ''}
177
- <div class="radar-container">${radar}</div>
178
- ${card.superpower ? `<div class="superpower">⚡ ${card.superpower}</div>` : ''}
179
- ${card.tags?.length ? `<div class="tags">${card.tags.map(t => `<span class="tag">${t}</span>`).join('')}</div>` : ''}
180
- <div class="tips">
181
- ${card.icebreakers?.length ? `<div class="tip-row"><span class="tip-icon">💡</span><span class="tip-label">Icebreaker</span>${card.icebreakers[0]}</div>` : ''}
182
- ${card.dos?.length ? `<div class="tip-row"><span class="tip-icon">✅</span><span class="tip-label">Do</span>${card.dos[0]}</div>` : ''}
183
- ${card.donts?.length ? `<div class="tip-row"><span class="tip-icon">❌</span><span class="tip-label">Don't</span>${card.donts[0]}</div>` : ''}
184
- </div>
185
- </div>`;
186
- }
187
-
188
- function openModal(index) {
189
- const card = cards[index];
190
- const s = card.scores || {};
191
- const modal = document.getElementById('modal-content');
192
-
193
- modal.innerHTML = `
194
- <button class="modal-close" onclick="closeModal()">&times;</button>
195
- <h1 style="color:#38bdf8;margin-bottom:4px">${card.name}</h1>
196
- <p style="color:#94a3b8">${card.title || ''}${card.company ? ' @ ' + card.company : ''}${card.location ? ' · ' + card.location : ''}</p>
197
- ${card.quote ? `<div class="card-quote" style="margin:16px 0">"${card.quote}"</div>` : ''}
198
-
199
- <h2>Personality</h2>
200
- <p><strong>DISC:</strong> ${card.disc || '?'} &nbsp; <strong>MBTI:</strong> ${card.mbti || '?'}</p>
201
- <div style="display:flex;justify-content:center;margin:16px 0">${radarSvg(s, 260)}</div>
202
-
203
- ${card.expertise?.length ? `<h2>Expertise</h2><ul>${card.expertise.map(e => `<li>${e}</li>`).join('')}</ul>` : ''}
204
- ${card.superpower ? `<p><strong>⚡ Superpower:</strong> ${card.superpower}</p>` : ''}
205
-
206
- <h2>How to Work With Them</h2>
207
- ${card.icebreakers?.length ? `<p><strong>💡 Icebreakers:</strong> ${card.icebreakers.join(', ')}</p>` : ''}
208
- ${card.dos?.length ? `<p><strong>✅ Do:</strong> ${card.dos.join(' · ')}</p>` : ''}
209
- ${card.donts?.length ? `<p><strong>❌ Don't:</strong> ${card.donts.join(' · ')}</p>` : ''}
210
- ${card.gifts?.length ? `<p><strong>🎁 Gifts:</strong> ${card.gifts.join(', ')}</p>` : ''}
211
-
212
- ${card.tags?.length ? `<h2>Tags</h2><div class="tags">${card.tags.map(t => `<span class="tag">${t}</span>`).join('')}</div>` : ''}
213
- `;
214
-
215
- document.getElementById('modal').classList.add('active');
216
- }
217
-
218
- function closeModal() {
219
- document.getElementById('modal').classList.remove('active');
220
- }
221
-
222
- document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); });
223
-
224
- // Render
225
- const grid = document.getElementById('grid');
226
- grid.innerHTML = cards.map((card, i) => renderCard(card, i)).join('');
227
- </script>
228
- </body>
229
- </html>