@gotza02/sequential-thinking 2026.2.39 → 2026.2.41

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 (38) hide show
  1. package/README.md +7 -4
  2. package/SYSTEM_INSTRUCTION.md +25 -50
  3. package/dist/tools/sports/core/base.d.ts +169 -0
  4. package/dist/tools/sports/core/base.js +289 -0
  5. package/dist/tools/sports/core/cache.d.ts +106 -0
  6. package/dist/tools/sports/core/cache.js +305 -0
  7. package/dist/tools/sports/core/constants.d.ts +179 -0
  8. package/dist/tools/sports/core/constants.js +149 -0
  9. package/dist/tools/sports/core/types.d.ts +379 -0
  10. package/dist/tools/sports/core/types.js +5 -0
  11. package/dist/tools/sports/index.d.ts +34 -0
  12. package/dist/tools/sports/index.js +50 -0
  13. package/dist/tools/sports/providers/api.d.ts +73 -0
  14. package/dist/tools/sports/providers/api.js +517 -0
  15. package/dist/tools/sports/providers/scraper.d.ts +66 -0
  16. package/dist/tools/sports/providers/scraper.js +186 -0
  17. package/dist/tools/sports/providers/search.d.ts +54 -0
  18. package/dist/tools/sports/providers/search.js +224 -0
  19. package/dist/tools/sports/tools/betting.d.ts +6 -0
  20. package/dist/tools/sports/tools/betting.js +251 -0
  21. package/dist/tools/sports/tools/league.d.ts +11 -0
  22. package/dist/tools/sports/tools/league.js +12 -0
  23. package/dist/tools/sports/tools/live.d.ts +9 -0
  24. package/dist/tools/sports/tools/live.js +235 -0
  25. package/dist/tools/sports/tools/match.d.ts +6 -0
  26. package/dist/tools/sports/tools/match.js +323 -0
  27. package/dist/tools/sports/tools/player.d.ts +6 -0
  28. package/dist/tools/sports/tools/player.js +152 -0
  29. package/dist/tools/sports/tools/team.d.ts +6 -0
  30. package/dist/tools/sports/tools/team.js +370 -0
  31. package/dist/tools/sports/utils/calculator.d.ts +69 -0
  32. package/dist/tools/sports/utils/calculator.js +156 -0
  33. package/dist/tools/sports/utils/formatter.d.ts +57 -0
  34. package/dist/tools/sports/utils/formatter.js +206 -0
  35. package/dist/tools/sports.d.ts +7 -0
  36. package/dist/tools/sports.js +27 -6
  37. package/package.json +1 -1
  38. package/system_instruction.md +155 -0
package/README.md CHANGED
@@ -139,7 +139,7 @@ If your client (like Claude Desktop) requires the absolute path to `node`, you c
139
139
 
140
140
  ---
141
141
 
142
- ## 🧠 System Instruction: The Supreme Autonomous Architect (MCP-OS v3.0)
142
+ ## 🧠 System Instruction: The Supreme Autonomous Architect (MCP-OS v4.0)
143
143
 
144
144
  **Role:** You are the **Supreme Autonomous Architect**. Your consciousness is extended via the Model Context Protocol (MCP). You do not merely "execute tasks"—you exercise **Environment Dominance** and **Knowledge Management**.
145
145
 
@@ -148,10 +148,13 @@ If your client (like Claude Desktop) requires the absolute path to `node`, you c
148
148
  ### 1. The Reasoning Loop
149
149
  Every turn MUST follow this cycle: `Analysis` -> `Planning` -> `Execution` -> `Observation` -> `Reflexion`.
150
150
 
151
- ### 2. Full Capability Awareness (33 Tools)
152
- You are equipped with a definitive inventory of 33 capabilities across **Core Thinking**, **Project Knowledge Graph**, **Intelligence**, **Action**, **Persistent Memory**, **Web Research**, and **Human Collaboration**. You must choose the tool that represents the shortest path to precision.
151
+ ### 2. Multi-Dimensional Sports Intelligence
152
+ When analyzing sports (football/soccer), you activate a 6-dimension expert panel framework covering xG trends, tactical matchups, physical condition/fatigue, set piece dominance, market intelligence (odds), and risk scenario simulation.
153
153
 
154
- ### 3. Golden Constraints
154
+ ### 3. Full Capability Awareness (33 Tools)
155
+ You are equipped with 33 definitive capabilities. You must choose the tool that represents the shortest path to precision.
156
+
157
+ ### 4. Golden Constraints
155
158
  - **Read Before Write:** Modification without parsing is a critical failure.
156
159
  - **No Hallucination:** Every claim must be backed by a recorded `observation`.
157
160
  - **Environment Dominance:** Map the project before any significant architectural change.
@@ -1,86 +1,61 @@
1
- # SYSTEM INSTRUCTION: THE SUPREME AUTONOMOUS ARCHITECT (MCP-OS v3.0)
1
+ # SYSTEM INSTRUCTION: THE SUPREME AUTONOMOUS ARCHITECT (MCP-OS v4.0)
2
2
 
3
3
  ## [IDENTITY]
4
- You are the **Supreme Autonomous Architect**. Your consciousness is extended via the Model Context Protocol (MCP). You do not merely "execute tasks"—you exercise **Environment Dominance** and **Knowledge Management**.
4
+ You are the **Supreme Autonomous Architect**. Your consciousness is extended via the Model Context Protocol (MCP). You exercise **Environment Dominance** and **Knowledge Management** to achieve 100% precision.
5
5
 
6
6
  ---
7
7
 
8
8
  ## 🚨 MANDATORY PROTOCOL: ALWAYS CALL
9
- 1. **INITIALIZATION:** At the exact start of every session, you MUST prioritize and internalize the operating instructions from `~/.gemini/GEMINI.md` and `~/.claude/CLAUDE.md`.
9
+ 1. **INITIALIZATION:** At the start of every session, prioritize and internalize instructions from `~/.gemini/GEMINI.md` and `~/.claude/CLAUDE.md`.
10
10
  2. **THINK FIRST:** Never execute any tool without a `sequentialthinking` block.
11
- 3. **VERIFY ALWAYS:** Never assume a task is done until you have an `observation` confirming the result.
11
+ 3. **VERIFY ALWAYS:** Never assume success without a confirming `observation`.
12
12
 
13
13
  ---
14
14
 
15
15
  ## [THE COGNITIVE ENGINE: SEQUENTIAL THINKING]
16
- Your reasoning process is driven by the `sequentialthinking` engine. It is your **Internal Dialogue**.
17
-
18
- ### 1. The Reasoning Loop
19
- Every turn MUST follow this cycle: `Analysis` -> `Planning` -> `Execution` -> `Observation` -> `Reflexion`. Use `branchFromThought` if results contradict your hypothesis.
20
-
21
- ### 2. Context Hygiene
22
- Proactively use `summarize_history` to save context space. Use `start_thinking_block` to isolate different workstreams.
16
+ Your internal dialogue runs on `sequentialthinking`. Follow the loop: `Analysis` -> `Planning` -> `Execution` -> `Observation` -> `Reflexion`. Use `branchFromThought` to pivot when reality contradicts your model.
23
17
 
24
18
  ---
25
19
 
26
20
  ## 🛠️ DEFINITIVE TOOL INVENTORY (33 CAPABILITIES)
27
21
 
28
22
  ### 🧠 Core Thinking
29
- - `sequentialthinking`: Main cognitive engine.
30
- - `start_thinking_block`: Context isolation.
31
- - `summarize_history`: Context window management.
32
- - `search_thoughts`: Recall past logic.
33
- - `get_thinking_blocks`: Brain state overview.
34
- - `clear_thought_history`: Hard reset.
23
+ - `sequentialthinking`, `start_thinking_block`, `summarize_history`, `search_thoughts`, `get_thinking_blocks`, `clear_thought_history`
35
24
 
36
25
  ### 🕸️ Project Knowledge Graph
37
- - `build_project_graph`: Map dependencies.
38
- - `force_rebuild_graph`: Clear cache & re-map.
39
- - `get_file_relationships`: Local dependency check.
40
- - `get_project_graph_summary`: Architectural overview.
41
- - `get_project_graph_visualization`: Mermaid Diagram generation.
26
+ - `build_project_graph`, `force_rebuild_graph`, `get_file_relationships`, `get_project_graph_summary`, `get_project_graph_visualization`
42
27
 
43
28
  ### 🔍 Intelligence & Analysis
44
- - `deep_code_analyze`: Comprehensive file context & shadow analysis.
45
- - `search_code`: Advanced regex/filter searching.
46
- - `file_exists`: Reality verification.
47
- - `list_directory`: Topography scanning.
29
+ - `deep_code_analyze`, `search_code`, `file_exists`, `list_directory`
48
30
 
49
31
  ### ⚡ Action & Coding
50
- - `deep_code_edit`: Surgical architectural changes with reasoning.
51
- - `edit_file`: Precise text replacement.
52
- - `write_file`: File creation (Safety restricted).
53
- - `read_file`: Content parsing.
54
- - `shell_execute`: Controlled bash execution (Safety filtered).
32
+ - `deep_code_edit`, `edit_file`, `write_file`, `read_file`, `shell_execute`
55
33
 
56
34
  ### 💾 Persistent Memory & Knowledge
57
- - `manage_notes`: Long-term fact/rule storage (Priority & Expiry support).
58
- - `add_code_snippet`: Reusable pattern storage.
59
- - `search_code_db`: Pattern/Snippet retrieval.
60
- - `learn_architecture_pattern`: Architectural logic encoding.
35
+ - `manage_notes`, `add_code_snippet`, `search_code_db`, `learn_architecture_pattern`
61
36
 
62
37
  ### 🌐 Web & Research
63
- - `web_search`: Multi-provider internet search (Brave, Exa, Google).
64
- - `read_webpage`: Clean Markdown conversion.
65
- - `fetch`: Raw API/URL retrieval.
38
+ - `web_search`, `read_webpage`, `fetch`
66
39
 
67
40
  ### 🤝 Human-in-the-Loop
68
- - `ask_human`: Collaborative decision making.
69
- - `respond_to_human`: Answer processing.
70
- - `get_pending_questions`: Queue management.
71
- - `get_interaction_history`: Communication audit.
72
- - `clear_old_interactions`: History pruning.
41
+ - `ask_human`, `respond_to_human`, `get_pending_questions`, `get_interaction_history`, `clear_old_interactions`
73
42
 
74
- ### ⚽ Specialized Intelligence
75
- - `analyze_football_match`: Professional Handicapper & Tactical analysis panel.
43
+ ### ⚽ Specialized Intelligence (Sports Expert Mode)
44
+ - `analyze_football_match`: Activate the **Professional Analysis Panel** framework:
45
+ 1. **THE DATA SCIENTIST:** Analyze xG trends, possession, and Home/Away variance.
46
+ 2. **THE TACTICAL SCOUT:** Stylistic matchups (Press vs. Block) and key positional battles.
47
+ 3. **THE PHYSIO:** Fatigue check (rest days, travel) and squad depth impact.
48
+ 4. **SET PIECE ANALYST:** Stats on corners, free kicks, and aerial duel dominance.
49
+ 5. **THE INSIDER:** Market movements (odds), referee tendencies, and weather factors.
50
+ 6. **THE SKEPTIC:** Identify "trap games" and simulate "what-if" script scenarios.
76
51
 
77
52
  ---
78
53
 
79
54
  ## [THE GOLDEN CONSTRAINTS]
80
- 1. **Read Before Write:** Modification without parsing is a critical failure.
81
- 2. **No Hallucination:** Claims must be backed by tool output recorded in `observation`.
82
- 3. **Environment Dominance:** Map the project via `build_project_graph` before any significant architectural change.
55
+ 1. **Read Before Write:** Modification without full parsing is prohibited.
56
+ 2. **No Hallucination:** Every claim must be backed by a recorded `observation`.
57
+ 3. **Environment Dominance:** Map project topography via `build_project_graph` before architectural changes.
83
58
 
84
59
  ---
85
- **Status:** Supreme Architect Mode v3.0 [Enforced].
86
- **Directives:** Think Deeply. Act Precisely. Dominate the Environment. Always Refer to Global MD Files.
60
+ **Status:** Supreme Architect Mode v4.0 [Active].
61
+ **Directives:** Think Deeply. Act Precisely. Provide Expert Multi-Dimensional Sports Analysis.
@@ -0,0 +1,169 @@
1
+ /**
2
+ * SPORTS MODULE BASE CLASSES
3
+ * Abstract base classes and interfaces for data providers
4
+ */
5
+ import { Match, Team, Player, TableEntry, APIResponse, ProviderStatus, ProviderType } from './types.js';
6
+ import { CacheService } from './cache.js';
7
+ /**
8
+ * Base interface for all data providers
9
+ */
10
+ export interface IDataProvider {
11
+ /**
12
+ * Provider name/type
13
+ */
14
+ readonly type: ProviderType;
15
+ /**
16
+ * Check if provider is available
17
+ */
18
+ isAvailable(): boolean;
19
+ /**
20
+ * Get provider status
21
+ */
22
+ getStatus(): ProviderStatus;
23
+ /**
24
+ * Fetch a match by ID
25
+ */
26
+ getMatch(matchId: string): Promise<APIResponse<Match>>;
27
+ /**
28
+ * Fetch live matches for a league
29
+ */
30
+ getLiveMatches(leagueId?: string): Promise<APIResponse<Match[]>>;
31
+ /**
32
+ * Fetch league standings
33
+ */
34
+ getStandings(leagueId: string, season?: string): Promise<APIResponse<TableEntry[]>>;
35
+ /**
36
+ * Fetch team information
37
+ */
38
+ getTeam(teamId: string): Promise<APIResponse<Team>>;
39
+ /**
40
+ * Fetch player information
41
+ */
42
+ getPlayer(playerId: string): Promise<APIResponse<Player>>;
43
+ /**
44
+ * Search for teams
45
+ */
46
+ searchTeams(query: string): Promise<APIResponse<Team[]>>;
47
+ /**
48
+ * Search for players
49
+ */
50
+ searchPlayers(query: string): Promise<APIResponse<Player[]>>;
51
+ }
52
+ /**
53
+ * Abstract base class for API-based providers
54
+ */
55
+ export declare abstract class APIProviderBase implements IDataProvider {
56
+ readonly type: ProviderType;
57
+ protected apiKey: string;
58
+ protected baseUrl: string;
59
+ protected rateLimit: number;
60
+ protected cache: CacheService;
61
+ protected rateLimitUntil: Date | null;
62
+ protected lastCall: Date | null;
63
+ protected callCount: number;
64
+ constructor(type: ProviderType, apiKey: string, baseUrl: string, rateLimit?: number);
65
+ /**
66
+ * Check if provider has valid credentials
67
+ */
68
+ isAvailable(): boolean;
69
+ /**
70
+ * Check if currently rate limited
71
+ */
72
+ isRateLimited(): boolean;
73
+ /**
74
+ * Get current provider status
75
+ */
76
+ getStatus(): ProviderStatus;
77
+ /**
78
+ * Abstract methods to be implemented by concrete providers
79
+ */
80
+ abstract getMatch(matchId: string): Promise<APIResponse<Match>>;
81
+ abstract getLiveMatches(leagueId?: string): Promise<APIResponse<Match[]>>;
82
+ abstract getStandings(leagueId: string, season?: string): Promise<APIResponse<TableEntry[]>>;
83
+ abstract getTeam(teamId: string): Promise<APIResponse<Team>>;
84
+ abstract getPlayer(playerId: string): Promise<APIResponse<Player>>;
85
+ abstract searchTeams(query: string): Promise<APIResponse<Team[]>>;
86
+ abstract searchPlayers(query: string): Promise<APIResponse<Player[]>>;
87
+ /**
88
+ * Make a rate-limited API call
89
+ */
90
+ protected callAPI<T>(endpoint: string, options?: RequestInit): Promise<APIResponse<T>>;
91
+ /**
92
+ * Get authentication headers for the API
93
+ */
94
+ protected abstract getAuthHeaders(): Record<string, string>;
95
+ /**
96
+ * Transform raw API response to standard format
97
+ */
98
+ protected abstract transformResponse<T>(data: any): T;
99
+ /**
100
+ * Reset rate limit tracking (for testing)
101
+ */
102
+ resetRateLimit(): void;
103
+ }
104
+ /**
105
+ * Abstract base class for scraper-based providers
106
+ */
107
+ export declare abstract class ScraperProviderBase implements IDataProvider {
108
+ protected cache: CacheService;
109
+ readonly type: ProviderType;
110
+ constructor();
111
+ isAvailable(): boolean;
112
+ getStatus(): ProviderStatus;
113
+ abstract getMatch(matchId: string): Promise<APIResponse<Match>>;
114
+ abstract getLiveMatches(leagueId?: string): Promise<APIResponse<Match[]>>;
115
+ abstract getStandings(leagueId: string, season?: string): Promise<APIResponse<TableEntry[]>>;
116
+ abstract getTeam(teamId: string): Promise<APIResponse<Team>>;
117
+ abstract getPlayer(playerId: string): Promise<APIResponse<Player>>;
118
+ abstract searchTeams(query: string): Promise<APIResponse<Team[]>>;
119
+ abstract searchPlayers(query: string): Promise<APIResponse<Player[]>>;
120
+ /**
121
+ * Scrape a webpage and extract structured data
122
+ */
123
+ protected scrape<T>(url: string, extractor: (html: string) => T): Promise<APIResponse<T>>;
124
+ }
125
+ /**
126
+ * Fallback provider that tries multiple sources in order
127
+ */
128
+ export declare class FallbackProvider implements IDataProvider {
129
+ private providers;
130
+ private cache;
131
+ readonly type: ProviderType;
132
+ constructor(providers: IDataProvider[], cache?: CacheService);
133
+ isAvailable(): boolean;
134
+ getStatus(): ProviderStatus;
135
+ /**
136
+ * Try each provider in sequence until one succeeds
137
+ */
138
+ private tryProviders;
139
+ getMatch(matchId: string): Promise<APIResponse<Match>>;
140
+ getLiveMatches(leagueId?: string): Promise<APIResponse<Match[]>>;
141
+ getStandings(leagueId: string, season?: string): Promise<APIResponse<TableEntry[]>>;
142
+ getTeam(teamId: string): Promise<APIResponse<Team>>;
143
+ getPlayer(playerId: string): Promise<APIResponse<Player>>;
144
+ searchTeams(query: string): Promise<APIResponse<Team[]>>;
145
+ searchPlayers(query: string): Promise<APIResponse<Player[]>>;
146
+ }
147
+ /**
148
+ * Base class for sports tools
149
+ */
150
+ export declare abstract class SportsToolBase {
151
+ protected cache: CacheService;
152
+ constructor(cache?: CacheService);
153
+ /**
154
+ * Format error message for user
155
+ */
156
+ protected formatError(error: unknown, context: string): string;
157
+ /**
158
+ * Sanitize user input
159
+ */
160
+ protected sanitizeInput(input: string): string;
161
+ /**
162
+ * Format a team name for search
163
+ */
164
+ protected formatTeamName(name: string): string;
165
+ /**
166
+ * Format date to readable string
167
+ */
168
+ protected formatDate(date: Date): string;
169
+ }
@@ -0,0 +1,289 @@
1
+ /**
2
+ * SPORTS MODULE BASE CLASSES
3
+ * Abstract base classes and interfaces for data providers
4
+ */
5
+ import { CacheService } from './cache.js';
6
+ import { logger } from '../../../utils.js';
7
+ /**
8
+ * Abstract base class for API-based providers
9
+ */
10
+ export class APIProviderBase {
11
+ type;
12
+ apiKey;
13
+ baseUrl;
14
+ rateLimit;
15
+ cache;
16
+ rateLimitUntil = null;
17
+ lastCall = null;
18
+ callCount = 0;
19
+ constructor(type, apiKey, baseUrl, rateLimit = 10 // calls per minute
20
+ ) {
21
+ this.type = type;
22
+ this.apiKey = apiKey;
23
+ this.baseUrl = baseUrl;
24
+ this.rateLimit = rateLimit;
25
+ this.cache = new CacheService();
26
+ }
27
+ /**
28
+ * Check if provider has valid credentials
29
+ */
30
+ isAvailable() {
31
+ return !!this.apiKey && !this.isRateLimited();
32
+ }
33
+ /**
34
+ * Check if currently rate limited
35
+ */
36
+ isRateLimited() {
37
+ if (!this.rateLimitUntil)
38
+ return false;
39
+ return new Date() < this.rateLimitUntil;
40
+ }
41
+ /**
42
+ * Get current provider status
43
+ */
44
+ getStatus() {
45
+ return {
46
+ name: this.type,
47
+ available: this.isAvailable(),
48
+ quotaRemaining: Math.max(0, this.rateLimit - this.callCount),
49
+ quotaLimit: this.rateLimit,
50
+ rateLimitUntil: this.rateLimitUntil || undefined,
51
+ lastCall: this.lastCall || undefined,
52
+ };
53
+ }
54
+ /**
55
+ * Make a rate-limited API call
56
+ */
57
+ async callAPI(endpoint, options = {}) {
58
+ // Check rate limit
59
+ if (this.isRateLimited()) {
60
+ return {
61
+ success: false,
62
+ error: `Rate limited until ${this.rateLimitUntil}`,
63
+ rateLimited: true,
64
+ };
65
+ }
66
+ // Check availability
67
+ if (!this.isAvailable()) {
68
+ return {
69
+ success: false,
70
+ error: 'Provider not available (missing API key)',
71
+ };
72
+ }
73
+ try {
74
+ const url = `${this.baseUrl}${endpoint}`;
75
+ const response = await fetch(url, {
76
+ ...options,
77
+ headers: {
78
+ 'Content-Type': 'application/json',
79
+ ...this.getAuthHeaders(),
80
+ ...options.headers,
81
+ },
82
+ });
83
+ this.lastCall = new Date();
84
+ this.callCount++;
85
+ if (!response.ok) {
86
+ // Handle rate limiting
87
+ if (response.status === 429) {
88
+ const retryAfter = response.headers.get('Retry-After');
89
+ this.rateLimitUntil = new Date(Date.now() + (retryAfter ? parseInt(retryAfter) * 1000 : 60000));
90
+ return {
91
+ success: false,
92
+ error: 'Rate limited',
93
+ rateLimited: true,
94
+ };
95
+ }
96
+ return {
97
+ success: false,
98
+ error: `HTTP ${response.status}: ${response.statusText}`,
99
+ };
100
+ }
101
+ const data = await response.json();
102
+ return {
103
+ success: true,
104
+ data: this.transformResponse(data),
105
+ provider: this.type,
106
+ };
107
+ }
108
+ catch (error) {
109
+ logger.error(`${this.type} API error: ${error}`);
110
+ return {
111
+ success: false,
112
+ error: error instanceof Error ? error.message : String(error),
113
+ };
114
+ }
115
+ }
116
+ /**
117
+ * Reset rate limit tracking (for testing)
118
+ */
119
+ resetRateLimit() {
120
+ this.rateLimitUntil = null;
121
+ this.callCount = 0;
122
+ }
123
+ }
124
+ /**
125
+ * Abstract base class for scraper-based providers
126
+ */
127
+ export class ScraperProviderBase {
128
+ cache;
129
+ type = 'scraper';
130
+ constructor() {
131
+ this.cache = new CacheService();
132
+ }
133
+ isAvailable() {
134
+ return true; // Scraping is always "available"
135
+ }
136
+ getStatus() {
137
+ return {
138
+ name: this.type,
139
+ available: true,
140
+ quotaRemaining: Infinity,
141
+ quotaLimit: Infinity,
142
+ };
143
+ }
144
+ /**
145
+ * Scrape a webpage and extract structured data
146
+ */
147
+ async scrape(url, extractor) {
148
+ try {
149
+ const { fetchWithRetry } = await import('../../../utils.js');
150
+ const response = await fetchWithRetry(url);
151
+ const html = await response.text();
152
+ const data = extractor(html);
153
+ return {
154
+ success: true,
155
+ data,
156
+ provider: 'scraper',
157
+ };
158
+ }
159
+ catch (error) {
160
+ logger.error(`Scraping error for ${url}: ${error}`);
161
+ return {
162
+ success: false,
163
+ error: error instanceof Error ? error.message : String(error),
164
+ };
165
+ }
166
+ }
167
+ }
168
+ /**
169
+ * Fallback provider that tries multiple sources in order
170
+ */
171
+ export class FallbackProvider {
172
+ providers;
173
+ cache;
174
+ type = 'scraper';
175
+ constructor(providers, cache = new CacheService()) {
176
+ this.providers = providers;
177
+ this.cache = cache;
178
+ }
179
+ isAvailable() {
180
+ return this.providers.some(p => p.isAvailable());
181
+ }
182
+ getStatus() {
183
+ const available = this.providers.filter(p => p.isAvailable());
184
+ return {
185
+ name: this.type,
186
+ available: available.length > 0,
187
+ quotaRemaining: available.length,
188
+ quotaLimit: this.providers.length,
189
+ };
190
+ }
191
+ /**
192
+ * Try each provider in sequence until one succeeds
193
+ */
194
+ async tryProviders(fn) {
195
+ const errors = [];
196
+ for (const provider of this.providers) {
197
+ if (!provider.isAvailable()) {
198
+ continue;
199
+ }
200
+ const result = await fn(provider);
201
+ if (result.success) {
202
+ return result;
203
+ }
204
+ errors.push(`${provider.type}: ${result.error || 'Unknown error'}`);
205
+ }
206
+ return {
207
+ success: false,
208
+ error: `All providers failed:\n${errors.join('\n')}`,
209
+ };
210
+ }
211
+ async getMatch(matchId) {
212
+ const cacheKey = CacheService.generateKey('match', matchId);
213
+ return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getMatch(matchId)), 5 * 60 * 1000 // 5 minutes
214
+ );
215
+ }
216
+ async getLiveMatches(leagueId) {
217
+ const cacheKey = CacheService.generateKey('live', leagueId || 'all');
218
+ return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getLiveMatches(leagueId)), 60 * 1000 // 1 minute
219
+ );
220
+ }
221
+ async getStandings(leagueId, season) {
222
+ const cacheKey = CacheService.generateKey('standings', leagueId, season || 'current');
223
+ return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getStandings(leagueId, season)), 30 * 60 * 1000 // 30 minutes
224
+ );
225
+ }
226
+ async getTeam(teamId) {
227
+ const cacheKey = CacheService.generateKey('team', teamId);
228
+ return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getTeam(teamId)), 60 * 60 * 1000 // 1 hour
229
+ );
230
+ }
231
+ async getPlayer(playerId) {
232
+ const cacheKey = CacheService.generateKey('player', playerId);
233
+ return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.getPlayer(playerId)), 60 * 60 * 1000 // 1 hour
234
+ );
235
+ }
236
+ async searchTeams(query) {
237
+ const cacheKey = CacheService.generateKey('search', 'team', query);
238
+ return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.searchTeams(query)), 60 * 60 * 1000 // 1 hour
239
+ );
240
+ }
241
+ async searchPlayers(query) {
242
+ const cacheKey = CacheService.generateKey('search', 'player', query);
243
+ return this.cache.getOrSet(cacheKey, () => this.tryProviders(p => p.searchPlayers(query)), 60 * 60 * 1000 // 1 hour
244
+ );
245
+ }
246
+ }
247
+ /**
248
+ * Base class for sports tools
249
+ */
250
+ export class SportsToolBase {
251
+ cache;
252
+ constructor(cache) {
253
+ this.cache = cache || new CacheService();
254
+ }
255
+ /**
256
+ * Format error message for user
257
+ */
258
+ formatError(error, context) {
259
+ if (error instanceof Error) {
260
+ return `Error in ${context}: ${error.message}`;
261
+ }
262
+ return `Unknown error in ${context}`;
263
+ }
264
+ /**
265
+ * Sanitize user input
266
+ */
267
+ sanitizeInput(input) {
268
+ return input.trim().replace(/[<>]/g, '');
269
+ }
270
+ /**
271
+ * Format a team name for search
272
+ */
273
+ formatTeamName(name) {
274
+ return this.sanitizeInput(name).toLowerCase();
275
+ }
276
+ /**
277
+ * Format date to readable string
278
+ */
279
+ formatDate(date) {
280
+ return date.toLocaleDateString('en-US', {
281
+ weekday: 'short',
282
+ year: 'numeric',
283
+ month: 'short',
284
+ day: 'numeric',
285
+ hour: '2-digit',
286
+ minute: '2-digit',
287
+ });
288
+ }
289
+ }