@gotza02/sequential-thinking 2026.2.12 → 2026.2.14
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 +74 -225
- package/dist/graph.js +54 -16
- package/dist/lib.js +56 -31
- package/dist/tools/web.js +35 -1
- package/dist/utils.js +20 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,246 +1,76 @@
|
|
|
1
|
-
# Sequential Thinking MCP
|
|
1
|
+
# Sequential Thinking MCP (Extended) 🧠
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Advanced MCP Server enabling AI to act as a Software Engineer with Deep Thinking, Codebase Graph Analysis, and Persistent Memory.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## ✨ Key Features
|
|
6
|
+
- **Deep Thinking:** Step-by-step reasoning with auto-correction (Loop Breaker).
|
|
7
|
+
- **Code Intelligence:** Dependency graph mapping (`build_project_graph`) & surgical code editing.
|
|
8
|
+
- **Memory:** Long-term project notes, reusable code database, and thought history.
|
|
9
|
+
- **Research:** Integrated Web search (Brave/Exa) & webpage reading.
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 🌟 จุดเด่นของเวอร์ชัน Extended
|
|
12
|
-
|
|
13
|
-
1. **Deepest Thinking Protocol**: บังคับให้ AI วิเคราะห์ปัญหาแบบเป็นลำดับขั้นตอน (Step-by-step) พร้อมการแตกกิ่งความคิด (Branching) และการทบทวนตัวเอง (Reflexion) เพื่อหาทางเลือกที่ดีที่สุด
|
|
14
|
-
2. **Codebase Intelligence**: ระบบ `ProjectKnowledgeGraph` ที่ใช้ TypeScript Compiler API และ Regex (สำหรับ Python/Go) ในการสแกนความสัมพันธ์ระหว่างไฟล์และ Exported Symbols
|
|
15
|
-
3. **Code Database (CodeStore)**: ระบบจัดเก็บ Snippets และ Architectural Patterns ลงในไฟล์ JSON ถาวร ช่วยให้ AI "จดจำ" วิธีแก้ปัญหาและนำกลับมาใช้ใหม่ได้
|
|
16
|
-
4. **Deep Coding Workflow**: เครื่องมือใหม่สำหรับการแก้ไขโค้ดที่ต้องผ่านการวิเคราะห์บริบท (Context Document) และการวางแผนที่ผ่านการตรวจสอบเหตุผลแล้วเท่านั้น
|
|
17
|
-
5. **Smart Notes**: ระบบบันทึกที่มี **Priority Level** และ **Expiration Date** ช่วยจัดลำดับความสำคัญของงานได้ดียิ่งขึ้น พร้อมฟีเจอร์ **Auto-Repair** กู้คืนไฟล์อัตโนมัติหากข้อมูลเสียหาย
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 🔑 Environment Variables (การตั้งค่าตัวแปร)
|
|
22
|
-
|
|
23
|
-
| Variable Name | Description | Required? |
|
|
24
|
-
|--------------|-------------|-----------|
|
|
25
|
-
| `BRAVE_API_KEY` | สำหรับค้นหาผ่าน Brave Search | ❌ (Optional) |
|
|
26
|
-
| `EXA_API_KEY` | สำหรับค้นหาผ่าน Exa.ai (แนะนำมากสำหรับงาน Deep Research) | ❌ (Optional) |
|
|
27
|
-
| `GOOGLE_SEARCH_API_KEY` | Google Custom Search API Key | ❌ (Optional) |
|
|
28
|
-
| `GOOGLE_SEARCH_CX` | Google Custom Search Engine ID (CX) | ❌ (Optional) |
|
|
29
|
-
| `THOUGHTS_STORAGE_PATH` | ไฟล์เก็บประวัติความคิดเพื่อใช้ต่อเนื่อง | `thoughts_history.json` |
|
|
30
|
-
| `NOTES_STORAGE_PATH` | ไฟล์เก็บความจำระยะยาว (กฎโปรเจกต์, ความชอบ) | `project_notes.json` |
|
|
31
|
-
| `CODE_DB_PATH` | ไฟล์เก็บฐานข้อมูลความรู้โค้ด (Snippets/Patterns) | `code_database.json` |
|
|
32
|
-
| `THOUGHT_DELAY_MS` | เวลาหน่วงระหว่างคิด (เพื่อความสมจริงและการจัดการ Rate Limit) | `0` |
|
|
33
|
-
| `LOG_LEVEL` | ระดับการแสดงผล Log (debug, info, warn, error) | `info` |
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## 🛠️ รายการเครื่องมือ (Tools Capability)
|
|
38
|
-
|
|
39
|
-
### 🧠 1. Cognitive & Deepest Thinking
|
|
40
|
-
* **`sequentialthinking`**: (ปรับปรุงใหม่!) บังคับใช้หลักการ **Deepest Thinking** รองรับการทำ Analysis, Reflexion, Evaluation และ Selection ในทุกการแก้ปัญหา
|
|
41
|
-
* **`summarize_history`**: สรุปประวัติความคิดที่ยาวเหยียดเพื่อประหยัดพื้นที่บริบท
|
|
42
|
-
* **`clear_thought_history`**: ล้างสมองเพื่อเริ่มงานชิ้นใหม่
|
|
43
|
-
|
|
44
|
-
### 💻 2. Deep Coding & Codebase Context
|
|
45
|
-
* **`deep_code_analyze`**: 🌟 **(ใหม่)** สร้าง Codebase Context Document รวบรวมเนื้อหาไฟล์, Symbols และความสัมพันธ์ (Import/Export) เพื่อให้ AI "เรียนรู้" ก่อนแก้โค้ด
|
|
46
|
-
* **`deep_code_edit`**: 🌟 **(ใหม่)** แก้ไขโค้ดแบบ Surgical Edit โดยต้องระบุ `reasoning` (เหตุผลเชิงลึก) ที่ผ่านการคิดวิเคราะห์มาแล้ว
|
|
47
|
-
* **`build_project_graph`**: สแกนโปรเจกต์เพื่อสร้างแผนผังความสัมพันธ์ทั้งหมด (รองรับ TS, JS, PY, GO)
|
|
48
|
-
* **`get_file_relationships`**: ตรวจสอบว่าไฟล์เป้าหมายกระทบกับส่วนไหนของระบบบ้าง
|
|
49
|
-
* **`search_code`**: ค้นหา Code Pattern ทั้งโปรเจกต์แบบชาญฉลาด
|
|
50
|
-
|
|
51
|
-
### 📚 3. Knowledge & Memory (CodeStore)
|
|
52
|
-
* **`add_code_snippet`**: 🌟 **(ใหม่)** บันทึก Snippet โค้ดที่ใช้งานบ่อยลงฐานข้อมูล
|
|
53
|
-
* **`search_code_db`**: 🌟 **(ใหม่)** ค้นหาความรู้เดิมจากฐานข้อมูล (ช่วยลดเวลาในการเริ่มงานใหม่)
|
|
54
|
-
* **`learn_architecture_pattern`**: 🌟 **(ใหม่)** บันทึกรูปแบบโครงสร้างของระบบ
|
|
55
|
-
* **`manage_notes`**: (ปรับปรุงใหม่!) จัดการบันทึกพร้อมระบุ **Priority** และ **Expiration**
|
|
56
|
-
|
|
57
|
-
### 🌐 4. External Research
|
|
58
|
-
* **`web_search`**: ค้นหาข้อมูลล่าสุดจากโลกภายนอก (Brave/Exa/Google)
|
|
59
|
-
* **`read_webpage`**: อ่านเนื้อหาเว็บแปลงเป็น Markdown ที่สะอาดและเข้าใจง่าย
|
|
60
|
-
* **`fetch`**: ดึงข้อมูล Raw จาก API ต่างๆ
|
|
61
|
-
|
|
62
|
-
### 📁 5. System & Persistence
|
|
63
|
-
* **`read_file` / `write_file` / `edit_file`**: จัดการไฟล์ในระบบ
|
|
64
|
-
* **`shell_execute`**: รันคำสั่ง Terminal (มีระบบป้องกันคำสั่งอันตราย เช่น `rm -rf /`)
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
## 🚀 การติดตั้ง (Installation)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
คุณสามารถเลือกติดตั้งได้ 3 รูปแบบตามความสะดวกดังนี้:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
### 1. แบบ npx (ไม่ต้องติดตั้งลงเครื่องถาวร)
|
|
79
|
-
|
|
80
|
-
วิธีที่ง่ายที่สุดและแนะนำสำหรับ AI Client ส่วนใหญ่:
|
|
11
|
+
## 🚀 Quick Setup
|
|
81
12
|
|
|
13
|
+
### 1. Installation
|
|
14
|
+
**Option A: Run Temporarily (Recommended for testing)**
|
|
82
15
|
```bash
|
|
83
|
-
|
|
84
16
|
npx -y @gotza02/sequential-thinking
|
|
85
|
-
|
|
86
17
|
```
|
|
87
18
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
### 2. แบบ npm (ติดตั้งลงเครื่อง)
|
|
91
|
-
|
|
92
|
-
เหมาะสำหรับผู้ที่ต้องการความเสถียรหรือใช้งานในสภาพแวดล้อมที่ไม่มีอินเทอร์เน็ตตลอดเวลา:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
* **Global Installation (แนะนำ):**
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
|
|
100
|
-
npm install -g @gotza02/sequential-thinking
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
* **Local Installation (ในโปรเจกต์):
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
|
|
108
|
-
npm install @gotza02/sequential-thinking
|
|
109
|
-
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
### 3. แบบจาก Source Code (Developer)
|
|
115
|
-
|
|
19
|
+
**Option B: Install Globally**
|
|
116
20
|
```bash
|
|
117
|
-
|
|
118
|
-
git clone https://github.com/gotza02/sequential-thinking.git && cd sequential-thinking && npm install && npm run build
|
|
119
|
-
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
## 🔍 วิธีค้นหา Path ที่ถูกต้อง (Path Discovery)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
หากคุณติดตั้งผ่าน `npm` และต้องการทราบว่าไฟล์ `index.js` อยู่ที่ไหนเพื่อนำไปใส่ใน AI Client:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
* **กรณีติดตั้ง Global:**
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
|
|
140
|
-
# สำหรับ Linux/Android (Termux)
|
|
141
|
-
|
|
142
|
-
echo "$(npm config get prefix)/lib/node_modules/@gotza02/sequential-thinking/dist/index.js"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
# สำหรับ Windows (PowerShell)
|
|
147
|
-
|
|
148
|
-
echo "$env:APPDATA\npm\node_modules\@gotza02\sequential-thinking\dist\index.js"
|
|
149
|
-
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
* **กรณีติดตั้ง Local:**
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
|
|
156
|
-
echo "$(pwd)/node_modules/@gotza02/sequential-thinking/dist/index.js"
|
|
157
|
-
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
## ⚙️ การตั้งค่าใน AI Client (Configuration Examples)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
### 1. การใช้ npx (ง่ายที่สุด)
|
|
171
|
-
|
|
172
|
-
เหมาะสำหรับ **Gemini CLI** หรือ **Claude Desktop** ที่เชื่อมต่ออินเทอร์เน็ตได้:
|
|
173
|
-
|
|
174
|
-
```json
|
|
175
|
-
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
"command": "npx",
|
|
179
|
-
|
|
180
|
-
"args": ["-y", "@gotza02/sequential-thinking"]
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
21
|
+
npm install -g @gotza02/sequential-thinking
|
|
184
22
|
```
|
|
185
23
|
|
|
24
|
+
### 2. Configuration (Gemini/Claude)
|
|
25
|
+
Add this to your MCP settings (`~/.gemini/settings.json` or `claude_desktop_config.json`).
|
|
186
26
|
|
|
187
|
-
|
|
188
|
-
### 2. การใช้ npm/Node (เสถียรกว่า)
|
|
189
|
-
|
|
190
|
-
ต้องระบุ Path ที่หาได้จากขั้นตอน **Path Discovery** ด้านบน:
|
|
191
|
-
|
|
27
|
+
**For npx usage:**
|
|
192
28
|
```json
|
|
193
|
-
|
|
194
29
|
{
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"smartagent": {
|
|
32
|
+
"command": "npx",
|
|
33
|
+
"args": ["-y", "@gotza02/sequential-thinking"],
|
|
34
|
+
"env": { "THOUGHTS_STORAGE_PATH": "thoughts.json" }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
200
37
|
}
|
|
201
|
-
|
|
202
38
|
```
|
|
203
39
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
#### ตัวอย่างไฟล์ Config (Gemini CLI: `~/.gemini/settings.json`)
|
|
40
|
+
**For npm usage:**
|
|
41
|
+
*(Find path with: `npm root -g`)*
|
|
207
42
|
```json
|
|
208
43
|
{
|
|
209
44
|
"mcpServers": {
|
|
210
45
|
"smartagent": {
|
|
211
|
-
"command": "
|
|
212
|
-
"args": ["
|
|
213
|
-
"env": {
|
|
214
|
-
"BRAVE_API_KEY": "YOUR_BRAVE_KEY",
|
|
215
|
-
"EXA_API_KEY": "YOUR_EXA_KEY",
|
|
216
|
-
"GOOGLE_SEARCH_API_KEY": "YOUR_GOOGLE_API_KEY",
|
|
217
|
-
"GOOGLE_SEARCH_CX": "YOUR_GOOGLE_SEARCH_ENGINE_ID",
|
|
218
|
-
"THOUGHTS_STORAGE_PATH": "thoughts_history.json",
|
|
219
|
-
"NOTES_STORAGE_PATH": "project_notes.json",
|
|
220
|
-
"CODE_DB_PATH": "code_database.json"
|
|
221
|
-
}
|
|
46
|
+
"command": "node",
|
|
47
|
+
"args": ["/PATH/TO/node_modules/@gotza02/sequential-thinking/dist/index.js"]
|
|
222
48
|
}
|
|
223
49
|
}
|
|
224
50
|
}
|
|
225
51
|
```
|
|
226
52
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
53
|
+
## 🔑 Environment Variables
|
|
54
|
+
| Variable | Description |
|
|
55
|
+
|----------|-------------|
|
|
56
|
+
| `THOUGHTS_STORAGE_PATH` | Path to save thought history (Required) |
|
|
57
|
+
| `NOTES_STORAGE_PATH` | Path to save project notes |
|
|
58
|
+
| `CODE_DB_PATH` | Path to save code snippets |
|
|
59
|
+
| `BRAVE_API_KEY` | Optional: For Brave Search |
|
|
60
|
+
| `EXA_API_KEY` | Optional: For Exa.ai Search |
|
|
61
|
+
|
|
62
|
+
## 🛠️ Tools Summary
|
|
63
|
+
- **Thinking:** `sequentialthinking`, `summarize_history`, `clear_thought_history`
|
|
64
|
+
- **Code:** `build_project_graph`, `deep_code_analyze`, `deep_code_edit`, `search_code`
|
|
65
|
+
- **Memory:** `add_code_snippet`, `search_code_db`, `manage_notes`
|
|
66
|
+
- **Web:** `web_search`, `read_webpage`, `fetch`
|
|
67
|
+
- **System:** `read_file`, `write_file`, `edit_file`, `shell_execute`
|
|
238
68
|
|
|
239
69
|
---
|
|
240
70
|
|
|
241
71
|
## 🤖 Recommended System Instruction (The Ultimate Deep Engineer)
|
|
242
72
|
|
|
243
|
-
|
|
73
|
+
Copy this into your AI's System Prompt to enable the Loop Breaker and Deep Thinking protocols:
|
|
244
74
|
|
|
245
75
|
```markdown
|
|
246
76
|
# 🤖 System Instruction: Ultimate Deep Engineer (Sequential Thinking Extended)
|
|
@@ -252,20 +82,41 @@ You are a Senior AI Software Engineer equipped with the Sequential Thinking MCP
|
|
|
252
82
|
- **Atomic Analysis:** Use `thoughtType: 'analysis'` to break every request into atomic requirements and constraints before proposing solutions.
|
|
253
83
|
- **Mandatory Reflexion:** Use `thoughtType: 'reflexion'` frequently to critique your own logic, identify potential edge cases, and challenge your assumptions.
|
|
254
84
|
- **Tree of Thoughts:** For critical architectural decisions, use branching to explore multiple paths (Conservative vs. Aggressive) and evaluate them using `thoughtType: 'evaluation'`.
|
|
255
|
-
- **Loop Breaker
|
|
85
|
+
- **Loop Breaker (Auto-Correction):**
|
|
86
|
+
- **Self-Monitoring:** If you detect yourself repeating a failed strategy or stuck in a loop -> **STOP**.
|
|
87
|
+
- **Mandatory Branching:** You MUST create a new branch (`branchFromThought`) to explore a *radically different* approach.
|
|
88
|
+
- **Explicit Statement:** State "Stuck detected. Branching to Strategy B..." before proceeding.
|
|
256
89
|
- **Verified Completion:** Only set `nextThoughtNeeded: false` when the solution is definitive, verified, and follows project standards.
|
|
257
90
|
|
|
258
|
-
##
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
91
|
+
## 🛠️ TOOL USAGE TRIGGERS (Context-Action Mapping)
|
|
92
|
+
**You MUST follow these explicit triggers:**
|
|
93
|
+
|
|
94
|
+
1. **Unknown/New Project Context:**
|
|
95
|
+
- **Trigger:** Start of session or new task.
|
|
96
|
+
- **Action:** `build_project_graph` -> `read_file` (README/config).
|
|
97
|
+
- **Goal:** Map the territory before moving.
|
|
98
|
+
|
|
99
|
+
2. **Coding/Refactoring Tasks:**
|
|
100
|
+
- **Trigger:** Request to write or edit code.
|
|
101
|
+
- **Action:** `deep_code_analyze` (on target files) -> `sequentialthinking` (Plan) -> `deep_code_edit` or `edit_file`.
|
|
102
|
+
- **Constraint:** NEVER edit code without reading the file and its related context first.
|
|
103
|
+
|
|
104
|
+
3. **Debugging/Error Fixing:**
|
|
105
|
+
- **Trigger:** User reports a bug or error.
|
|
106
|
+
- **Action:** `read_file` (logs/code) OR `diagnose_error_screenshot` -> `sequentialthinking` (Hypothesis) -> `codebase_investigator` (Verification).
|
|
107
|
+
- **Constraint:** Do not guess. Evidence first.
|
|
108
|
+
|
|
109
|
+
4. **Unknown Libraries/Docs:**
|
|
110
|
+
- **Trigger:** Need to use a specific library/tool you are unsure about.
|
|
111
|
+
- **Action:** `web_search` -> `read_webpage` or `get_code_context_exa`.
|
|
112
|
+
- **Constraint:** Do not hallucinate APIs.
|
|
262
113
|
|
|
263
114
|
## 💻 DEEP CODING WORKFLOW
|
|
264
|
-
1.
|
|
265
|
-
2.
|
|
266
|
-
3.
|
|
267
|
-
4.
|
|
268
|
-
5.
|
|
115
|
+
1. **Discovery:** `build_project_graph` -> Identify entry points and core logic.
|
|
116
|
+
2. **Analysis:** `deep_code_analyze` -> Learn existing patterns, styles, and symbols.
|
|
117
|
+
3. **Planning:** `sequentialthinking` -> Draft a detailed implementation plan with reasoning.
|
|
118
|
+
4. **Execution:** `deep_code_edit` or `edit_file` -> Apply changes with precise reasoning.
|
|
119
|
+
5. **Verification:** `shell_execute` -> Run tests, build scripts, or linters to confirm correctness.
|
|
269
120
|
|
|
270
121
|
## 🛡 SAFETY & PRECISION
|
|
271
122
|
- **Surgical Edits:** Prefer `edit_file` or `deep_code_edit` over `write_file` for existing files to minimize risk.
|
|
@@ -278,7 +129,5 @@ You are a Senior AI Software Engineer equipped with the Sequential Thinking MCP
|
|
|
278
129
|
- **Session Continuity:** Your thought history is saved. If you restart, review the history to maintain context.
|
|
279
130
|
```
|
|
280
131
|
|
|
281
|
-
---
|
|
282
|
-
|
|
283
132
|
## License
|
|
284
|
-
MIT -
|
|
133
|
+
MIT - Developed by @gotza02/sequential-thinking
|
package/dist/graph.js
CHANGED
|
@@ -18,9 +18,11 @@ export class ProjectKnowledgeGraph {
|
|
|
18
18
|
symbols: []
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
// Step 2: Parse imports and build edges
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
// Step 2: Parse imports and build edges in parallel with concurrency limit
|
|
22
|
+
const CONCURRENCY_LIMIT = 20;
|
|
23
|
+
for (let i = 0; i < files.length; i += CONCURRENCY_LIMIT) {
|
|
24
|
+
const chunk = files.slice(i, i + CONCURRENCY_LIMIT);
|
|
25
|
+
await Promise.all(chunk.map(file => this.parseFile(file)));
|
|
24
26
|
}
|
|
25
27
|
return {
|
|
26
28
|
nodeCount: this.nodes.size,
|
|
@@ -126,27 +128,52 @@ export class ProjectKnowledgeGraph {
|
|
|
126
128
|
const imports = [];
|
|
127
129
|
const symbols = [];
|
|
128
130
|
const ext = path.extname(filePath);
|
|
129
|
-
// Basic Regex for generic symbols and imports
|
|
130
131
|
if (ext === '.py') {
|
|
131
|
-
// Python
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
// 1. Python Imports
|
|
133
|
+
// Handle: import os, sys
|
|
134
|
+
const simpleImportMatches = content.matchAll(/^\s*import\s+([^#\n]+)/gm);
|
|
135
|
+
for (const match of simpleImportMatches) {
|
|
136
|
+
match[1].split(',').forEach(s => {
|
|
137
|
+
const clean = s.trim().split(/\s+/)[0]; // Handle 'import x as y'
|
|
138
|
+
if (clean)
|
|
139
|
+
imports.push(clean);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Handle: from .module import func OR from package.module import func
|
|
143
|
+
const fromImportMatches = content.matchAll(/^\s*from\s+([.a-zA-Z0-9_]+)\s+import/gm);
|
|
144
|
+
for (const match of fromImportMatches)
|
|
134
145
|
imports.push(match[1]);
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
// 2. Python Symbols (Only top-level defs/classes to avoid nested methods)
|
|
147
|
+
const topLevelFuncMatches = content.matchAll(/^def\s+([a-zA-Z0-9_]+)/gm);
|
|
148
|
+
for (const match of topLevelFuncMatches)
|
|
137
149
|
symbols.push(`def:${match[1]}`);
|
|
138
|
-
const
|
|
139
|
-
for (const match of
|
|
150
|
+
const topLevelClassMatches = content.matchAll(/^class\s+([a-zA-Z0-9_]+)/gm);
|
|
151
|
+
for (const match of topLevelClassMatches)
|
|
140
152
|
symbols.push(`class:${match[1]}`);
|
|
141
153
|
}
|
|
142
154
|
else if (ext === '.go') {
|
|
143
|
-
// Go
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
// 1. Go Imports
|
|
156
|
+
// Single line: import "fmt"
|
|
157
|
+
const singleImportMatches = content.matchAll(/import\s+"([^"]+)"/g);
|
|
158
|
+
for (const match of singleImportMatches)
|
|
146
159
|
imports.push(match[1]);
|
|
147
|
-
|
|
160
|
+
// Block: import ( "fmt"; "os" )
|
|
161
|
+
const blockImportMatches = content.matchAll(/import\s+\(([\s\S]*?)\)/g);
|
|
162
|
+
for (const match of blockImportMatches) {
|
|
163
|
+
const block = match[1];
|
|
164
|
+
const innerMatches = block.matchAll(/"([^"]+)"/g);
|
|
165
|
+
for (const im of innerMatches)
|
|
166
|
+
imports.push(im[1]);
|
|
167
|
+
}
|
|
168
|
+
// 2. Go Symbols
|
|
169
|
+
// Functions: func Name(...)
|
|
170
|
+
const funcMatches = content.matchAll(/^func\s+([a-zA-Z0-9_]+)/gm);
|
|
148
171
|
for (const match of funcMatches)
|
|
149
172
|
symbols.push(`func:${match[1]}`);
|
|
173
|
+
// Types: type Name struct/interface
|
|
174
|
+
const typeMatches = content.matchAll(/^type\s+([a-zA-Z0-9_]+)\s+(?:struct|interface)/gm);
|
|
175
|
+
for (const match of typeMatches)
|
|
176
|
+
symbols.push(`type:${match[1]}`);
|
|
150
177
|
}
|
|
151
178
|
await this.finalizeFileNodes(filePath, imports, symbols);
|
|
152
179
|
}
|
|
@@ -175,6 +202,12 @@ export class ProjectKnowledgeGraph {
|
|
|
175
202
|
this.nodes.get(resolvedPath)?.importedBy.push(filePath);
|
|
176
203
|
}
|
|
177
204
|
}
|
|
205
|
+
else {
|
|
206
|
+
// If we can't resolve to a local file, keep the original import string as an external dependency
|
|
207
|
+
if (!currentNode.imports.includes(importPath)) {
|
|
208
|
+
currentNode.imports.push(importPath);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
178
211
|
}
|
|
179
212
|
}
|
|
180
213
|
async resolvePath(dir, relativePath) {
|
|
@@ -222,7 +255,12 @@ export class ProjectKnowledgeGraph {
|
|
|
222
255
|
return null;
|
|
223
256
|
return {
|
|
224
257
|
path: node.path,
|
|
225
|
-
imports: node.imports.map(p =>
|
|
258
|
+
imports: node.imports.map(p => {
|
|
259
|
+
if (path.isAbsolute(p)) {
|
|
260
|
+
return path.relative(this.rootDir, p);
|
|
261
|
+
}
|
|
262
|
+
return p; // Return as is for external libraries
|
|
263
|
+
}),
|
|
226
264
|
importedBy: node.importedBy.map(p => path.relative(this.rootDir, p)),
|
|
227
265
|
symbols: node.symbols
|
|
228
266
|
};
|
package/dist/lib.js
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import * as fs from 'fs/promises';
|
|
3
3
|
import { existsSync, readFileSync } from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
+
import { AsyncMutex } from './utils.js';
|
|
5
6
|
export class SequentialThinkingServer {
|
|
6
7
|
thoughtHistory = [];
|
|
7
8
|
branches = {};
|
|
@@ -10,6 +11,7 @@ export class SequentialThinkingServer {
|
|
|
10
11
|
delayMs;
|
|
11
12
|
isSaving = false;
|
|
12
13
|
hasPendingSave = false;
|
|
14
|
+
saveMutex = new AsyncMutex();
|
|
13
15
|
constructor(storagePath = 'thoughts_history.json', delayMs = 0) {
|
|
14
16
|
this.disableThoughtLogging = (process.env.DISABLE_THOUGHT_LOGGING || "").toLowerCase() === "true";
|
|
15
17
|
this.storagePath = path.resolve(storagePath);
|
|
@@ -20,11 +22,40 @@ export class SequentialThinkingServer {
|
|
|
20
22
|
try {
|
|
21
23
|
if (existsSync(this.storagePath)) {
|
|
22
24
|
const data = readFileSync(this.storagePath, 'utf-8');
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
if (!data.trim())
|
|
26
|
+
return; // Empty file is fine
|
|
27
|
+
try {
|
|
28
|
+
const history = JSON.parse(data);
|
|
29
|
+
if (Array.isArray(history)) {
|
|
30
|
+
this.thoughtHistory = [];
|
|
31
|
+
this.branches = {};
|
|
32
|
+
history.forEach(thought => {
|
|
33
|
+
if (thought && typeof thought === 'object' && 'thought' in thought) {
|
|
34
|
+
this.addToMemory(thought);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (parseError) {
|
|
40
|
+
console.error(`Error parsing history from ${this.storagePath}, attempting recovery:`, parseError);
|
|
41
|
+
// Basic Recovery: Try to fix truncated JSON by finding the last complete object
|
|
42
|
+
// This is a simple heuristic: find the last '}' that closes a thought object
|
|
43
|
+
const lastBrace = data.lastIndexOf('}');
|
|
44
|
+
if (lastBrace !== -1) {
|
|
45
|
+
try {
|
|
46
|
+
const recoveredData = data.substring(0, lastBrace + 1).trim();
|
|
47
|
+
// If it's part of an array, we might need to add ']'
|
|
48
|
+
const attemptedJson = recoveredData.endsWith(']') ? recoveredData : recoveredData + ']';
|
|
49
|
+
const history = JSON.parse(attemptedJson);
|
|
50
|
+
if (Array.isArray(history)) {
|
|
51
|
+
history.forEach(thought => this.addToMemory(thought));
|
|
52
|
+
console.log(`Successfully recovered ${history.length} thoughts.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (recoveryError) {
|
|
56
|
+
console.error('Recovery failed, starting with empty history.');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
28
59
|
}
|
|
29
60
|
}
|
|
30
61
|
}
|
|
@@ -33,27 +64,17 @@ export class SequentialThinkingServer {
|
|
|
33
64
|
}
|
|
34
65
|
}
|
|
35
66
|
async saveHistory() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Atomic write: write to tmp then rename
|
|
43
|
-
const tmpPath = `${this.storagePath}.tmp`;
|
|
44
|
-
await fs.writeFile(tmpPath, JSON.stringify(this.thoughtHistory, null, 2), 'utf-8');
|
|
45
|
-
await fs.rename(tmpPath, this.storagePath);
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
console.error(`Error saving history to ${this.storagePath}:`, error);
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
this.isSaving = false;
|
|
52
|
-
if (this.hasPendingSave) {
|
|
53
|
-
this.hasPendingSave = false;
|
|
54
|
-
this.saveHistory();
|
|
67
|
+
// Use mutex to ensure only one file operation happens at a time
|
|
68
|
+
await this.saveMutex.dispatch(async () => {
|
|
69
|
+
try {
|
|
70
|
+
const tmpPath = `${this.storagePath}.tmp`;
|
|
71
|
+
await fs.writeFile(tmpPath, JSON.stringify(this.thoughtHistory, null, 2), 'utf-8');
|
|
72
|
+
await fs.rename(tmpPath, this.storagePath);
|
|
55
73
|
}
|
|
56
|
-
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`Error saving history to ${this.storagePath}:`, error);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
57
78
|
}
|
|
58
79
|
async clearHistory() {
|
|
59
80
|
this.thoughtHistory = [];
|
|
@@ -74,13 +95,18 @@ export class SequentialThinkingServer {
|
|
|
74
95
|
// Remove the range and insert summary
|
|
75
96
|
const removedCount = endIndex - startIndex + 1;
|
|
76
97
|
this.thoughtHistory.splice(startIndex - 1, removedCount, summaryThought);
|
|
77
|
-
//
|
|
98
|
+
// Update all thoughts to reflect the new total and renumber subsequent ones
|
|
99
|
+
const newTotal = this.thoughtHistory.length;
|
|
78
100
|
const shiftAmount = removedCount - 1;
|
|
79
|
-
for (let i =
|
|
101
|
+
for (let i = 0; i < this.thoughtHistory.length; i++) {
|
|
80
102
|
const t = this.thoughtHistory[i];
|
|
81
|
-
// Update
|
|
82
|
-
t.
|
|
83
|
-
//
|
|
103
|
+
// 1. Update total thoughts for everyone
|
|
104
|
+
t.totalThoughts = newTotal;
|
|
105
|
+
// 2. Renumber thoughts that came after the summarized range
|
|
106
|
+
if (i >= startIndex) {
|
|
107
|
+
t.thoughtNumber -= shiftAmount;
|
|
108
|
+
}
|
|
109
|
+
// 3. Update references for all thoughts
|
|
84
110
|
if (t.branchFromThought) {
|
|
85
111
|
if (t.branchFromThought > endIndex) {
|
|
86
112
|
t.branchFromThought -= shiftAmount;
|
|
@@ -89,7 +115,6 @@ export class SequentialThinkingServer {
|
|
|
89
115
|
t.branchFromThought = startIndex; // Point to summary
|
|
90
116
|
}
|
|
91
117
|
}
|
|
92
|
-
// Update references (revisesThought)
|
|
93
118
|
if (t.revisesThought) {
|
|
94
119
|
if (t.revisesThought > endIndex) {
|
|
95
120
|
t.revisesThought -= shiftAmount;
|
package/dist/tools/web.js
CHANGED
|
@@ -136,7 +136,41 @@ export function registerWebTools(server) {
|
|
|
136
136
|
const article = reader.parse();
|
|
137
137
|
if (!article)
|
|
138
138
|
throw new Error("Could not parse article content");
|
|
139
|
-
const turndownService = new TurndownService(
|
|
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
|
+
});
|
|
140
174
|
let markdown = turndownService.turndown(article.content || "");
|
|
141
175
|
if (markdown.length > 20000) {
|
|
142
176
|
markdown = markdown.substring(0, 20000) + "\n...(truncated)";
|
package/dist/utils.js
CHANGED
|
@@ -52,13 +52,10 @@ export function validatePath(requestedPath) {
|
|
|
52
52
|
return absolutePath;
|
|
53
53
|
}
|
|
54
54
|
function isPrivateIP(ip) {
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
// 192.168.0.0/16
|
|
60
|
-
// 0.0.0.0/8
|
|
61
|
-
const parts = ip.split('.').map(Number);
|
|
55
|
+
// Remove brackets if present (IPv6 literals in hostnames)
|
|
56
|
+
const cleanIp = ip.replace(/^\[|\]$/g, '');
|
|
57
|
+
// IPv4 check
|
|
58
|
+
const parts = cleanIp.split('.').map(Number);
|
|
62
59
|
if (parts.length === 4) {
|
|
63
60
|
if (parts[0] === 127)
|
|
64
61
|
return true;
|
|
@@ -72,13 +69,19 @@ function isPrivateIP(ip) {
|
|
|
72
69
|
return true;
|
|
73
70
|
return false;
|
|
74
71
|
}
|
|
75
|
-
// IPv6
|
|
76
|
-
|
|
72
|
+
// IPv6 check
|
|
73
|
+
// Normalize: remove leading/trailing colons and convert to lowercase
|
|
74
|
+
const normalized = cleanIp.toLowerCase();
|
|
75
|
+
// Loopback: ::1, 0:0:0:0:0:0:0:1
|
|
76
|
+
if (normalized === '::1' || normalized === '::' || normalized.replace(/:0/g, ':').replace(/^0+/, '') === '::1')
|
|
77
77
|
return true;
|
|
78
|
-
|
|
78
|
+
// Private/Link-local ranges
|
|
79
|
+
if (normalized.startsWith('fc') || normalized.startsWith('fd'))
|
|
79
80
|
return true; // Unique Local
|
|
80
|
-
if (
|
|
81
|
+
if (normalized.startsWith('fe80'))
|
|
81
82
|
return true; // Link Local
|
|
83
|
+
if (normalized.startsWith('::ffff:7f') || normalized.startsWith('::ffff:10.') || normalized.startsWith('::ffff:192.168.'))
|
|
84
|
+
return true; // IPv4-mapped private
|
|
82
85
|
return false;
|
|
83
86
|
}
|
|
84
87
|
export async function validatePublicUrl(urlString) {
|
|
@@ -86,7 +89,11 @@ export async function validatePublicUrl(urlString) {
|
|
|
86
89
|
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
87
90
|
throw new Error('Invalid protocol. Only http and https are allowed.');
|
|
88
91
|
}
|
|
89
|
-
//
|
|
92
|
+
// 1. Direct check for IP literals in hostname
|
|
93
|
+
if (isPrivateIP(parsed.hostname)) {
|
|
94
|
+
throw new Error(`Access denied: Host '${parsed.hostname}' is a private IP`);
|
|
95
|
+
}
|
|
96
|
+
// 2. Resolve and check all resulting IPs
|
|
90
97
|
try {
|
|
91
98
|
const addresses = await dns.lookup(parsed.hostname, { all: true });
|
|
92
99
|
for (const addr of addresses) {
|
|
@@ -100,7 +107,7 @@ export async function validatePublicUrl(urlString) {
|
|
|
100
107
|
if (error instanceof Error && error.message.startsWith('Access denied')) {
|
|
101
108
|
throw error;
|
|
102
109
|
}
|
|
103
|
-
// Ignore DNS errors
|
|
110
|
+
// Ignore other DNS errors, let fetch handle them
|
|
104
111
|
}
|
|
105
112
|
}
|
|
106
113
|
class Logger {
|