sophhub 0.2.3 → 0.2.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.
Files changed (107) hide show
  1. package/package.json +1 -1
  2. package/skills/consensus/skill.json +20 -0
  3. package/skills/consensus/src/SKILL.md +93 -0
  4. package/skills/deepwiki/skill.json +20 -0
  5. package/skills/deepwiki/src/SKILL.md +45 -0
  6. package/skills/deepwiki/src/_meta.json +6 -0
  7. package/skills/deepwiki/src/scripts/deepwiki.js +135 -0
  8. package/skills/feishu-bitable/skill.json +20 -0
  9. package/skills/feishu-bitable/src/CHECKLIST.md +150 -0
  10. package/skills/feishu-bitable/src/README.md +178 -0
  11. package/skills/feishu-bitable/src/SKILL.md +113 -0
  12. package/skills/feishu-bitable/src/_meta.json +6 -0
  13. package/skills/feishu-bitable/src/api.js +381 -0
  14. package/skills/feishu-bitable/src/bin/cli.js +284 -0
  15. package/skills/feishu-bitable/src/description.md +143 -0
  16. package/skills/feishu-bitable/src/examples/create-records.json +52 -0
  17. package/skills/feishu-bitable/src/examples/create-table.json +64 -0
  18. package/skills/feishu-bitable/src/package-lock.json +324 -0
  19. package/skills/feishu-bitable/src/package.json +33 -0
  20. package/skills/feishu-bitable/src/publish-config.json +14 -0
  21. package/skills/feishu-bitable/src/test-simple.js +61 -0
  22. package/skills/feishu-bitable/src/utils.js +261 -0
  23. package/skills/google-maps/skill.json +20 -0
  24. package/skills/google-maps/src/SKILL.md +237 -0
  25. package/skills/google-maps/src/_meta.json +6 -0
  26. package/skills/google-maps/src/lib/map_helper.py +912 -0
  27. package/skills/large-task-router/skill.json +20 -0
  28. package/skills/large-task-router/src/SKILL.md +79 -0
  29. package/skills/large-task-router/src/templates/plan.md +74 -0
  30. package/skills/skillhub/skill.json +11 -4
  31. package/skills/skillhub/src/SKILL.md +11 -1
  32. package/skills/sophnet-dailynews/skill.json +20 -0
  33. package/skills/sophnet-dailynews/src/SKILL.md +179 -0
  34. package/skills/sophnet-dailynews/src/cache.json +151 -0
  35. package/skills/sophnet-dailynews/src/sources.json +230 -0
  36. package/skills/sophnet-schedule/skill.json +20 -0
  37. package/skills/sophnet-schedule/src/ARCHITECTURE.md +321 -0
  38. package/skills/sophnet-schedule/src/IMPROVEMENTS.md +145 -0
  39. package/skills/sophnet-schedule/src/SKILL.md +1050 -0
  40. package/skills/sophnet-schedule/src/_meta.json +6 -0
  41. package/skills/sophnet-schedule/src/api/__init__.py +0 -0
  42. package/skills/sophnet-schedule/src/api/models.py +245 -0
  43. package/skills/sophnet-schedule/src/apps/add_event.py +237 -0
  44. package/skills/sophnet-schedule/src/apps/check_reminders.py +112 -0
  45. package/skills/sophnet-schedule/src/apps/check_roc.py +246 -0
  46. package/skills/sophnet-schedule/src/apps/generate_daily_plan.py +342 -0
  47. package/skills/sophnet-schedule/src/apps/import_events.py +216 -0
  48. package/skills/sophnet-schedule/src/apps/monitor_calendar_changes.py +140 -0
  49. package/skills/sophnet-schedule/src/apps/register_tasks.py +169 -0
  50. package/skills/sophnet-schedule/src/apps/sync_roc_to_gcal.py +174 -0
  51. package/skills/sophnet-schedule/src/compat.py +66 -0
  52. package/skills/sophnet-schedule/src/config/__init__.py +0 -0
  53. package/skills/sophnet-schedule/src/config/reminder_rules.yaml +96 -0
  54. package/skills/sophnet-schedule/src/config/roc_events.yaml +44 -0
  55. package/skills/sophnet-schedule/src/config/settings.py +133 -0
  56. package/skills/sophnet-schedule/src/config/task_registry.yaml +92 -0
  57. package/skills/sophnet-schedule/src/docs/FRONTEND_INTEGRATION_GUIDE.md +437 -0
  58. package/skills/sophnet-schedule/src/gcal/__init__.py +0 -0
  59. package/skills/sophnet-schedule/src/gcal/client.py +374 -0
  60. package/skills/sophnet-schedule/src/gcal/models.py +91 -0
  61. package/skills/sophnet-schedule/src/requirements.txt +6 -0
  62. package/skills/sophnet-schedule/src/scripts/setup_gcal_token.py +85 -0
  63. package/skills/sophnet-schedule/src/server.py +669 -0
  64. package/skills/sophnet-schedule/src/services/__init__.py +0 -0
  65. package/skills/sophnet-schedule/src/services/calendar_backend.py +139 -0
  66. package/skills/sophnet-schedule/src/services/conflict_detector.py +96 -0
  67. package/skills/sophnet-schedule/src/services/datetime_utils.py +117 -0
  68. package/skills/sophnet-schedule/src/services/event_classifier.py +100 -0
  69. package/skills/sophnet-schedule/src/services/event_diff.py +160 -0
  70. package/skills/sophnet-schedule/src/services/google_integration.py +500 -0
  71. package/skills/sophnet-schedule/src/services/job_store.py +100 -0
  72. package/skills/sophnet-schedule/src/services/local_event_store.py +266 -0
  73. package/skills/sophnet-schedule/src/services/reminder_planner.py +116 -0
  74. package/skills/sophnet-schedule/src/services/runtime_utils.py +31 -0
  75. package/skills/sophnet-schedule/src/services/table_parser.py +286 -0
  76. package/skills/sophnet-schedule/src/services/task_builder.py +167 -0
  77. package/skills/sophnet-schedule/src/services/time_window.py +72 -0
  78. package/skills/sophnet-stock/skill.json +20 -0
  79. package/skills/sophnet-stock/src/App-Plan.md +442 -0
  80. package/skills/sophnet-stock/src/README.md +214 -0
  81. package/skills/sophnet-stock/src/SKILL.md +236 -0
  82. package/skills/sophnet-stock/src/TODO.md +394 -0
  83. package/skills/sophnet-stock/src/_meta.json +6 -0
  84. package/skills/sophnet-stock/src/docs/ARCHITECTURE.md +408 -0
  85. package/skills/sophnet-stock/src/docs/CONCEPT.md +233 -0
  86. package/skills/sophnet-stock/src/docs/HOT_SCANNER.md +288 -0
  87. package/skills/sophnet-stock/src/docs/README.md +95 -0
  88. package/skills/sophnet-stock/src/docs/USAGE.md +465 -0
  89. package/skills/sophnet-stock/src/scripts/analyze_stock.py +2565 -0
  90. package/skills/sophnet-stock/src/scripts/dividends.py +365 -0
  91. package/skills/sophnet-stock/src/scripts/hot_scanner.py +582 -0
  92. package/skills/sophnet-stock/src/scripts/portfolio.py +548 -0
  93. package/skills/sophnet-stock/src/scripts/rumor_scanner.py +342 -0
  94. package/skills/sophnet-stock/src/scripts/test_stock_analysis.py +409 -0
  95. package/skills/sophnet-stock/src/scripts/watchlist.py +336 -0
  96. package/skills/xiaohongshu/skill.json +20 -0
  97. package/skills/xiaohongshu/src/SKILL.md +91 -0
  98. package/skills/xiaohongshu/src/_meta.json +6 -0
  99. package/skills/xiaohongshu/src/assets/card.html +216 -0
  100. package/skills/xiaohongshu/src/assets/cover.html +82 -0
  101. package/skills/xiaohongshu/src/assets/example.md +84 -0
  102. package/skills/xiaohongshu/src/assets/styles.css +318 -0
  103. package/skills/xiaohongshu/src/scripts/render_xhs_v2.py +737 -0
  104. package/skills/xiaohongshu/src/scripts/sign_server.py +158 -0
  105. package/skills/xiaohongshu/src/scripts/stealth.min.js +7 -0
  106. package/skills/xiaohongshu/src/scripts/xhs_tool.py +186 -0
  107. package/skills/xiaohongshu/src/workflow.py +185 -0
@@ -0,0 +1,342 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 🔮 RUMOR & BUZZ SCANNER
4
+ Scans for early signals, rumors, and whispers before they become mainstream news.
5
+
6
+ Sources:
7
+ - Twitter/X: "hearing", "rumor", "sources say", unusual buzz
8
+ - Google News: M&A, insider, upgrade/downgrade
9
+ - Unusual keywords detection
10
+
11
+ Usage: python3 rumor_scanner.py
12
+ """
13
+
14
+ import json
15
+ import os
16
+ import subprocess
17
+ import sys
18
+ import re
19
+ from datetime import datetime, timezone
20
+ from pathlib import Path
21
+ from urllib.request import urlopen, Request
22
+ from urllib.parse import quote_plus
23
+ import gzip
24
+
25
+ CACHE_DIR = Path(__file__).parent.parent / "cache"
26
+ CACHE_DIR.mkdir(exist_ok=True)
27
+
28
+ # Bird CLI path
29
+ BIRD_CLI = "/home/clawdbot/.nvm/versions/node/v24.12.0/bin/bird"
30
+ BIRD_ENV = Path(__file__).parent.parent / ".env"
31
+
32
+ def load_env():
33
+ """Load environment variables from .env file."""
34
+ if BIRD_ENV.exists():
35
+ for line in BIRD_ENV.read_text().splitlines():
36
+ if '=' in line and not line.startswith('#'):
37
+ key, value = line.split('=', 1)
38
+ os.environ[key.strip()] = value.strip().strip('"').strip("'")
39
+
40
+ def fetch_url(url, timeout=15):
41
+ """Fetch URL with headers."""
42
+ headers = {
43
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
44
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
45
+ 'Accept-Encoding': 'gzip, deflate',
46
+ 'Accept-Language': 'en-US,en;q=0.9',
47
+ }
48
+ req = Request(url, headers=headers)
49
+ try:
50
+ with urlopen(req, timeout=timeout) as resp:
51
+ data = resp.read()
52
+ if resp.info().get('Content-Encoding') == 'gzip':
53
+ data = gzip.decompress(data)
54
+ return data.decode('utf-8', errors='ignore')
55
+ except Exception as e:
56
+ return None
57
+
58
+ def search_twitter_rumors():
59
+ """Search Twitter for rumors and early signals."""
60
+ results = []
61
+
62
+ # Rumor-focused search queries
63
+ queries = [
64
+ '"hearing that" stock OR $',
65
+ '"sources say" stock OR company',
66
+ '"rumor" merger OR acquisition',
67
+ 'insider buying stock',
68
+ '"upgrade" OR "downgrade" stock tomorrow',
69
+ '$AAPL OR $TSLA OR $NVDA rumor',
70
+ '"breaking" stock market',
71
+ 'M&A rumor',
72
+ ]
73
+
74
+ load_env()
75
+
76
+ for query in queries[:4]: # Limit to avoid rate limits
77
+ try:
78
+ cmd = [BIRD_CLI, 'search', query, '-n', '10', '--json']
79
+ env = os.environ.copy()
80
+
81
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, env=env)
82
+
83
+ if result.returncode == 0 and result.stdout:
84
+ try:
85
+ tweets = json.loads(result.stdout)
86
+ for tweet in tweets:
87
+ text = tweet.get('text', '')
88
+ # Filter for actual rumors/signals
89
+ if any(kw in text.lower() for kw in ['hearing', 'rumor', 'source', 'insider', 'upgrade', 'downgrade', 'breaking', 'M&A', 'merger', 'acquisition']):
90
+ results.append({
91
+ 'source': 'twitter',
92
+ 'type': 'rumor',
93
+ 'text': text[:300],
94
+ 'author': tweet.get('author', {}).get('username', 'unknown'),
95
+ 'likes': tweet.get('likes', 0),
96
+ 'retweets': tweet.get('retweets', 0),
97
+ 'query': query
98
+ })
99
+ except json.JSONDecodeError:
100
+ pass
101
+ except Exception as e:
102
+ pass
103
+
104
+ # Dedupe by text similarity
105
+ seen = set()
106
+ unique = []
107
+ for r in results:
108
+ key = r['text'][:100]
109
+ if key not in seen:
110
+ seen.add(key)
111
+ unique.append(r)
112
+
113
+ return unique
114
+
115
+ def search_twitter_buzz():
116
+ """Search Twitter for general stock buzz - what are people talking about?"""
117
+ results = []
118
+
119
+ queries = [
120
+ '$SPY OR $QQQ',
121
+ 'stock to buy',
122
+ 'calls OR puts expiring',
123
+ 'earnings play',
124
+ 'short squeeze',
125
+ ]
126
+
127
+ load_env()
128
+
129
+ for query in queries[:3]:
130
+ try:
131
+ cmd = [BIRD_CLI, 'search', query, '-n', '15', '--json']
132
+ env = os.environ.copy()
133
+
134
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, env=env)
135
+
136
+ if result.returncode == 0 and result.stdout:
137
+ try:
138
+ tweets = json.loads(result.stdout)
139
+ for tweet in tweets:
140
+ text = tweet.get('text', '')
141
+ # Extract stock symbols
142
+ symbols = re.findall(r'\$([A-Z]{1,5})\b', text)
143
+ if symbols:
144
+ results.append({
145
+ 'source': 'twitter',
146
+ 'type': 'buzz',
147
+ 'text': text[:300],
148
+ 'symbols': symbols,
149
+ 'author': tweet.get('author', {}).get('username', 'unknown'),
150
+ 'engagement': tweet.get('likes', 0) + tweet.get('retweets', 0) * 2
151
+ })
152
+ except json.JSONDecodeError:
153
+ pass
154
+ except Exception as e:
155
+ pass
156
+
157
+ # Sort by engagement
158
+ results.sort(key=lambda x: x.get('engagement', 0), reverse=True)
159
+ return results[:20]
160
+
161
+ def search_news_rumors():
162
+ """Search Google News for M&A, insider, upgrade news."""
163
+ results = []
164
+
165
+ queries = [
166
+ 'merger acquisition rumor',
167
+ 'insider buying stock',
168
+ 'analyst upgrade stock',
169
+ 'takeover bid company',
170
+ 'SEC investigation company',
171
+ ]
172
+
173
+ for query in queries:
174
+ url = f"https://news.google.com/rss/search?q={quote_plus(query)}&hl=en-US&gl=US&ceid=US:en"
175
+ content = fetch_url(url)
176
+
177
+ if content:
178
+ import xml.etree.ElementTree as ET
179
+ try:
180
+ root = ET.fromstring(content)
181
+ for item in root.findall('.//item')[:5]:
182
+ title = item.find('title')
183
+ link = item.find('link')
184
+ pub_date = item.find('pubDate')
185
+
186
+ if title is not None:
187
+ title_text = title.text or ''
188
+ # Extract company names or symbols
189
+ results.append({
190
+ 'source': 'google_news',
191
+ 'type': 'news_rumor',
192
+ 'title': title_text,
193
+ 'link': link.text if link is not None else '',
194
+ 'date': pub_date.text if pub_date is not None else '',
195
+ 'query': query
196
+ })
197
+ except ET.ParseError:
198
+ pass
199
+
200
+ return results
201
+
202
+ def extract_symbols_from_text(text):
203
+ """Extract stock symbols from text."""
204
+ # $SYMBOL pattern
205
+ dollar_symbols = re.findall(r'\$([A-Z]{1,5})\b', text)
206
+
207
+ # Common company name to symbol mapping
208
+ company_map = {
209
+ 'apple': 'AAPL', 'tesla': 'TSLA', 'nvidia': 'NVDA', 'microsoft': 'MSFT',
210
+ 'google': 'GOOGL', 'amazon': 'AMZN', 'meta': 'META', 'netflix': 'NFLX',
211
+ 'coinbase': 'COIN', 'robinhood': 'HOOD', 'disney': 'DIS', 'intel': 'INTC',
212
+ 'amd': 'AMD', 'palantir': 'PLTR', 'gamestop': 'GME', 'amc': 'AMC',
213
+ }
214
+
215
+ text_lower = text.lower()
216
+ company_symbols = [sym for name, sym in company_map.items() if name in text_lower]
217
+
218
+ return list(set(dollar_symbols + company_symbols))
219
+
220
+ def calculate_rumor_score(item):
221
+ """Score a rumor by potential impact."""
222
+ score = 0
223
+ text = (item.get('text', '') + item.get('title', '')).lower()
224
+
225
+ # High impact keywords
226
+ if any(kw in text for kw in ['merger', 'acquisition', 'takeover', 'buyout']):
227
+ score += 5
228
+ if any(kw in text for kw in ['insider', 'ceo buying', 'director buying']):
229
+ score += 4
230
+ if any(kw in text for kw in ['upgrade', 'price target raised']):
231
+ score += 3
232
+ if any(kw in text for kw in ['downgrade', 'sec investigation', 'fraud']):
233
+ score += 3
234
+ if any(kw in text for kw in ['hearing', 'sources say', 'rumor']):
235
+ score += 2
236
+ if any(kw in text for kw in ['breaking', 'just in', 'alert']):
237
+ score += 2
238
+
239
+ # Engagement boost
240
+ if item.get('engagement', 0) > 100:
241
+ score += 2
242
+ if item.get('likes', 0) > 50:
243
+ score += 1
244
+
245
+ return score
246
+
247
+ def main():
248
+ print("=" * 60)
249
+ print("🔮 RUMOR & BUZZ SCANNER")
250
+ print(f"📅 {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')} UTC")
251
+ print("=" * 60)
252
+ print()
253
+ print("🔍 Scanning for early signals...")
254
+ print()
255
+
256
+ all_rumors = []
257
+ all_buzz = []
258
+
259
+ # Twitter Rumors
260
+ print(" 🐦 Twitter rumors...")
261
+ rumors = search_twitter_rumors()
262
+ print(f" ✅ {len(rumors)} potential rumors")
263
+ all_rumors.extend(rumors)
264
+
265
+ # Twitter Buzz
266
+ print(" 🐦 Twitter buzz...")
267
+ buzz = search_twitter_buzz()
268
+ print(f" ✅ {len(buzz)} buzz items")
269
+ all_buzz.extend(buzz)
270
+
271
+ # News Rumors
272
+ print(" 📰 News rumors...")
273
+ news = search_news_rumors()
274
+ print(f" ✅ {len(news)} news items")
275
+ all_rumors.extend(news)
276
+
277
+ # Score and sort rumors
278
+ for item in all_rumors:
279
+ item['score'] = calculate_rumor_score(item)
280
+ item['symbols'] = extract_symbols_from_text(item.get('text', '') + item.get('title', ''))
281
+
282
+ all_rumors.sort(key=lambda x: x['score'], reverse=True)
283
+
284
+ # Count symbol mentions in buzz
285
+ symbol_counts = {}
286
+ for item in all_buzz:
287
+ for sym in item.get('symbols', []):
288
+ symbol_counts[sym] = symbol_counts.get(sym, 0) + 1
289
+
290
+ # Output
291
+ print()
292
+ print("=" * 60)
293
+ print("🔮 RESULTS")
294
+ print("=" * 60)
295
+ print()
296
+
297
+ # Top Rumors
298
+ print("🚨 TOP RUMORS (by potential impact):")
299
+ print()
300
+ for item in all_rumors[:10]:
301
+ if item['score'] > 0:
302
+ source = item['source']
303
+ symbols = ', '.join(item.get('symbols', [])) or 'N/A'
304
+ text = item.get('text', item.get('title', ''))[:80]
305
+ print(f" [{item['score']}] [{source}] {symbols}")
306
+ print(f" {text}...")
307
+ print()
308
+
309
+ # Buzz Leaderboard
310
+ print("📊 BUZZ LEADERBOARD (most discussed):")
311
+ print()
312
+ sorted_symbols = sorted(symbol_counts.items(), key=lambda x: x[1], reverse=True)
313
+ for symbol, count in sorted_symbols[:15]:
314
+ bar = "█" * min(count, 20)
315
+ print(f" ${symbol:5} {bar} ({count})")
316
+
317
+ print()
318
+
319
+ # Recent Buzz Snippets
320
+ print("💬 WHAT PEOPLE ARE SAYING:")
321
+ print()
322
+ for item in all_buzz[:8]:
323
+ author = item.get('author', 'anon')
324
+ text = item.get('text', '')[:120]
325
+ engagement = item.get('engagement', 0)
326
+ print(f" @{author} ({engagement}♥): {text}...")
327
+ print()
328
+
329
+ # Save results
330
+ output = {
331
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
332
+ 'rumors': all_rumors[:20],
333
+ 'buzz': all_buzz[:30],
334
+ 'symbol_counts': symbol_counts,
335
+ }
336
+
337
+ output_file = CACHE_DIR / 'rumor_scan_latest.json'
338
+ output_file.write_text(json.dumps(output, indent=2, default=str))
339
+ print(f"💾 Saved: {output_file}")
340
+
341
+ if __name__ == "__main__":
342
+ main()