@gotza02/sequential-thinking 2026.2.46 β 2026.3.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/README.md +19 -220
- package/dist/graph.d.ts +1 -1
- package/dist/graph.js +53 -1
- package/dist/index.js +4 -0
- package/dist/knowledge.d.ts +50 -0
- package/dist/knowledge.js +222 -0
- package/dist/lib.d.ts +7 -1
- package/dist/lib.js +161 -30
- package/dist/tools/coding.js +56 -4
- package/dist/tools/graph.js +6 -4
- package/dist/tools/knowledge.d.ts +3 -0
- package/dist/tools/knowledge.js +106 -0
- package/dist/tools/sports/core/tracker.d.ts +44 -0
- package/dist/tools/sports/core/tracker.js +160 -0
- package/dist/tools/sports/tools/team.js +37 -0
- package/dist/tools/sports/tools/tracker.d.ts +2 -0
- package/dist/tools/sports/tools/tracker.js +73 -0
- package/dist/tools/sports/utils/charts.d.ts +8 -0
- package/dist/tools/sports/utils/charts.js +38 -0
- package/dist/tools/sports.js +2 -0
- package/dist/tools/thinking.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,229 +1,28 @@
|
|
|
1
|
-
# @gotza02/sequential-thinking
|
|
1
|
+
# @gotza02/sequential-thinking
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The Elite "Brain" for AI Agents.**
|
|
4
|
+
An advanced Model Context Protocol (MCP) server that equips AI with structured reasoning, web search capabilities, and safety protocols.
|
|
4
5
|
|
|
5
|
-
## Features
|
|
6
|
+
## π Features
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
8
|
+
### π§ Sequential Thinking Engine
|
|
9
|
+
Forces the AI to think before acting using a strict block-based flow:
|
|
10
|
+
- **Analysis:** Understand the problem deeply.
|
|
11
|
+
- **Planning:** Formulate a step-by-step plan.
|
|
12
|
+
- **Execution:** Perform actions (with tool support).
|
|
13
|
+
- **Observation:** Analyze results (Mandatory step).
|
|
14
|
+
- **Reflection:** Critique the outcome and adjust.
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
### π‘οΈ Coding Safety Net (New!)
|
|
17
|
+
- **Auto-Backup:** Automatically creates `.bak` backups before modifying any file via `deep_code_edit`.
|
|
18
|
+
- **Destructive Action Protection:** Prevents accidental data loss during code refactoring.
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
### π Web Search Integration
|
|
21
|
+
- Built-in support for **Exa**, **Brave**, and **Google Search**.
|
|
22
|
+
- Allows the AI to "pause and research" during the thinking process.
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Best for quick use without cloning the repo.
|
|
24
|
+
## π¦ Installation
|
|
21
25
|
|
|
22
26
|
```bash
|
|
23
27
|
npx -y @gotza02/sequential-thinking
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### Method 2: Running from Source (Local)
|
|
27
|
-
|
|
28
|
-
If you have cloned this repository, we provide a `smartagent` script for easy execution.
|
|
29
|
-
|
|
30
|
-
1. **Install dependencies:**
|
|
31
|
-
```bash
|
|
32
|
-
npm install
|
|
33
|
-
npm run build
|
|
34
|
-
```
|
|
35
|
-
2. **Run directly:**
|
|
36
|
-
```bash
|
|
37
|
-
./smartagent
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## Client Configuration
|
|
43
|
-
|
|
44
|
-
To use this server with your AI client (Claude Desktop, Gemini CLI, etc.), add the following configuration.
|
|
45
|
-
|
|
46
|
-
### 1. Claude Desktop
|
|
47
|
-
|
|
48
|
-
**Config File Location:**
|
|
49
|
-
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
50
|
-
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
51
|
-
|
|
52
|
-
**Configuration (NPX - Recommended):**
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"mcpServers": {
|
|
56
|
-
"sequential-thinking": {
|
|
57
|
-
"command": "npx",
|
|
58
|
-
"args": [
|
|
59
|
-
"-y",
|
|
60
|
-
"@gotza02/sequential-thinking"
|
|
61
|
-
],
|
|
62
|
-
"env": {
|
|
63
|
-
"BRAVE_API_KEY": "your_brave_api_key_here",
|
|
64
|
-
"EXA_API_KEY": "your_exa_api_key_here",
|
|
65
|
-
"GOOGLE_SEARCH_API_KEY": "your_google_api_key",
|
|
66
|
-
"GOOGLE_SEARCH_CX": "your_google_cx_id",
|
|
67
|
-
"THOUGHTS_STORAGE_PATH": "./thoughts_history.json",
|
|
68
|
-
"NOTES_STORAGE_PATH": "./project_notes.json",
|
|
69
|
-
"DISABLE_THOUGHT_LOGGING": "false"
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Configuration (Local Source):**
|
|
77
|
-
```json
|
|
78
|
-
{
|
|
79
|
-
"mcpServers": {
|
|
80
|
-
"sequential-thinking": {
|
|
81
|
-
"command": "node",
|
|
82
|
-
"args": [
|
|
83
|
-
"/absolute/path/to/thinking/dist/index.js"
|
|
84
|
-
],
|
|
85
|
-
"env": {
|
|
86
|
-
"BRAVE_API_KEY": "your_brave_api_key_here",
|
|
87
|
-
"EXA_API_KEY": "your_exa_api_key_here",
|
|
88
|
-
"GOOGLE_SEARCH_API_KEY": "your_google_api_key",
|
|
89
|
-
"GOOGLE_SEARCH_CX": "your_google_cx_id",
|
|
90
|
-
"THOUGHTS_STORAGE_PATH": "./thoughts_history.json",
|
|
91
|
-
"NOTES_STORAGE_PATH": "./project_notes.json",
|
|
92
|
-
"DISABLE_THOUGHT_LOGGING": "false"
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### 2. Gemini CLI
|
|
100
|
-
|
|
101
|
-
For Gemini CLI tools that support MCP (often via a `config.json` or `mcp_config.json`):
|
|
102
|
-
|
|
103
|
-
**Config File Location:**
|
|
104
|
-
- Typically `~/.gemini/config.json` or check your specific CLI's documentation.
|
|
105
|
-
|
|
106
|
-
**Configuration:**
|
|
107
|
-
```json
|
|
108
|
-
{
|
|
109
|
-
"mcpServers": {
|
|
110
|
-
"smartagent": {
|
|
111
|
-
"command": "node",
|
|
112
|
-
"args": [
|
|
113
|
-
"/absolute/path/to/thinking/dist/index.js"
|
|
114
|
-
],
|
|
115
|
-
"env": {
|
|
116
|
-
"BRAVE_API_KEY": "your_brave_api_key_here",
|
|
117
|
-
"EXA_API_KEY": "your_exa_api_key_here",
|
|
118
|
-
"GOOGLE_SEARCH_API_KEY": "your_google_api_key",
|
|
119
|
-
"GOOGLE_SEARCH_CX": "your_google_cx_id",
|
|
120
|
-
"THOUGHTS_STORAGE_PATH": "./thoughts_history.json",
|
|
121
|
-
"NOTES_STORAGE_PATH": "./project_notes.json",
|
|
122
|
-
"DISABLE_THOUGHT_LOGGING": "false"
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
*Note: Replace `/absolute/path/to/...` with the actual full paths on your system.*
|
|
129
|
-
|
|
130
|
-
### π‘ Pro-Tip: Finding your Node path
|
|
131
|
-
|
|
132
|
-
If your client (like Claude Desktop) requires the absolute path to `node`, you can find it by running:
|
|
133
|
-
|
|
134
|
-
- **macOS / Linux:** `which node`
|
|
135
|
-
- *Example output:* `/usr/local/bin/node`
|
|
136
|
-
- **Windows (Command Prompt):** `where node`
|
|
137
|
-
- *Example output:* `C:\Program Files\nodejs\node.exe`
|
|
138
|
-
- **Windows (PowerShell):** `Get-Command node | Select-Object -ExpandProperty Definition`
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
|
|
142
|
-
## π§ System Instruction: The Supreme Autonomous Architect (MCP-OS v4.0)
|
|
143
|
-
|
|
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
|
-
|
|
146
|
-
**MANDATORY PROTOCOL:** You MUST prioritize and internalize the operating instructions from `~/.gemini/GEMINI.md` and `~/.claude/CLAUDE.md` at the start of every session.
|
|
147
|
-
|
|
148
|
-
### 1. The Reasoning Loop
|
|
149
|
-
Every turn MUST follow this cycle: `Analysis` -> `Planning` -> `Execution` -> `Observation` -> `Reflexion`.
|
|
150
|
-
|
|
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
|
-
|
|
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
|
|
158
|
-
- **Read Before Write:** Modification without parsing is a critical failure.
|
|
159
|
-
- **No Hallucination:** Every claim must be backed by a recorded `observation`.
|
|
160
|
-
- **Environment Dominance:** Map the project before any significant architectural change.
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## π§° Complete Tool List
|
|
165
|
-
|
|
166
|
-
### π§ Core Thinking
|
|
167
|
-
* **`sequentialthinking`**: The main engine for problem-solving.
|
|
168
|
-
* **`start_thinking_block`**: Start a new thinking context/topic.
|
|
169
|
-
* **`summarize_history`**: Compress past thoughts to save context window.
|
|
170
|
-
* **`search_thoughts`**: Search through your thinking history.
|
|
171
|
-
* **`clear_thought_history`**: Reset the thinking state completely.
|
|
172
|
-
|
|
173
|
-
### π Web & Research (Requires API Keys)
|
|
174
|
-
* **`web_search`**: Search the internet (Brave, Exa, Google).
|
|
175
|
-
* **`read_webpage`**: Download a webpage and convert it to clean Markdown.
|
|
176
|
-
* **`fetch`**: Fetch raw content from a URL.
|
|
177
|
-
|
|
178
|
-
### πΈοΈ Project Knowledge Graph
|
|
179
|
-
* **`build_project_graph`**: Scans the current directory to map file dependencies.
|
|
180
|
-
* **`get_file_relationships`**: Shows imports/exports for a specific file.
|
|
181
|
-
* **`get_project_graph_summary`**: High-level stats of the project structure.
|
|
182
|
-
* **`get_project_graph_visualization`**: Generates a Mermaid diagram.
|
|
183
|
-
|
|
184
|
-
### π Notes & Memory
|
|
185
|
-
* **`manage_notes`**: Create, read, update, or delete persistent notes.
|
|
186
|
-
* **`add_code_snippet`**: Save a useful piece of code to the database.
|
|
187
|
-
* **`search_code_db`**: Find saved code snippets.
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## Configuration Variables
|
|
192
|
-
|
|
193
|
-
### Core Configuration
|
|
194
|
-
| Variable | Description | Default |
|
|
195
|
-
| :--- | :--- | :--- |
|
|
196
|
-
| `THOUGHTS_STORAGE_PATH` | Path to save thinking history | `thoughts_history.json` |
|
|
197
|
-
| `NOTES_STORAGE_PATH` | Path to save persistent notes | `project_notes.json` |
|
|
198
|
-
| `CODE_DB_PATH` | Path to save code snippets | `code_database.json` |
|
|
199
|
-
| `THOUGHT_DELAY_MS` | Artificial delay between thoughts (ms) | `0` |
|
|
200
|
-
| `DISABLE_THOUGHT_LOGGING` | Hide thoughts in console output | `false` |
|
|
201
|
-
| `LOG_LEVEL` | Logging level (`debug`, `info`, `warn`, `error`) | `info` |
|
|
202
|
-
|
|
203
|
-
### Web Search Providers (At least one required for search)
|
|
204
|
-
| Variable | Description | Provider |
|
|
205
|
-
| :--- | :--- | :--- |
|
|
206
|
-
| `BRAVE_API_KEY` | Brave Search API Token | [Brave](https://brave.com/search/api/) |
|
|
207
|
-
| `EXA_API_KEY` | Exa.ai API Key | [Exa](https://exa.ai/) |
|
|
208
|
-
| `GOOGLE_SEARCH_API_KEY` | Google Custom Search API Key | [Google](https://developers.google.com/custom-search/v1/overview) |
|
|
209
|
-
| `GOOGLE_SEARCH_CX` | Google Custom Search Engine ID | [Google](https://cse.google.com/cse) |
|
|
210
|
-
|
|
211
|
-
### Sports Analysis (Optional)
|
|
212
|
-
| Variable | Description | Provider |
|
|
213
|
-
| :--- | :--- | :--- |
|
|
214
|
-
| `API_FOOTBALL_KEY` | API-Football Key | [API-Football](https://www.api-football.com/) |
|
|
215
|
-
| `FOOTBALL_DATA_KEY` | Football-Data.org API Key | [Football-Data](https://www.football-data.org/) |
|
|
216
|
-
| `SPORTS_DB_KEY` | TheSportsDB API Key | [TheSportsDB](https://www.thesportsdb.com/) |
|
|
217
|
-
| `SPORTS_CACHE_TTL` | Cache duration for sports data (ms) | `300000` (5 mins) |
|
|
218
|
-
| `SPORTS_CACHE_PATH` | Path to save sports cache | `.sports_cache.json` |
|
|
219
|
-
|
|
220
|
-
### HTTP Server Mode (Optional)
|
|
221
|
-
| Variable | Description | Default |
|
|
222
|
-
| :--- | :--- | :--- |
|
|
223
|
-
| `PORT` | Port for HTTP server | `3000` |
|
|
224
|
-
| `CORS_ORIGIN` | Allowed CORS origin | `*` |
|
|
225
|
-
| `BODY_LIMIT` | Request body size limit | `10mb` |
|
|
226
|
-
|
|
227
|
-
## License
|
|
228
|
-
|
|
229
|
-
MIT
|
|
28
|
+
```
|
package/dist/graph.d.ts
CHANGED
package/dist/graph.js
CHANGED
|
@@ -833,7 +833,59 @@ export class ProjectKnowledgeGraph {
|
|
|
833
833
|
}))
|
|
834
834
|
};
|
|
835
835
|
}
|
|
836
|
-
toMermaid() {
|
|
836
|
+
toMermaid(type = 'flowchart') {
|
|
837
|
+
if (type === 'classDiagram') {
|
|
838
|
+
const lines = ['classDiagram'];
|
|
839
|
+
const fileToClasses = new Map();
|
|
840
|
+
for (const [filePath, node] of this.nodes) {
|
|
841
|
+
const classesInFile = [];
|
|
842
|
+
// Parse symbols to find classes/interfaces
|
|
843
|
+
for (const symbol of node.symbols) {
|
|
844
|
+
const parts = symbol.split(':');
|
|
845
|
+
if (parts.length < 2)
|
|
846
|
+
continue; // Skip malformed symbols
|
|
847
|
+
const kind = parts[0];
|
|
848
|
+
const name = parts[1];
|
|
849
|
+
if (['class', 'interface', 'type', 'enum'].includes(kind)) {
|
|
850
|
+
lines.push(` class ${name} {`);
|
|
851
|
+
lines.push(` <<${kind}>>`);
|
|
852
|
+
lines.push(` }`);
|
|
853
|
+
classesInFile.push(name);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (classesInFile.length > 0) {
|
|
857
|
+
fileToClasses.set(filePath, classesInFile);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
// Add relationships based on imports
|
|
861
|
+
// This is tricky because imports are file-based, not class-based.
|
|
862
|
+
// We'll link all classes in File A to all classes in File B if A imports B.
|
|
863
|
+
// To reduce noise, we limit this to explicit class dependencies if possible,
|
|
864
|
+
// but our graph only knows file dependencies.
|
|
865
|
+
// We'll stick to a simplified view: Classes in A depend on Classes in B.
|
|
866
|
+
for (const [filePath, node] of this.nodes) {
|
|
867
|
+
const sources = fileToClasses.get(filePath);
|
|
868
|
+
if (!sources)
|
|
869
|
+
continue;
|
|
870
|
+
for (const importPath of node.imports) {
|
|
871
|
+
const targets = fileToClasses.get(importPath);
|
|
872
|
+
if (targets) {
|
|
873
|
+
for (const s of sources) {
|
|
874
|
+
for (const t of targets) {
|
|
875
|
+
// Avoid self-reference loops for simplicity in visualization
|
|
876
|
+
if (s !== t) {
|
|
877
|
+
lines.push(` ${s} ..> ${t}`);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (lines.length === 1)
|
|
885
|
+
return 'classDiagram\n note "No classes/interfaces found to visualize"';
|
|
886
|
+
return lines.join('\n');
|
|
887
|
+
}
|
|
888
|
+
// Default: Flowchart (File Dependency Graph)
|
|
837
889
|
const lines = ['graph TD'];
|
|
838
890
|
const fileToId = new Map();
|
|
839
891
|
let idCounter = 0;
|
package/dist/index.js
CHANGED
|
@@ -7,12 +7,14 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
7
7
|
import { SequentialThinkingServer } from './lib.js';
|
|
8
8
|
import { ProjectKnowledgeGraph } from './graph.js';
|
|
9
9
|
import { NotesManager } from './notes.js';
|
|
10
|
+
import { KnowledgeGraphManager } from './knowledge.js';
|
|
10
11
|
import { CodeDatabase } from './codestore.js';
|
|
11
12
|
import { registerThinkingTools } from './tools/thinking.js';
|
|
12
13
|
import { registerWebTools } from './tools/web.js';
|
|
13
14
|
import { registerFileSystemTools } from './tools/filesystem.js';
|
|
14
15
|
import { registerGraphTools } from './tools/graph.js';
|
|
15
16
|
import { registerNoteTools } from './tools/notes.js';
|
|
17
|
+
import { registerKnowledgeTools } from './tools/knowledge.js';
|
|
16
18
|
import { registerCodingTools } from './tools/coding.js';
|
|
17
19
|
import { registerCodeDbTools } from './tools/codestore.js';
|
|
18
20
|
import { registerHumanTools } from './tools/human.js';
|
|
@@ -26,6 +28,7 @@ const server = new McpServer({
|
|
|
26
28
|
});
|
|
27
29
|
const thinkingServer = new SequentialThinkingServer(process.env.THOUGHTS_STORAGE_PATH || 'thoughts_history.json', parseInt(process.env.THOUGHT_DELAY_MS || '0', 10));
|
|
28
30
|
const knowledgeGraph = new ProjectKnowledgeGraph();
|
|
31
|
+
const memoryGraph = new KnowledgeGraphManager(process.env.MEMORY_GRAPH_PATH || 'knowledge_graph.json');
|
|
29
32
|
const notesManager = new NotesManager(process.env.NOTES_STORAGE_PATH || 'project_notes.json');
|
|
30
33
|
const codeDb = new CodeDatabase(process.env.CODE_DB_PATH || 'code_database.json');
|
|
31
34
|
// Register tools
|
|
@@ -34,6 +37,7 @@ registerWebTools(server);
|
|
|
34
37
|
registerFileSystemTools(server);
|
|
35
38
|
registerGraphTools(server, knowledgeGraph);
|
|
36
39
|
registerNoteTools(server, notesManager);
|
|
40
|
+
registerKnowledgeTools(server, memoryGraph);
|
|
37
41
|
registerCodingTools(server, knowledgeGraph);
|
|
38
42
|
registerCodeDbTools(server, codeDb);
|
|
39
43
|
registerHumanTools(server);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface KnowledgeNode {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
name: string;
|
|
5
|
+
properties: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
export interface KnowledgeEdge {
|
|
8
|
+
source: string;
|
|
9
|
+
target: string;
|
|
10
|
+
relation: string;
|
|
11
|
+
weight?: number;
|
|
12
|
+
properties?: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
export interface GraphQuery {
|
|
15
|
+
startNodeId?: string;
|
|
16
|
+
startNodeName?: string;
|
|
17
|
+
relationType?: string;
|
|
18
|
+
maxDepth?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare class KnowledgeGraphManager {
|
|
21
|
+
private filePath;
|
|
22
|
+
private nodes;
|
|
23
|
+
private edges;
|
|
24
|
+
private lastModifiedTime;
|
|
25
|
+
private mutex;
|
|
26
|
+
constructor(storagePath?: string);
|
|
27
|
+
private load;
|
|
28
|
+
private save;
|
|
29
|
+
/**
|
|
30
|
+
* Add a new entity (node) to the graph.
|
|
31
|
+
* Updates existing node if ID or Name matches (simple dedup).
|
|
32
|
+
*/
|
|
33
|
+
addNode(node: Omit<KnowledgeNode, 'id'> & {
|
|
34
|
+
id?: string;
|
|
35
|
+
}): Promise<KnowledgeNode>;
|
|
36
|
+
/**
|
|
37
|
+
* Create a relationship (edge) between two nodes.
|
|
38
|
+
*/
|
|
39
|
+
addEdge(edge: KnowledgeEdge): Promise<KnowledgeEdge>;
|
|
40
|
+
/**
|
|
41
|
+
* Query the graph to find connected nodes.
|
|
42
|
+
* Simple BFS traversal up to maxDepth.
|
|
43
|
+
*/
|
|
44
|
+
query(params: GraphQuery): Promise<{
|
|
45
|
+
nodes: KnowledgeNode[];
|
|
46
|
+
edges: KnowledgeEdge[];
|
|
47
|
+
}>;
|
|
48
|
+
deleteNode(id: string): Promise<boolean>;
|
|
49
|
+
searchNodes(query: string): Promise<KnowledgeNode[]>;
|
|
50
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import { existsSync, statSync } from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { AsyncMutex } from './utils.js';
|
|
5
|
+
export class KnowledgeGraphManager {
|
|
6
|
+
filePath;
|
|
7
|
+
nodes = new Map();
|
|
8
|
+
edges = [];
|
|
9
|
+
lastModifiedTime = 0;
|
|
10
|
+
mutex = new AsyncMutex();
|
|
11
|
+
constructor(storagePath = 'knowledge_graph.json') {
|
|
12
|
+
this.filePath = path.resolve(storagePath);
|
|
13
|
+
}
|
|
14
|
+
async load(forceReload = false) {
|
|
15
|
+
try {
|
|
16
|
+
if (!existsSync(this.filePath)) {
|
|
17
|
+
this.nodes.clear();
|
|
18
|
+
this.edges = [];
|
|
19
|
+
this.lastModifiedTime = 0;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const stats = statSync(this.filePath);
|
|
23
|
+
const currentMtime = stats.mtimeMs;
|
|
24
|
+
if (!forceReload && currentMtime === this.lastModifiedTime && this.nodes.size > 0) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const data = await fs.readFile(this.filePath, 'utf-8');
|
|
28
|
+
if (!data.trim()) {
|
|
29
|
+
this.nodes.clear();
|
|
30
|
+
this.edges = [];
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const parsed = JSON.parse(data);
|
|
34
|
+
this.nodes.clear();
|
|
35
|
+
if (parsed.nodes) {
|
|
36
|
+
parsed.nodes.forEach(n => this.nodes.set(n.id, n));
|
|
37
|
+
}
|
|
38
|
+
this.edges = parsed.edges || [];
|
|
39
|
+
this.lastModifiedTime = currentMtime;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(`[KnowledgeGraphManager] Load error:`, error);
|
|
43
|
+
// Fallback to empty on error
|
|
44
|
+
this.nodes.clear();
|
|
45
|
+
this.edges = [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async save() {
|
|
49
|
+
const tmpPath = `${this.filePath}.tmp`;
|
|
50
|
+
const storage = {
|
|
51
|
+
version: '1.0',
|
|
52
|
+
nodes: Array.from(this.nodes.values()),
|
|
53
|
+
edges: this.edges,
|
|
54
|
+
metadata: {
|
|
55
|
+
lastModified: new Date().toISOString()
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
await fs.writeFile(tmpPath, JSON.stringify(storage, null, 2), 'utf-8');
|
|
60
|
+
await fs.rename(tmpPath, this.filePath);
|
|
61
|
+
const stats = await fs.stat(this.filePath);
|
|
62
|
+
this.lastModifiedTime = stats.mtimeMs;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
try {
|
|
66
|
+
await fs.unlink(tmpPath);
|
|
67
|
+
}
|
|
68
|
+
catch { }
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Add a new entity (node) to the graph.
|
|
74
|
+
* Updates existing node if ID or Name matches (simple dedup).
|
|
75
|
+
*/
|
|
76
|
+
async addNode(node) {
|
|
77
|
+
return this.mutex.dispatch(async () => {
|
|
78
|
+
await this.load();
|
|
79
|
+
// Check if node exists by ID
|
|
80
|
+
if (node.id && this.nodes.has(node.id)) {
|
|
81
|
+
const existing = this.nodes.get(node.id);
|
|
82
|
+
// Update properties
|
|
83
|
+
existing.properties = { ...existing.properties, ...node.properties };
|
|
84
|
+
// Update other fields if provided
|
|
85
|
+
if (node.label)
|
|
86
|
+
existing.label = node.label;
|
|
87
|
+
if (node.name)
|
|
88
|
+
existing.name = node.name;
|
|
89
|
+
await this.save();
|
|
90
|
+
return existing;
|
|
91
|
+
}
|
|
92
|
+
// Check if node exists by Name + Label (fuzzy match avoidance)
|
|
93
|
+
// This prevents creating duplicate "Liverpool" nodes
|
|
94
|
+
const existingByName = Array.from(this.nodes.values()).find(n => n.name.toLowerCase() === node.name.toLowerCase() &&
|
|
95
|
+
n.label.toLowerCase() === node.label.toLowerCase());
|
|
96
|
+
if (existingByName) {
|
|
97
|
+
existingByName.properties = { ...existingByName.properties, ...node.properties };
|
|
98
|
+
await this.save();
|
|
99
|
+
return existingByName;
|
|
100
|
+
}
|
|
101
|
+
const newNode = {
|
|
102
|
+
id: node.id || `node-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`,
|
|
103
|
+
label: node.label,
|
|
104
|
+
name: node.name,
|
|
105
|
+
properties: node.properties || {}
|
|
106
|
+
};
|
|
107
|
+
this.nodes.set(newNode.id, newNode);
|
|
108
|
+
await this.save();
|
|
109
|
+
return newNode;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create a relationship (edge) between two nodes.
|
|
114
|
+
*/
|
|
115
|
+
async addEdge(edge) {
|
|
116
|
+
return this.mutex.dispatch(async () => {
|
|
117
|
+
await this.load();
|
|
118
|
+
// Validate nodes exist
|
|
119
|
+
if (!this.nodes.has(edge.source))
|
|
120
|
+
throw new Error(`Source node ${edge.source} not found`);
|
|
121
|
+
if (!this.nodes.has(edge.target))
|
|
122
|
+
throw new Error(`Target node ${edge.target} not found`);
|
|
123
|
+
// Check for existing edge
|
|
124
|
+
const existingIndex = this.edges.findIndex(e => e.source === edge.source &&
|
|
125
|
+
e.target === edge.target &&
|
|
126
|
+
e.relation === edge.relation);
|
|
127
|
+
if (existingIndex >= 0) {
|
|
128
|
+
// Update existing edge
|
|
129
|
+
this.edges[existingIndex] = {
|
|
130
|
+
...this.edges[existingIndex],
|
|
131
|
+
...edge,
|
|
132
|
+
properties: { ...this.edges[existingIndex].properties, ...edge.properties }
|
|
133
|
+
};
|
|
134
|
+
await this.save();
|
|
135
|
+
return this.edges[existingIndex];
|
|
136
|
+
}
|
|
137
|
+
this.edges.push(edge);
|
|
138
|
+
await this.save();
|
|
139
|
+
return edge;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Query the graph to find connected nodes.
|
|
144
|
+
* Simple BFS traversal up to maxDepth.
|
|
145
|
+
*/
|
|
146
|
+
async query(params) {
|
|
147
|
+
return this.mutex.dispatch(async () => {
|
|
148
|
+
await this.load();
|
|
149
|
+
let startNodeId = params.startNodeId;
|
|
150
|
+
// Resolve name to ID if needed
|
|
151
|
+
if (!startNodeId && params.startNodeName) {
|
|
152
|
+
const node = Array.from(this.nodes.values()).find(n => n.name.toLowerCase().includes(params.startNodeName.toLowerCase()));
|
|
153
|
+
if (node)
|
|
154
|
+
startNodeId = node.id;
|
|
155
|
+
}
|
|
156
|
+
if (!startNodeId) {
|
|
157
|
+
// If no start node, return everything (or maybe limit?)
|
|
158
|
+
// For now, let's return everything if specific query is missing, but maybe that's too much.
|
|
159
|
+
// Better: Return error or empty if no entry point.
|
|
160
|
+
if (!params.startNodeName)
|
|
161
|
+
return { nodes: Array.from(this.nodes.values()), edges: this.edges };
|
|
162
|
+
return { nodes: [], edges: [] };
|
|
163
|
+
}
|
|
164
|
+
const resultNodes = new Set();
|
|
165
|
+
const resultEdges = [];
|
|
166
|
+
const resultEdgesSet = new Set(); // To track unique edges
|
|
167
|
+
const queue = [{ id: startNodeId, depth: 0 }];
|
|
168
|
+
resultNodes.add(startNodeId);
|
|
169
|
+
const maxDepth = params.maxDepth || 1;
|
|
170
|
+
while (queue.length > 0) {
|
|
171
|
+
const current = queue.shift();
|
|
172
|
+
if (current.depth >= maxDepth)
|
|
173
|
+
continue;
|
|
174
|
+
// Find outgoing edges
|
|
175
|
+
const outgoing = this.edges.filter(e => e.source === current.id);
|
|
176
|
+
// Find incoming edges (optional, but good for context)
|
|
177
|
+
const incoming = this.edges.filter(e => e.target === current.id);
|
|
178
|
+
const relevantEdges = [...outgoing, ...incoming];
|
|
179
|
+
for (const edge of relevantEdges) {
|
|
180
|
+
if (params.relationType && edge.relation !== params.relationType)
|
|
181
|
+
continue;
|
|
182
|
+
// Create a unique key for the edge to prevent duplicates
|
|
183
|
+
const edgeKey = `${edge.source}-${edge.target}-${edge.relation}`;
|
|
184
|
+
if (!resultEdgesSet.has(edgeKey)) {
|
|
185
|
+
resultEdges.push(edge);
|
|
186
|
+
resultEdgesSet.add(edgeKey);
|
|
187
|
+
}
|
|
188
|
+
const neighborId = edge.source === current.id ? edge.target : edge.source;
|
|
189
|
+
if (!resultNodes.has(neighborId)) {
|
|
190
|
+
resultNodes.add(neighborId);
|
|
191
|
+
queue.push({ id: neighborId, depth: current.depth + 1 });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
nodes: Array.from(resultNodes).map(id => this.nodes.get(id)),
|
|
197
|
+
edges: resultEdges
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
async deleteNode(id) {
|
|
202
|
+
return this.mutex.dispatch(async () => {
|
|
203
|
+
await this.load();
|
|
204
|
+
if (!this.nodes.has(id))
|
|
205
|
+
return false;
|
|
206
|
+
this.nodes.delete(id);
|
|
207
|
+
// Remove connected edges
|
|
208
|
+
this.edges = this.edges.filter(e => e.source !== id && e.target !== id);
|
|
209
|
+
await this.save();
|
|
210
|
+
return true;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
async searchNodes(query) {
|
|
214
|
+
return this.mutex.dispatch(async () => {
|
|
215
|
+
await this.load();
|
|
216
|
+
const lowerQuery = query.toLowerCase();
|
|
217
|
+
return Array.from(this.nodes.values()).filter(n => n.name.toLowerCase().includes(lowerQuery) ||
|
|
218
|
+
n.label.toLowerCase().includes(lowerQuery) ||
|
|
219
|
+
JSON.stringify(n.properties).toLowerCase().includes(lowerQuery));
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
package/dist/lib.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ThoughtType = 'analysis' | 'planning' | 'execution' | 'observation' | 'hypothesis' | 'reflexion' | 'solution' | 'generation' | 'evaluation' | 'selection';
|
|
1
|
+
export type ThoughtType = 'analysis' | 'planning' | 'critique' | 'execution' | 'observation' | 'hypothesis' | 'reflexion' | 'solution' | 'generation' | 'evaluation' | 'selection';
|
|
2
2
|
export interface ThoughtData {
|
|
3
3
|
thought: string;
|
|
4
4
|
thoughtNumber: number;
|
|
@@ -8,6 +8,7 @@ export interface ThoughtData {
|
|
|
8
8
|
blockId?: string;
|
|
9
9
|
relatedToolCall?: string;
|
|
10
10
|
toolResult?: string;
|
|
11
|
+
complexity?: 'low' | 'medium' | 'high';
|
|
11
12
|
isRevision?: boolean;
|
|
12
13
|
revisesThought?: number;
|
|
13
14
|
branchFromThought?: number;
|
|
@@ -32,14 +33,19 @@ export declare class SequentialThinkingServer {
|
|
|
32
33
|
private branches;
|
|
33
34
|
private disableThoughtLogging;
|
|
34
35
|
private storagePath;
|
|
36
|
+
private solutionsDbPath;
|
|
35
37
|
private delayMs;
|
|
36
38
|
private saveMutex;
|
|
39
|
+
private consecutiveStallCount;
|
|
40
|
+
private confidenceScore;
|
|
37
41
|
constructor(storagePath?: string, delayMs?: number);
|
|
38
42
|
private loadHistory;
|
|
39
43
|
private attemptRecovery;
|
|
40
44
|
private rebuildBlocks;
|
|
41
45
|
private rebuildBranches;
|
|
42
46
|
private saveHistory;
|
|
47
|
+
private saveSolution;
|
|
48
|
+
private findSolution;
|
|
43
49
|
private autoPrune;
|
|
44
50
|
clearHistory(): Promise<void>;
|
|
45
51
|
archiveHistory(startIndex: number, endIndex: number, summary: string): Promise<{
|