@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 CHANGED
@@ -1,246 +1,76 @@
1
- # Sequential Thinking MCP Server (Extended Edition) 🧠✨
1
+ # Sequential Thinking MCP (Extended) 🧠
2
2
 
3
- **MCP Server ที่ยกระดับ AI ให้เป็นวิศวกรซอฟต์แวร์อัจฉริยะ ด้วยระบบ Deepest Thinking, การวิเคราะห์ Codebase เชิงลึก และฐานข้อมูลความรู้ (Code Database)**
3
+ Advanced MCP Server enabling AI to act as a Software Engineer with Deep Thinking, Codebase Graph Analysis, and Persistent Memory.
4
4
 
5
- > **🛡️ Battle-Tested:** ผ่านการทดสอบ **Chaos Engineering** และ **Stress Testing** รองรับโหลดหนักและกู้คืนตัวเองจากความเสียหายได้ (Auto-Repair)
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
- โปรเจกต์นี้คือส่วนขยายขั้นสูงของ Sequential Thinking ที่รวมเอาความสามารถในการวางแผนที่เป็นระบบ, การหาข้อมูลทั่วโลก (Web Search), การสร้างแผนผังความสัมพันธ์ของโค้ด (Dependency Graph), การจัดการหน่วยความจำระยะยาว และ **ฐานข้อมูลความรู้โค้ด (Code Database)** เข้าด้วยกัน เพื่อให้ AI สามารถทำงานที่ซับซ้อนได้อย่างอิสระและแม่นยำ
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
- "command": "node",
197
-
198
- "args": ["/ทาง/ไป/ยัง/node_modules/@gotza02/sequential-thinking/dist/index.js"]
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": "npx",
212
- "args": ["-y", "@gotza02/sequential-thinking"],
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
- > **Note:** สำหรับ Google Search คุณจำเป็นต้องมีทั้ง **API Key** และ **Search Engine ID (CX)** ซึ่งสามารถสร้างได้จาก [Google Custom Search Engine](https://programmablesearchengine.google.com/)
228
-
229
- ---
230
-
231
- ## 🛠️ ติดตั้งคำสั่งระบบ (System Instruction Setup)
232
-
233
- คุณสามารถติดตั้งคำสั่งระบบ **Ultimate Deep Engineer** ให้กับ Gemini และ Claude ได้โดยอัตโนมัติด้วย Script นี้:
234
-
235
- ```bash
236
- chmod +x install-ultimate-engineer.sh && ./install-ultimate-engineer.sh
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
- ให้ Copy ข้อความด้านล่างนี้ไปใส่ใน **System Prompt** ของ AI Agent เพื่อประสิทธิภาพสูงสุด:
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:** If a bug persists after 2 attempts or reasoning loops, MUST create a new branch (`branchFromThought`) to change strategy completely.
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
- ## 🏗 CODEBASE INTELLIGENCE PROTOCOL
259
- - **Map the Territory:** Run `build_project_graph` immediately upon entering a new project or after significant changes.
260
- - **Context is King:** Use `deep_code_analyze` before creating or editing any code. Never rely on partial file content; always understand the relationships (imports/exports).
261
- - **Dependency Awareness:** Use `get_file_relationships` to ensure your changes do not break downstream dependents.
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. **Discovery:** `build_project_graph` -> Identify entry points and core logic.
265
- 2. **Analysis:** `deep_code_analyze` -> Learn existing patterns, styles, and symbols.
266
- 3. **Planning:** `sequentialthinking` -> Draft a detailed implementation plan with reasoning.
267
- 4. **Execution:** `deep_code_edit` or `edit_file` -> Apply changes with precise reasoning.
268
- 5. **Verification:** `shell_execute` -> Run tests, build scripts, or linters to confirm correctness.
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 - พัฒนาโดย @gotza02/sequential-thinking
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
- for (const file of files) {
23
- await this.parseFile(file);
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: import x, from x import y, def func, class Cls
132
- const importMatches = content.matchAll(/^\s*(?:import|from)\s+([a-zA-Z0-9_.]+)/gm);
133
- for (const match of importMatches)
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
- const funcMatches = content.matchAll(/^\s*def\s+([a-zA-Z0-9_]+)/gm);
136
- for (const match of funcMatches)
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 classMatches = content.matchAll(/^\s*class\s+([a-zA-Z0-9_]+)/gm);
139
- for (const match of classMatches)
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: import "x", func Name, type Name
144
- const importMatches = content.matchAll(/import\s+"([^"]+)"/g);
145
- for (const match of importMatches)
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
- const funcMatches = content.matchAll(/^\s*func\s+([a-zA-Z0-9_]+)/gm);
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 => path.relative(this.rootDir, 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
- const history = JSON.parse(data);
24
- if (Array.isArray(history)) {
25
- this.thoughtHistory = []; // Reset to avoid duplicates
26
- this.branches = {};
27
- history.forEach(thought => this.addToMemory(thought));
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
- if (this.isSaving) {
37
- this.hasPendingSave = true;
38
- return;
39
- }
40
- this.isSaving = true;
41
- try {
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
- // Renumber subsequent thoughts and update references
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 = startIndex; i < this.thoughtHistory.length; i++) {
101
+ for (let i = 0; i < this.thoughtHistory.length; i++) {
80
102
  const t = this.thoughtHistory[i];
81
- // Update own number
82
- t.thoughtNumber -= shiftAmount;
83
- // Update references (branchFromThought)
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
- // IPv4 ranges
56
- // 127.0.0.0/8
57
- // 10.0.0.0/8
58
- // 172.16.0.0/12
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 checks (simple check for loopback/link-local)
76
- if (ip === '::1' || ip === '::')
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
- if (ip.startsWith('fc') || ip.startsWith('fd'))
78
+ // Private/Link-local ranges
79
+ if (normalized.startsWith('fc') || normalized.startsWith('fd'))
79
80
  return true; // Unique Local
80
- if (ip.startsWith('fe80'))
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
- // Attempt to resolve hostname
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 here, let fetch handle them (or fail safely)
110
+ // Ignore other DNS errors, let fetch handle them
104
111
  }
105
112
  }
106
113
  class Logger {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotza02/sequential-thinking",
3
- "version": "2026.2.12",
3
+ "version": "2026.2.14",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },