@gotza02/sequential-thinking 2026.2.32 → 2026.2.33
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/dist/index.js +2 -0
- package/dist/tools/sports.d.ts +2 -0
- package/dist/tools/sports.js +133 -0
- package/dist/tools/web.d.ts +1 -0
- package/dist/tools/web.js +52 -48
- package/dist/verify_all_tools.d.ts +1 -0
- package/dist/verify_all_tools.js +129 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { registerNoteTools } from './tools/notes.js';
|
|
|
16
16
|
import { registerCodingTools } from './tools/coding.js';
|
|
17
17
|
import { registerCodeDbTools } from './tools/codestore.js';
|
|
18
18
|
import { registerHumanTools } from './tools/human.js';
|
|
19
|
+
import { registerSportsTools } from './tools/sports.js';
|
|
19
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
21
|
const __dirname = dirname(__filename);
|
|
21
22
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
@@ -36,6 +37,7 @@ registerNoteTools(server, notesManager);
|
|
|
36
37
|
registerCodingTools(server, knowledgeGraph);
|
|
37
38
|
registerCodeDbTools(server, codeDb);
|
|
38
39
|
registerHumanTools(server);
|
|
40
|
+
registerSportsTools(server);
|
|
39
41
|
async function runServer() {
|
|
40
42
|
const transport = new StdioServerTransport();
|
|
41
43
|
await server.connect(transport);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fetchWithRetry } from "../utils.js";
|
|
3
|
+
import { scrapeWebpage } from "./web.js";
|
|
4
|
+
export function registerSportsTools(server) {
|
|
5
|
+
// 19. analyze_football_match
|
|
6
|
+
server.tool("analyze_football_match", "Gather comprehensive data for a football (soccer) match to perform a detailed analysis. Fetches Head-to-Head, Recent Form, Team News, and Predictions from the web. It also performs a 'Deep Dive' on the best data source found.", {
|
|
7
|
+
homeTeam: z.string().describe("Name of the home team"),
|
|
8
|
+
awayTeam: z.string().describe("Name of the away team"),
|
|
9
|
+
league: z.string().optional().describe("League name (optional, helps with accuracy)"),
|
|
10
|
+
context: z.string().optional().describe("Any specific angle to focus on (e.g., 'betting', 'tactical', 'injury news')")
|
|
11
|
+
}, async ({ homeTeam, awayTeam, league, context }) => {
|
|
12
|
+
const errors = [];
|
|
13
|
+
// Identify available search provider
|
|
14
|
+
let searchProvider = null;
|
|
15
|
+
if (process.env.BRAVE_API_KEY)
|
|
16
|
+
searchProvider = 'brave';
|
|
17
|
+
else if (process.env.EXA_API_KEY)
|
|
18
|
+
searchProvider = 'exa';
|
|
19
|
+
else if (process.env.GOOGLE_SEARCH_API_KEY && process.env.GOOGLE_SEARCH_CX)
|
|
20
|
+
searchProvider = 'google';
|
|
21
|
+
if (!searchProvider) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: "Error: No search provider configured (BRAVE_API_KEY, EXA_API_KEY, or GOOGLE_SEARCH_API_KEY). Cannot fetch live match data." }],
|
|
24
|
+
isError: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const leagueStr = league ? `${league} ` : '';
|
|
28
|
+
const baseQuery = `${leagueStr}${homeTeam} vs ${awayTeam}`;
|
|
29
|
+
// Critical: Add date context to ensure freshness (Accuracy Upgrade)
|
|
30
|
+
const today = new Date().toLocaleDateString('en-US', { month: 'long', year: 'numeric' }); // e.g., "January 2025"
|
|
31
|
+
const dateQuery = ` ${today}`;
|
|
32
|
+
// Define targeted queries for "Detailed" analysis
|
|
33
|
+
const queries = [
|
|
34
|
+
{ type: 'General & Prediction', query: `${baseQuery} match prediction stats${dateQuery}` },
|
|
35
|
+
{ type: 'Head to Head', query: `${baseQuery} head to head history` },
|
|
36
|
+
{ type: 'Recent Form', query: `${homeTeam} ${awayTeam} recent form last 5 matches results${dateQuery}` },
|
|
37
|
+
{ type: 'Team News & Lineups', query: `${baseQuery} team news injuries suspensions predicted confirmed lineups${dateQuery}` },
|
|
38
|
+
{ type: 'Advanced Metrics', query: `${baseQuery} xG expected goals stats possession shots per game${dateQuery}` },
|
|
39
|
+
// Combine Manager/Press with "Sack Race/Pressure" context
|
|
40
|
+
{ type: 'Manager & Atmosphere', query: `${homeTeam} ${awayTeam} manager quotes fan sentiment sack race pressure${dateQuery}` },
|
|
41
|
+
// NEW: Market Intelligence
|
|
42
|
+
{ type: 'Market & Odds', query: `${baseQuery} odds movement dropping odds betting trends${dateQuery}` },
|
|
43
|
+
// NEW: Environmental Factors
|
|
44
|
+
{ type: 'Ref & Weather', query: `${baseQuery} referee stats cards per game weather forecast stadium${dateQuery}` },
|
|
45
|
+
// NEW: Fatigue & Schedule (Critical for late game performance)
|
|
46
|
+
{ type: 'Fatigue & Schedule', query: `${homeTeam} ${awayTeam} days rest fixture congestion travel distance${dateQuery}` },
|
|
47
|
+
// NEW: Set Piece Analysis (Where 30% of goals come from)
|
|
48
|
+
{ type: 'Set Pieces', query: `${homeTeam} ${awayTeam} set piece goals scored conceded corners stats aerial duels${dateQuery}` }
|
|
49
|
+
];
|
|
50
|
+
if (context) {
|
|
51
|
+
queries.push({ type: 'Specific Context', query: `${baseQuery} ${context} ${today}` });
|
|
52
|
+
}
|
|
53
|
+
let combinedResults = `--- FOOTBALL MATCH DATA: ${homeTeam} vs ${awayTeam} ---\nMatch Context Date: ${today}\n\n`;
|
|
54
|
+
// Store potential URLs for deep dive
|
|
55
|
+
const candidateUrls = [];
|
|
56
|
+
// Execute searches in parallel
|
|
57
|
+
const searchPromises = queries.map(async (q) => {
|
|
58
|
+
try {
|
|
59
|
+
let resultsText = "";
|
|
60
|
+
if (searchProvider === 'brave') {
|
|
61
|
+
const response = await fetchWithRetry(`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(q.query)}&count=4`, {
|
|
62
|
+
headers: {
|
|
63
|
+
'X-Subscription-Token': process.env.BRAVE_API_KEY,
|
|
64
|
+
'Accept': 'application/json'
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if (!response.ok)
|
|
68
|
+
throw new Error(`Brave Status ${response.status}`);
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
const items = data.web?.results || [];
|
|
71
|
+
items.forEach((item) => candidateUrls.push(item.url));
|
|
72
|
+
resultsText = items.map((item) => `- [${item.title}](${item.url}): ${item.description}`).join('\n');
|
|
73
|
+
}
|
|
74
|
+
else if (searchProvider === 'exa') {
|
|
75
|
+
const response = await fetchWithRetry('https://api.exa.ai/search', {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: {
|
|
78
|
+
'x-api-key': process.env.EXA_API_KEY,
|
|
79
|
+
'Content-Type': 'application/json'
|
|
80
|
+
},
|
|
81
|
+
body: JSON.stringify({ query: q.query, numResults: 4 })
|
|
82
|
+
});
|
|
83
|
+
if (!response.ok)
|
|
84
|
+
throw new Error(`Exa Status ${response.status}`);
|
|
85
|
+
const data = await response.json();
|
|
86
|
+
const items = data.results || [];
|
|
87
|
+
items.forEach((item) => candidateUrls.push(item.url));
|
|
88
|
+
resultsText = items.map((item) => `- [${item.title}](${item.url}): ${item.text || item.title}`).join('\n');
|
|
89
|
+
}
|
|
90
|
+
else if (searchProvider === 'google') {
|
|
91
|
+
const response = await fetchWithRetry(`https://www.googleapis.com/customsearch/v1?key=${process.env.GOOGLE_SEARCH_API_KEY}&cx=${process.env.GOOGLE_SEARCH_CX}&q=${encodeURIComponent(q.query)}&num=4`);
|
|
92
|
+
if (!response.ok)
|
|
93
|
+
throw new Error(`Google Status ${response.status}`);
|
|
94
|
+
const data = await response.json();
|
|
95
|
+
const items = data.items || [];
|
|
96
|
+
items.forEach((item) => candidateUrls.push(item.link));
|
|
97
|
+
resultsText = items.map((item) => `- [${item.title}](${item.link}): ${item.snippet}`).join('\n');
|
|
98
|
+
}
|
|
99
|
+
return `### ${q.type}\n${resultsText}\n`;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return `### ${q.type} (Failed)\nError: ${error instanceof Error ? error.message : String(error)}\n`;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const results = await Promise.all(searchPromises);
|
|
106
|
+
combinedResults += results.join('\n');
|
|
107
|
+
// --- Deep Dive: Scrape the best URL ---
|
|
108
|
+
if (candidateUrls.length > 0) {
|
|
109
|
+
// Priority domains for sports data
|
|
110
|
+
const priorityDomains = ['whoscored.com', 'flashscore', 'sofascore', 'bbc.co.uk/sport', 'skysports.com', 'sportsmole', 'goal.com', 'understat.com', 'fbref.com'];
|
|
111
|
+
// Find first match from priority list, else take the first valid URL
|
|
112
|
+
const bestUrl = candidateUrls.find(url => priorityDomains.some(domain => url.includes(domain))) || candidateUrls[0];
|
|
113
|
+
if (bestUrl) {
|
|
114
|
+
combinedResults += `\n--- DEEP DIVE ANALYSIS (${bestUrl}) ---\n`;
|
|
115
|
+
try {
|
|
116
|
+
// Stability: Wrap scrape in a 15s timeout to prevent hanging
|
|
117
|
+
const scrapePromise = scrapeWebpage(bestUrl);
|
|
118
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Deep dive timeout (15s)")), 15000));
|
|
119
|
+
const scrapedContent = await Promise.race([scrapePromise, timeoutPromise]);
|
|
120
|
+
combinedResults += scrapedContent;
|
|
121
|
+
}
|
|
122
|
+
catch (scrapeError) {
|
|
123
|
+
combinedResults += `(Deep dive failed/skipped: ${scrapeError instanceof Error ? scrapeError.message : String(scrapeError)})`;
|
|
124
|
+
}
|
|
125
|
+
combinedResults += `\n--- END DEEP DIVE ---\n`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
combinedResults += `\n--- END DATA ---\n\nINSTRUCTIONS: Act as a World-Class Football Analysis Panel. Provide a deep, non-obvious analysis using this framework:\n\n1. 📊 THE DATA SCIENTIST (Quantitative):\n - Analyze xG trends & Possession stats.\n - Assess Home/Away variance.\n\n2. 🧠 THE TACTICAL SCOUT (Qualitative):\n - Stylistic Matchup (Press vs Block).\n - Key Battles.\n\n3. 🚑 THE PHYSIO (Physical Condition):\n - FATIGUE CHECK: Days rest? Travel distance? (Critical for last 20 mins).\n - Squad Depth: Who has the better bench?\n\n4. 🎯 SET PIECE ANALYST (Special Teams):\n - Corners/Free Kicks: Strong vs Weak? (Aerial Duel stats).\n - Who is most likely to score from a header?\n\n5. 💎 THE INSIDER (External Factors):\n - Market Movements (Odds dropping?).\n - Referee & Weather impact.\n - Managerial Pressure/Sack Race context.\n\n6. 🕵️ THE SKEPTIC & SCENARIOS:\n - Why might the favorite LOSE? (The "Trap").\n - Game Script: "If Team A scores first..."\n\n🏆 FINAL VERDICT:\n - Asian Handicap Leans\n - Goal Line (Over/Under)\n - The "Value Pick"`;
|
|
129
|
+
return {
|
|
130
|
+
content: [{ type: "text", text: combinedResults }]
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
}
|
package/dist/tools/web.d.ts
CHANGED
package/dist/tools/web.js
CHANGED
|
@@ -3,6 +3,56 @@ import { fetchWithRetry, validatePublicUrl } from "../utils.js";
|
|
|
3
3
|
import { JSDOM } from 'jsdom';
|
|
4
4
|
import { Readability } from '@mozilla/readability';
|
|
5
5
|
import TurndownService from 'turndown';
|
|
6
|
+
export async function scrapeWebpage(url) {
|
|
7
|
+
await validatePublicUrl(url);
|
|
8
|
+
const response = await fetchWithRetry(url);
|
|
9
|
+
const html = await response.text();
|
|
10
|
+
const doc = new JSDOM(html, { url });
|
|
11
|
+
const reader = new Readability(doc.window.document);
|
|
12
|
+
const article = reader.parse();
|
|
13
|
+
if (!article)
|
|
14
|
+
throw new Error("Could not parse article content");
|
|
15
|
+
const turndownService = new TurndownService({
|
|
16
|
+
headingStyle: 'atx',
|
|
17
|
+
codeBlockStyle: 'fenced'
|
|
18
|
+
});
|
|
19
|
+
// Custom Rule for GitHub Flavored Markdown Tables
|
|
20
|
+
turndownService.addRule('tables', {
|
|
21
|
+
filter: ['table'],
|
|
22
|
+
replacement: function (content, node) {
|
|
23
|
+
const rows = [];
|
|
24
|
+
const table = node;
|
|
25
|
+
const trs = Array.from(table.querySelectorAll('tr'));
|
|
26
|
+
trs.forEach((tr, index) => {
|
|
27
|
+
const cols = [];
|
|
28
|
+
const tds = Array.from(tr.querySelectorAll('th, td'));
|
|
29
|
+
tds.forEach(td => {
|
|
30
|
+
// Clean content: remove newlines and pipe characters
|
|
31
|
+
cols.push(td.textContent?.replace(/[\n\r]/g, ' ').replace(/\|/g, '\\|').trim() || "");
|
|
32
|
+
});
|
|
33
|
+
if (cols.length > 0) {
|
|
34
|
+
rows.push(`| ${cols.join(' | ')} |`);
|
|
35
|
+
// Add separator after header
|
|
36
|
+
if (index === 0 || tr.querySelector('th')) {
|
|
37
|
+
rows.push(`| ${cols.map(() => '---').join(' | ')} |`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
// Filter out duplicate separator lines if any
|
|
42
|
+
const uniqueRows = rows.filter((row, i) => {
|
|
43
|
+
if (row.includes('---') && rows[i - 1]?.includes('---'))
|
|
44
|
+
return false;
|
|
45
|
+
return true;
|
|
46
|
+
});
|
|
47
|
+
return '\n\n' + uniqueRows.join('\n') + '\n\n';
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
let markdown = turndownService.turndown(article.content || "");
|
|
51
|
+
if (markdown.length > 20000) {
|
|
52
|
+
markdown = markdown.substring(0, 20000) + "\n...(truncated)";
|
|
53
|
+
}
|
|
54
|
+
return `Title: ${article.title}\n\n${markdown}`;
|
|
55
|
+
}
|
|
6
56
|
export function registerWebTools(server) {
|
|
7
57
|
// 1. web_search
|
|
8
58
|
server.tool("web_search", "Search the web using Brave or Exa APIs (requires API keys in environment variables: BRAVE_API_KEY or EXA_API_KEY).", {
|
|
@@ -128,57 +178,11 @@ export function registerWebTools(server) {
|
|
|
128
178
|
url: z.string().url().describe("The URL to read")
|
|
129
179
|
}, async ({ url }) => {
|
|
130
180
|
try {
|
|
131
|
-
await
|
|
132
|
-
const response = await fetchWithRetry(url);
|
|
133
|
-
const html = await response.text();
|
|
134
|
-
const doc = new JSDOM(html, { url });
|
|
135
|
-
const reader = new Readability(doc.window.document);
|
|
136
|
-
const article = reader.parse();
|
|
137
|
-
if (!article)
|
|
138
|
-
throw new Error("Could not parse article content");
|
|
139
|
-
const turndownService = new TurndownService({
|
|
140
|
-
headingStyle: 'atx',
|
|
141
|
-
codeBlockStyle: 'fenced'
|
|
142
|
-
});
|
|
143
|
-
// Custom Rule for GitHub Flavored Markdown Tables
|
|
144
|
-
turndownService.addRule('tables', {
|
|
145
|
-
filter: ['table'],
|
|
146
|
-
replacement: function (content, node) {
|
|
147
|
-
const rows = [];
|
|
148
|
-
const table = node;
|
|
149
|
-
const trs = Array.from(table.querySelectorAll('tr'));
|
|
150
|
-
trs.forEach((tr, index) => {
|
|
151
|
-
const cols = [];
|
|
152
|
-
const tds = Array.from(tr.querySelectorAll('th, td'));
|
|
153
|
-
tds.forEach(td => {
|
|
154
|
-
// Clean content: remove newlines and pipe characters
|
|
155
|
-
cols.push(td.textContent?.replace(/[\n\r]/g, ' ').replace(/\|/g, '\\|').trim() || "");
|
|
156
|
-
});
|
|
157
|
-
if (cols.length > 0) {
|
|
158
|
-
rows.push(`| ${cols.join(' | ')} |`);
|
|
159
|
-
// Add separator after header
|
|
160
|
-
if (index === 0 || tr.querySelector('th')) {
|
|
161
|
-
rows.push(`| ${cols.map(() => '---').join(' | ')} |`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
// Filter out duplicate separator lines if any
|
|
166
|
-
const uniqueRows = rows.filter((row, i) => {
|
|
167
|
-
if (row.includes('---') && rows[i - 1]?.includes('---'))
|
|
168
|
-
return false;
|
|
169
|
-
return true;
|
|
170
|
-
});
|
|
171
|
-
return '\n\n' + uniqueRows.join('\n') + '\n\n';
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
let markdown = turndownService.turndown(article.content || "");
|
|
175
|
-
if (markdown.length > 20000) {
|
|
176
|
-
markdown = markdown.substring(0, 20000) + "\n...(truncated)";
|
|
177
|
-
}
|
|
181
|
+
const result = await scrapeWebpage(url);
|
|
178
182
|
return {
|
|
179
183
|
content: [{
|
|
180
184
|
type: "text",
|
|
181
|
-
text:
|
|
185
|
+
text: result
|
|
182
186
|
}]
|
|
183
187
|
};
|
|
184
188
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { SequentialThinkingServer } from './lib.js';
|
|
2
|
+
import { ProjectKnowledgeGraph } from './graph.js';
|
|
3
|
+
import { NotesManager } from './notes.js';
|
|
4
|
+
import { CodeDatabase } from './codestore.js';
|
|
5
|
+
import { fetchWithRetry } from './utils.js';
|
|
6
|
+
import * as fs from 'fs/promises';
|
|
7
|
+
// ANSI Colors
|
|
8
|
+
const colors = {
|
|
9
|
+
reset: "\x1b[0m",
|
|
10
|
+
green: "\x1b[32m",
|
|
11
|
+
blue: "\x1b[34m",
|
|
12
|
+
yellow: "\x1b[33m",
|
|
13
|
+
red: "\x1b[31m",
|
|
14
|
+
bold: "\x1b[1m"
|
|
15
|
+
};
|
|
16
|
+
function log(step, msg) {
|
|
17
|
+
console.log(`${colors.blue}[${step}]${colors.reset} ${msg}`);
|
|
18
|
+
}
|
|
19
|
+
function success(msg) {
|
|
20
|
+
console.log(`${colors.green} ✓ ${msg}${colors.reset}`);
|
|
21
|
+
}
|
|
22
|
+
async function main() {
|
|
23
|
+
console.log(`${colors.bold}=== STARTING AUTOMATED TOOL VERIFICATION ===${colors.reset}\n`);
|
|
24
|
+
// 1. Initialize Managers
|
|
25
|
+
log("INIT", "Initializing Core Managers...");
|
|
26
|
+
const thinking = new SequentialThinkingServer('test_thoughts.json', 0);
|
|
27
|
+
const graph = new ProjectKnowledgeGraph();
|
|
28
|
+
const notes = new NotesManager('test_notes.json');
|
|
29
|
+
const codeDb = new CodeDatabase('test_codedb.json');
|
|
30
|
+
// Clean up previous tests
|
|
31
|
+
try {
|
|
32
|
+
await Promise.all([
|
|
33
|
+
fs.unlink('test_thoughts.json').catch(() => { }),
|
|
34
|
+
fs.unlink('test_notes.json').catch(() => { }),
|
|
35
|
+
fs.unlink('test_codedb.json').catch(() => { })
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
catch { } // This catch block is intentionally empty
|
|
39
|
+
// 2. Test Thinking Engine
|
|
40
|
+
log("THINKING", "Testing Sequential Thinking Engine...");
|
|
41
|
+
await thinking.processThought({
|
|
42
|
+
thought: "Analysis step for testing",
|
|
43
|
+
thoughtNumber: 1,
|
|
44
|
+
totalThoughts: 3,
|
|
45
|
+
nextThoughtNeeded: true,
|
|
46
|
+
thoughtType: "analysis",
|
|
47
|
+
blockId: "test-block"
|
|
48
|
+
});
|
|
49
|
+
await thinking.processThought({
|
|
50
|
+
thought: "Planning step",
|
|
51
|
+
thoughtNumber: 2,
|
|
52
|
+
totalThoughts: 3,
|
|
53
|
+
nextThoughtNeeded: true,
|
|
54
|
+
thoughtType: "planning",
|
|
55
|
+
blockId: "test-block"
|
|
56
|
+
});
|
|
57
|
+
const history = await thinking.searchHistory("Analysis");
|
|
58
|
+
if (history.length > 0)
|
|
59
|
+
success(`History recorded and retrieved (${history.length} items)`);
|
|
60
|
+
else
|
|
61
|
+
throw new Error("Thinking history failed");
|
|
62
|
+
// 3. Test Notes Manager
|
|
63
|
+
log("NOTES", "Testing Notes Manager...");
|
|
64
|
+
const note = await notes.addNote("Test Note", "This is a test content", ["test"], "high");
|
|
65
|
+
success(`Note created: ${note.id}`);
|
|
66
|
+
const foundNotes = await notes.searchNotes("test content");
|
|
67
|
+
if (foundNotes.length > 0)
|
|
68
|
+
success(`Search successful (found ${foundNotes.length})`);
|
|
69
|
+
else
|
|
70
|
+
throw new Error("Note search failed");
|
|
71
|
+
await notes.deleteNote(note.id);
|
|
72
|
+
success("Note deleted");
|
|
73
|
+
// 4. Test Code Database
|
|
74
|
+
log("CODEDB", "Testing Code Database...");
|
|
75
|
+
const snippet = await codeDb.addSnippet({
|
|
76
|
+
title: "Hello World",
|
|
77
|
+
code: "console.log('Hello');",
|
|
78
|
+
language: "typescript",
|
|
79
|
+
description: "Simple log",
|
|
80
|
+
tags: ["example"],
|
|
81
|
+
context: "test.ts"
|
|
82
|
+
});
|
|
83
|
+
success(`Snippet added: ${snippet.id}`);
|
|
84
|
+
await codeDb.learnPattern("Singleton", "Use a static instance...");
|
|
85
|
+
success("Pattern learned");
|
|
86
|
+
const searchRes = await codeDb.searchSnippets("Hello");
|
|
87
|
+
if (searchRes.length > 0)
|
|
88
|
+
success(`Snippet search working`);
|
|
89
|
+
// 5. Test Knowledge Graph
|
|
90
|
+
log("GRAPH", "Testing Project Knowledge Graph...");
|
|
91
|
+
const buildRes = await graph.build(process.cwd());
|
|
92
|
+
success(`Graph built: ${buildRes.nodeCount} nodes, ${buildRes.totalFiles} files`);
|
|
93
|
+
const summary = graph.getSummary();
|
|
94
|
+
success(`Summary retrieved: ${summary.fileCount} files in graph`);
|
|
95
|
+
const rel = graph.getRelationships('src/index.ts');
|
|
96
|
+
if (rel) {
|
|
97
|
+
success(`Relationships for src/index.ts found (${rel.imports.length} imports)`);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
console.warn(`${colors.yellow} ! Warning: src/index.ts not found in graph (might be excluded or path issue)${colors.reset}`);
|
|
101
|
+
}
|
|
102
|
+
// 6. Test FileSystem Logic (Reading self)
|
|
103
|
+
log("FS", "Testing File Operations...");
|
|
104
|
+
const content = await fs.readFile('src/index.ts', 'utf-8');
|
|
105
|
+
if (content.length > 0)
|
|
106
|
+
success("read_file working");
|
|
107
|
+
// 7. Test Web Tools (Utils)
|
|
108
|
+
log("WEB", "Testing Web Utilities...");
|
|
109
|
+
try {
|
|
110
|
+
const res = await fetchWithRetry('https://example.com');
|
|
111
|
+
if (res.ok)
|
|
112
|
+
success("Web fetch working");
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
console.warn(`${colors.yellow} ! Web fetch failed (network might be offline), skipping.${colors.reset}`);
|
|
116
|
+
}
|
|
117
|
+
// Cleanup
|
|
118
|
+
log("CLEANUP", "Removing test artifacts...");
|
|
119
|
+
await Promise.all([
|
|
120
|
+
fs.unlink('test_thoughts.json').catch(() => { }),
|
|
121
|
+
fs.unlink('test_notes.json').catch(() => { }),
|
|
122
|
+
fs.unlink('test_codedb.json').catch(() => { })
|
|
123
|
+
]);
|
|
124
|
+
console.log(`\n${colors.green}${colors.bold}=== ALL TESTS PASSED SUCCESSFULLY ===${colors.reset}`);
|
|
125
|
+
}
|
|
126
|
+
main().catch(err => {
|
|
127
|
+
console.error(`\n${colors.red}${colors.bold}FATAL ERROR:${colors.reset}`, err);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
});
|