universal-ast-mapper 1.28.0 → 2.0.0
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/BLUEPRINT.md +230 -230
- package/CHANGELOG.md +466 -338
- package/README.md +878 -878
- package/package.json +48 -47
- package/scripts/install-skill.mjs +187 -187
- package/dist/analysis.js +0 -134
- package/dist/callgraph.js +0 -467
- package/dist/check.js +0 -112
- package/dist/cli.js +0 -1275
- package/dist/complexity.js +0 -98
- package/dist/config.js +0 -53
- package/dist/contextpack.js +0 -79
- package/dist/coupling.js +0 -35
- package/dist/crosslang.js +0 -425
- package/dist/diskcache.js +0 -97
- package/dist/explorer.js +0 -123
- package/dist/extractors/c.js +0 -204
- package/dist/extractors/common.js +0 -56
- package/dist/extractors/cpp.js +0 -272
- package/dist/extractors/csharp.js +0 -209
- package/dist/extractors/go.js +0 -212
- package/dist/extractors/java.js +0 -152
- package/dist/extractors/kotlin.js +0 -159
- package/dist/extractors/php.js +0 -208
- package/dist/extractors/python.js +0 -153
- package/dist/extractors/ruby.js +0 -146
- package/dist/extractors/rust.js +0 -249
- package/dist/extractors/swift.js +0 -192
- package/dist/extractors/typescript.js +0 -577
- package/dist/gitdiff.js +0 -178
- package/dist/graph-analysis.js +0 -279
- package/dist/graph.js +0 -165
- package/dist/html.js +0 -326
- package/dist/index.js +0 -1408
- package/dist/layers.js +0 -36
- package/dist/modulecoupling.js +0 -0
- package/dist/parser.js +0 -84
- package/dist/pool.js +0 -114
- package/dist/prompts.js +0 -67
- package/dist/registry.js +0 -87
- package/dist/report.js +0 -232
- package/dist/resolver.js +0 -222
- package/dist/roots.js +0 -47
- package/dist/search.js +0 -68
- package/dist/semantic.js +0 -365
- package/dist/sfc.js +0 -27
- package/dist/skeleton.js +0 -132
- package/dist/sourcemap.js +0 -60
- package/dist/testmap.js +0 -167
- package/dist/tsconfig.js +0 -212
- package/dist/typeflow.js +0 -124
- package/dist/types.js +0 -5
- package/dist/unused-params.js +0 -127
- package/dist/worker.js +0 -27
- package/dist/workspace.js +0 -330
package/BLUEPRINT.md
CHANGED
|
@@ -1,230 +1,230 @@
|
|
|
1
|
-
# Universal AST Mapper — MCP Server Blueprint (ฉบับเกลา v2)
|
|
2
|
-
|
|
3
|
-
> เอกสารนี้เป็น Blueprint ที่ปรับปรุงจากร่างแรก โดยคงแนวคิดหลักที่ดีอยู่แล้วไว้
|
|
4
|
-
> (สถาปัตยกรรม 4 โมดูล, Dual Output, Token Compression, Factory Pattern)
|
|
5
|
-
> และแก้ไขจุดอ่อนสำคัญ 1 จุดที่กระทบทั้งแผน คือ **กลยุทธ์ของ Parser Engine**
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 0. สรุปการเปลี่ยนแปลงสำคัญจากร่างแรก (Executive Summary)
|
|
10
|
-
|
|
11
|
-
| ประเด็น | ร่างแรก | ฉบับเกลานี้ | เหตุผล |
|
|
12
|
-
|---|---|---|---|
|
|
13
|
-
| **Parser หลัก** | Regex ก่อน → Tree-sitter ใน Phase 4 | **Tree-sitter ตั้งแต่ Phase 1** | Regex อ่าน nested scope / generics / comment / string ที่มี `{ }` ไม่ได้แม่น ทำให้ output เชื่อถือไม่ได้ และต้องรื้อทิ้งทั้งหมดใน Phase 4 อยู่ดี |
|
|
14
|
-
| **คำเรียก output** | "AST" | **"Code Skeleton / Symbol Map"** | สิ่งที่เราต้องการจริงคือ "โครงกระดูก" (class/func/struct + ตำแหน่ง + การซ้อน) ไม่ใช่ AST เต็มทุก node |
|
|
15
|
-
| **การเพิ่มภาษา** | เขียนชุด Regex ใหม่ทุกภาษา | **เพิ่มไฟล์ grammar + query (.scm)** | กลายเป็นงาน "config" แทน "เขียน logic ใหม่" — ขยายภาษาได้เร็วและพังยาก |
|
|
16
|
-
| **ไฟล์ HTML** | เซฟข้างไฟล์ต้นฉบับ | **เซฟใน output dir ที่กำหนดได้ + แนะนำ gitignore** | กันไฟล์ขยะรกใน repo และ git diff เพี้ยน |
|
|
17
|
-
| **"ไฟล์เด้งขึ้นมาเอง"** | สื่อว่าระบบเปิดไฟล์ให้อัตโนมัติ | **คืน path กลับไปให้ AI แล้วให้ AI ส่งลิงก์ให้ผู้ใช้** | MCP server สั่งเปิดไฟล์บนเดสก์ท็อปแบบข้ามแพลตฟอร์มไม่ได้เชื่อถือได้ |
|
|
18
|
-
|
|
19
|
-
**หัวใจของการเกลา:** เราไม่ได้ทิ้งไอเดียเดิม แต่ย้าย "ของแข็ง" (Tree-sitter) มาไว้เป็นรากฐานตั้งแต่ต้น แทนที่จะสร้างรากฐานด้วยของที่รู้อยู่แล้วว่าจะต้องทุบทิ้ง
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## 1. ทำไมต้องเลิกใช้ Regex เป็นแกนหลัก
|
|
24
|
-
|
|
25
|
-
Regex เหมาะกับการ "จับ pattern บรรทัดเดียว" แต่ภาษาโปรแกรมเป็นโครงสร้างซ้อนชั้น (recursive) ซึ่ง Regex จัดการไม่ได้โดยธรรมชาติ ตัวอย่างเคสที่ Regex มักพลาด:
|
|
26
|
-
|
|
27
|
-
- **Nested scope:** ฟังก์ชันซ้อนในฟังก์ชัน, method ใน class ใน module — Regex บอก "ใครอยู่ใต้ใคร" ไม่ได้แม่น
|
|
28
|
-
- **Multi-line declaration:** signature ที่ขึ้นบรรทัดใหม่, generics ยาวๆ เช่น `func Map[T any, R any](...)`
|
|
29
|
-
- **False positive:** คำว่า `class` / `func` ที่อยู่ใน comment, ใน string literal, หรือใน template string
|
|
30
|
-
- **Braces ใน string:** `const x = "if (a) { b }"` ทำให้การนับวงเล็บเพี้ยน
|
|
31
|
-
|
|
32
|
-
**ทางออก: Tree-sitter** — เป็น parser generator ที่ GitHub, Neovim ใช้สำหรับ code navigation จริง
|
|
33
|
-
|
|
34
|
-
- รองรับ ~100+ ภาษา ผ่าน **API เดียวกัน** (ตรงสเปก "Universal" ของเราเป๊ะ)
|
|
35
|
-
- **Error-tolerant:** โค้ดพังบางส่วนก็ยัง parse ส่วนที่เหลือได้ (สำคัญมากตอนโค้ดกำลังเขียนค้าง)
|
|
36
|
-
- มี **Query API (`.scm` files)** สำหรับ "ดึงเฉพาะ node ที่ต้องการ" (class, func, struct) โดยไม่ต้องเดิน tree เอง
|
|
37
|
-
- เร็วระดับ incremental parsing
|
|
38
|
-
|
|
39
|
-
> **หมายเหตุ:** Regex ยังมีที่ยืนเล็กๆ ในฐานะ *fallback ขั้นสุดท้าย* สำหรับไฟล์ที่ยังไม่มี grammar เท่านั้น (เช่นดึงแค่ comment header) — ไม่ใช่แกนหลัก
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## 2. สถาปัตยกรรมระบบ (System Architecture)
|
|
44
|
-
|
|
45
|
-
ยังคงโครง 4 โมดูลของร่างแรกไว้ (ดีอยู่แล้ว) แต่เปลี่ยนไส้ในของ Parser Engine
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
49
|
-
│ 1. Transport Layer (MCP / JSON-RPC 2.0 over stdio) │
|
|
50
|
-
│ ใช้ @modelcontextprotocol/sdk จัดการ handshake/protocol │
|
|
51
|
-
└───────────────┬─────────────────────────────────────────────┘
|
|
52
|
-
│ tool call: generate_skeleton(path, opts)
|
|
53
|
-
┌───────────────▼─────────────────────────────────────────────┐
|
|
54
|
-
│ 2. Controller / Router │
|
|
55
|
-
│ - resolve path, ตรวจ security (อยู่ใน allowed root ไหม) │
|
|
56
|
-
│ - อ่านนามสกุล → เลือก Language Adapter ผ่าน Registry │
|
|
57
|
-
│ - จัดการ fallback เมื่อไม่รู้จักนามสกุล │
|
|
58
|
-
└───────────────┬─────────────────────────────────────────────┘
|
|
59
|
-
│
|
|
60
|
-
┌───────────────▼─────────────────────────────────────────────┐
|
|
61
|
-
│ 3. Parser Engine (Core) │
|
|
62
|
-
│ - Tree-sitter loader (โหลด grammar .wasm ตามภาษา) │
|
|
63
|
-
│ - รัน Query (.scm) ดึง symbol nodes │
|
|
64
|
-
│ - Normalizer: แปลงเป็น Standard Skeleton JSON (schema กลาง) │
|
|
65
|
-
└───────────────┬─────────────────────────────────────────────┘
|
|
66
|
-
│ Standard Skeleton JSON
|
|
67
|
-
┌───────┴────────┐
|
|
68
|
-
▼ ▼
|
|
69
|
-
┌───────────────┐ ┌──────────────────────────────────┐
|
|
70
|
-
│ 4a. Data │ │ 4b. HTML Renderer │
|
|
71
|
-
│ Formatter │ │ - สวม JSON เข้า template (Tailwind)│
|
|
72
|
-
│ (JSON ย่อ →AI) │ │ - collapsible tree, self-contained│
|
|
73
|
-
│ │ │ - เซฟไฟล์ → คืน path กลับไปให้ AI │
|
|
74
|
-
└───────────────┘ └──────────────────────────────────┘
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
**Language Registry (หัวใจการขยายภาษา):** map `นามสกุล → { grammar, queryFile, kindMap }`
|
|
78
|
-
การเพิ่มภาษาใหม่ = เพิ่ม 1 entry + 1 ไฟล์ query + ตาราง mapping kind ไม่ต้องแตะ core เลย (นี่คือ Factory/Strategy pattern ที่ร่างแรกตั้งใจไว้ แต่ทำได้สะอาดกว่ามากเมื่อ logic อยู่ใน query file ไม่ใช่ใน regex)
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## 3. Standard Skeleton JSON (Schema กลาง)
|
|
83
|
-
|
|
84
|
-
นี่คือ "ภาษากลาง" ที่ทุก parser ต้องคืนออกมาให้หน้าตาเหมือนกัน ไม่ว่าต้นทางจะเป็นภาษาอะไร — AI จะได้เห็น vocabulary ชุดเดียว
|
|
85
|
-
|
|
86
|
-
```jsonc
|
|
87
|
-
{
|
|
88
|
-
"schemaVersion": "1.0",
|
|
89
|
-
"file": "services/inventory.go",
|
|
90
|
-
"language": "go",
|
|
91
|
-
"generatedAt": "2026-05-27T10:00:00Z",
|
|
92
|
-
"parser": { "engine": "tree-sitter", "grammar": "go@0.21.0" },
|
|
93
|
-
"symbolCount": 12,
|
|
94
|
-
"symbols": [
|
|
95
|
-
{
|
|
96
|
-
"name": "InventoryService",
|
|
97
|
-
"kind": "struct", // normalized enum (ดูด้านล่าง)
|
|
98
|
-
"signature": null,
|
|
99
|
-
"visibility": "public", // public | private (จากตัวพิมพ์ใหญ่/_underscore ตามภาษา)
|
|
100
|
-
"range": { "startLine": 14, "endLine": 22 },
|
|
101
|
-
"doc": "Handles stock-level operations", // leading comment/docstring (optional)
|
|
102
|
-
"children": [ // ⭐ ใช้ tree (children) ไม่ใช่ scope แบบ string
|
|
103
|
-
{
|
|
104
|
-
"name": "ReserveStock",
|
|
105
|
-
"kind": "method",
|
|
106
|
-
"signature": "(ctx context.Context, sku string, qty int) error",
|
|
107
|
-
"visibility": "public",
|
|
108
|
-
"range": { "startLine": 30, "endLine": 48 },
|
|
109
|
-
"children": []
|
|
110
|
-
}
|
|
111
|
-
]
|
|
112
|
-
}
|
|
113
|
-
]
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**ทำไมใช้ `children` (tree) แทน `scope` (string):** การซ้อนชั้นคือหัวใจของ "โครงสร้าง" ถ้าเก็บเป็น tree ตรงๆ ทั้ง HTML (collapsible) และ AI จะเข้าใจลำดับชั้นทันที โดยไม่ต้อง reconstruct จาก string
|
|
118
|
-
|
|
119
|
-
**Normalized `kind` enum** (แปลงศัพท์แต่ละภาษาให้เป็นชุดเดียว):
|
|
120
|
-
|
|
121
|
-
| kind กลาง | TS/JS | Python | Go |
|
|
122
|
-
|---|---|---|---|
|
|
123
|
-
| `class` | `class` | `class` | — |
|
|
124
|
-
| `struct` | — | — | `struct` |
|
|
125
|
-
| `interface` | `interface` | (Protocol) | `interface` |
|
|
126
|
-
| `function` | `function` / arrow | `def` (top-level) | `func` |
|
|
127
|
-
| `method` | method ใน class | `def` ใน class | method (มี receiver) |
|
|
128
|
-
| `type` | `type` / `enum` | `TypeAlias` | `type` |
|
|
129
|
-
| `const` / `var` | `const`/`let` | module-level assign | `const`/`var` |
|
|
130
|
-
|
|
131
|
-
> เก็บค่าดิบของภาษาไว้ใน field `rawKind` เผื่อ debug ได้ แต่ field หลักที่ AI/HTML ใช้คือ `kind` ที่ normalize แล้ว
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## 4. Tool Surface (MCP Tools ที่จะเปิดให้ AI เรียก)
|
|
136
|
-
|
|
137
|
-
| Tool | หน้าที่ | หมายเหตุ |
|
|
138
|
-
|---|---|---|
|
|
139
|
-
| `generate_skeleton` | input = path (ไฟล์ **หรือ** โฟลเดอร์/glob) → คืน JSON ย่อ + เซฟ HTML | tool หลัก |
|
|
140
|
-
| `get_skeleton_json` | คืนเฉพาะ JSON ไม่เซฟ HTML | สำหรับเคสที่ AI ต้องการแค่ข้อมูล ประหยัด IO |
|
|
141
|
-
| `list_supported_languages` | คืนรายชื่อภาษา + นามสกุลที่รองรับ | ให้ AI เช็คก่อนเรียก |
|
|
142
|
-
|
|
143
|
-
**Options ที่ควรมีใน `generate_skeleton`:**
|
|
144
|
-
|
|
145
|
-
- `detail`: `"outline"` (แค่ name+kind+range — ดีฟอลต์ ประหยัด token) หรือ `"full"` (+signature +doc)
|
|
146
|
-
- `emitHtml`: `true|false` — ปิดได้เมื่อไม่ต้องการไฟล์
|
|
147
|
-
- `outputDir`: ที่เก็บ HTML (ดีฟอลต์ `.ast-map/` ที่ root ของโปรเจกต์)
|
|
148
|
-
- `ignore`: glob ที่ข้าม (ดีฟอลต์ข้าม `node_modules`, `vendor`, `.git`, `dist`)
|
|
149
|
-
|
|
150
|
-
**รองรับโฟลเดอร์/หลายไฟล์:** workflow จริงของคุณ ("ไล่ระบบส่วน Inventory") มักครอบหลายไฟล์ ดังนั้น input ควรรับทั้งไฟล์เดี่ยวและโฟลเดอร์ตั้งแต่แรก
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## 5. Dual Output — ปรับให้ใช้จริงได้
|
|
155
|
-
|
|
156
|
-
**4a. JSON สำหรับ AI (ประหยัด token):**
|
|
157
|
-
- ดีฟอลต์ `detail: "outline"` ตัด field ที่ไม่จำเป็นออก, ไม่มี whitespace
|
|
158
|
-
- คืนพร้อม `htmlPath` ที่เซฟไว้ เพื่อให้ AI ส่งลิงก์ต่อให้ผู้ใช้
|
|
159
|
-
|
|
160
|
-
**4b. HTML สำหรับมนุษย์:**
|
|
161
|
-
- ไฟล์ **self-contained** ไฟล์เดียว (inline CSS/JS, Tailwind ผ่าน CDN หรือ inline) เปิดที่ไหนก็ได้
|
|
162
|
-
- **Collapsible tree** (expand/collapse) ตามไอเดียร่างแรก
|
|
163
|
-
- **ตำแหน่งเก็บ:** ดีฟอลต์ `.ast-map/<relative-path>-skeleton.html` ที่ root โปรเจกต์ + แนะนำให้ใส่ `.ast-map/` ใน `.gitignore`
|
|
164
|
-
- ใช้ relative path ในชื่อไฟล์ กัน `inventory.go` ใน 2 โฟลเดอร์ชนกัน
|
|
165
|
-
- **เรื่อง "เด้งขึ้นมาเอง":** server **ไม่** บังคับเปิดไฟล์เอง (ข้ามแพลตฟอร์มไม่ชัวร์) แต่จะ **คืน path กลับไปให้ Claude** แล้ว Claude แปะลิงก์ให้คุณคลิกเปิดเอง — ผลลัพธ์ที่คุณเห็นจะเหมือนเดิม (มีลิงก์ HTML โผล่พร้อมคำอธิบาย) แต่กลไกเชื่อถือได้กว่า
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## 6. แผนการพัฒนาใหม่ (Revised Roadmap)
|
|
170
|
-
|
|
171
|
-
### 🔵 Phase 0 — Decisions & Scaffolding (ตัดสินใจ + วางโครง)
|
|
172
|
-
- เลือก stack: **Node + TypeScript + `web-tree-sitter` (WASM)** + `@modelcontextprotocol/sdk`
|
|
173
|
-
*(เหตุผล: รันไทม์เดียวครอบทุกภาษาเป้าหมาย, WASM ไม่ต้อง build native, MCP SDK ฝั่ง TS โตเต็มที่ — ทางเลือกสำรองคือ Python + `tree-sitter` + python MCP SDK)*
|
|
174
|
-
- ล็อก **Skeleton JSON schema v1** (หัวข้อ 3) + เขียน type definitions
|
|
175
|
-
- ตั้ง repo, lint, test runner, โครง MCP handshake (initialize / listTools / callTool)
|
|
176
|
-
|
|
177
|
-
### 🟢 Phase 1 — Core Pipeline E2E (1 ภาษา)
|
|
178
|
-
- ต่อ pipeline ครบเส้น: `path → tree-sitter → query → normalize → JSON`
|
|
179
|
-
- เริ่มที่ **TypeScript/JavaScript**
|
|
180
|
-
- เขียน query file `.scm` แรก + kindMap แรก
|
|
181
|
-
- **ทดสอบ E2E:** AI สั่ง → ระบบอ่านไฟล์ `.ts` → คืน Standard JSON
|
|
182
|
-
- มี golden-file test (fixture .ts → expected .json)
|
|
183
|
-
|
|
184
|
-
### 🟡 Phase 2 — HTML Renderer & Dual Output
|
|
185
|
-
- เขียน renderer: Standard JSON → HTML self-contained (collapsible, Tailwind)
|
|
186
|
-
- ระบบ output dir + naming ตามหัวข้อ 5
|
|
187
|
-
- คืน `htmlPath` กลับไปใน tool response
|
|
188
|
-
- **ทดสอบควบคู่:** AI ได้ JSON ย่อ + ผู้ใช้ได้ลิงก์ HTML สวยๆ
|
|
189
|
-
|
|
190
|
-
### 🟠 Phase 3 — Multi-Language Scaling
|
|
191
|
-
- ทำ **Language Registry** (extension → adapter) ให้สมบูรณ์ (Factory pattern)
|
|
192
|
-
- เพิ่ม **Python** (query: class / def / async def)
|
|
193
|
-
- เพิ่ม **Go** (query: struct / interface / func / method)
|
|
194
|
-
- **Fallback:** เจอนามสกุลที่ไม่รู้จัก → คืนข้อความ `"unsupported: .xyz"` แบบ structured (ไม่ throw error แดง)
|
|
195
|
-
- รองรับ input เป็นโฟลเดอร์/glob (เดินหลายไฟล์ + รวม index)
|
|
196
|
-
|
|
197
|
-
### 🔴 Phase 4 — Advanced (Optional / Stretch)
|
|
198
|
-
> ทำต่อเมื่อ Phase 1–3 ใช้จริงแล้วและเห็นว่าจำเป็น — อย่าเพิ่งลงทุนก่อนเวลา
|
|
199
|
-
- **Semantic enrichment:** import graph, cross-file references, "ใครเรียกใคร"
|
|
200
|
-
- **Local LLM doc summary (Ollama):** สรุป docstring/comment ย่อใส่ HTML
|
|
201
|
-
- **Live Dashboard (Next.js + WebSocket):** แทนการ generate ไฟล์ HTML ดิบ
|
|
202
|
-
- *(หมายเหตุ: Tree-sitter ถูกย้ายมา Phase 1 แล้ว ดังนั้น Phase 4 จึงเหลือแต่ของที่เป็น "อนาคตจริงๆ")*
|
|
203
|
-
|
|
204
|
-
---
|
|
205
|
-
|
|
206
|
-
## 7. ประเด็นที่ร่างแรกยังไม่ได้พูดถึง (ควรเพิ่มในแผน)
|
|
207
|
-
|
|
208
|
-
- **Security / File access:** server อ่าน path อะไรก็ได้ → จำกัดให้อ่านได้เฉพาะใน allowed root (workspace) เท่านั้น กัน path traversal
|
|
209
|
-
- **Performance:** จำกัดขนาดไฟล์สูงสุด, ข้าม `node_modules`/`vendor`/build artifacts โดยดีฟอลต์
|
|
210
|
-
- **Error handling:** parse error → คืน partial result (tree-sitter ทำได้) ไม่ใช่ crash ทั้งคำสั่ง
|
|
211
|
-
- **Testing strategy:** golden-file test ต่อภาษา (fixture → expected JSON) เป็นด่านป้องกันหลัก เพราะ parser คือหัวใจ
|
|
212
|
-
- **Schema versioning:** ใส่ `schemaVersion` ตั้งแต่แรก เผื่อ schema เปลี่ยนในอนาคต
|
|
213
|
-
- **Config file:** `.ast-map.config.json` สำหรับ outputDir / ignore / default detail
|
|
214
|
-
|
|
215
|
-
---
|
|
216
|
-
|
|
217
|
-
## 8. Workflow ปลายทาง (เหมือนเดิม แต่กลไกชัดขึ้น)
|
|
218
|
-
|
|
219
|
-
1. เปิด Claude Desktop: *"ไล่ระบบหลังบ้านส่วน Inventory ให้หน่อย เริ่มจาก `services/inventory.go`"*
|
|
220
|
-
2. Claude เรียก `generate_skeleton("services/inventory.go")` แบบเงียบๆ
|
|
221
|
-
3. Tool: Controller ตรวจ path → Registry เลือก Go adapter → Tree-sitter parse + query → Normalize → คืน **JSON ย่อ** ให้ Claude **พร้อม `htmlPath`**
|
|
222
|
-
4. ระหว่าง Claude อธิบายบั๊กให้ฟัง มันก็แปะ **ลิงก์ `.ast-map/services/inventory.go-skeleton.html`** ให้คุณคลิกเปิดดูโครงสร้างควบคู่ไปด้วย
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
## 9. สรุป (ของร่างแรกที่เก็บไว้ vs ที่เปลี่ยน)
|
|
227
|
-
|
|
228
|
-
**เก็บไว้ (ดีอยู่แล้ว):** โครง 4 โมดูล · Dual Output · Token compression · Factory pattern · Fallback แทน error · workflow ปลายทาง
|
|
229
|
-
|
|
230
|
-
**เปลี่ยน (จุดสำคัญ):** Tree-sitter เป็นแกนตั้งแต่ Phase 1 (ไม่ใช่ Phase 4) · เรียก output ว่า Skeleton ไม่ใช่ AST · เก็บโครงเป็น tree (`children`) · HTML ลง output dir ที่ gitignore · ขยายภาษา = เพิ่ม query file ไม่ใช่ regex · เพิ่ม security/testing/perf เข้าแผน
|
|
1
|
+
# Universal AST Mapper — MCP Server Blueprint (ฉบับเกลา v2)
|
|
2
|
+
|
|
3
|
+
> เอกสารนี้เป็น Blueprint ที่ปรับปรุงจากร่างแรก โดยคงแนวคิดหลักที่ดีอยู่แล้วไว้
|
|
4
|
+
> (สถาปัตยกรรม 4 โมดูล, Dual Output, Token Compression, Factory Pattern)
|
|
5
|
+
> และแก้ไขจุดอ่อนสำคัญ 1 จุดที่กระทบทั้งแผน คือ **กลยุทธ์ของ Parser Engine**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. สรุปการเปลี่ยนแปลงสำคัญจากร่างแรก (Executive Summary)
|
|
10
|
+
|
|
11
|
+
| ประเด็น | ร่างแรก | ฉบับเกลานี้ | เหตุผล |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| **Parser หลัก** | Regex ก่อน → Tree-sitter ใน Phase 4 | **Tree-sitter ตั้งแต่ Phase 1** | Regex อ่าน nested scope / generics / comment / string ที่มี `{ }` ไม่ได้แม่น ทำให้ output เชื่อถือไม่ได้ และต้องรื้อทิ้งทั้งหมดใน Phase 4 อยู่ดี |
|
|
14
|
+
| **คำเรียก output** | "AST" | **"Code Skeleton / Symbol Map"** | สิ่งที่เราต้องการจริงคือ "โครงกระดูก" (class/func/struct + ตำแหน่ง + การซ้อน) ไม่ใช่ AST เต็มทุก node |
|
|
15
|
+
| **การเพิ่มภาษา** | เขียนชุด Regex ใหม่ทุกภาษา | **เพิ่มไฟล์ grammar + query (.scm)** | กลายเป็นงาน "config" แทน "เขียน logic ใหม่" — ขยายภาษาได้เร็วและพังยาก |
|
|
16
|
+
| **ไฟล์ HTML** | เซฟข้างไฟล์ต้นฉบับ | **เซฟใน output dir ที่กำหนดได้ + แนะนำ gitignore** | กันไฟล์ขยะรกใน repo และ git diff เพี้ยน |
|
|
17
|
+
| **"ไฟล์เด้งขึ้นมาเอง"** | สื่อว่าระบบเปิดไฟล์ให้อัตโนมัติ | **คืน path กลับไปให้ AI แล้วให้ AI ส่งลิงก์ให้ผู้ใช้** | MCP server สั่งเปิดไฟล์บนเดสก์ท็อปแบบข้ามแพลตฟอร์มไม่ได้เชื่อถือได้ |
|
|
18
|
+
|
|
19
|
+
**หัวใจของการเกลา:** เราไม่ได้ทิ้งไอเดียเดิม แต่ย้าย "ของแข็ง" (Tree-sitter) มาไว้เป็นรากฐานตั้งแต่ต้น แทนที่จะสร้างรากฐานด้วยของที่รู้อยู่แล้วว่าจะต้องทุบทิ้ง
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. ทำไมต้องเลิกใช้ Regex เป็นแกนหลัก
|
|
24
|
+
|
|
25
|
+
Regex เหมาะกับการ "จับ pattern บรรทัดเดียว" แต่ภาษาโปรแกรมเป็นโครงสร้างซ้อนชั้น (recursive) ซึ่ง Regex จัดการไม่ได้โดยธรรมชาติ ตัวอย่างเคสที่ Regex มักพลาด:
|
|
26
|
+
|
|
27
|
+
- **Nested scope:** ฟังก์ชันซ้อนในฟังก์ชัน, method ใน class ใน module — Regex บอก "ใครอยู่ใต้ใคร" ไม่ได้แม่น
|
|
28
|
+
- **Multi-line declaration:** signature ที่ขึ้นบรรทัดใหม่, generics ยาวๆ เช่น `func Map[T any, R any](...)`
|
|
29
|
+
- **False positive:** คำว่า `class` / `func` ที่อยู่ใน comment, ใน string literal, หรือใน template string
|
|
30
|
+
- **Braces ใน string:** `const x = "if (a) { b }"` ทำให้การนับวงเล็บเพี้ยน
|
|
31
|
+
|
|
32
|
+
**ทางออก: Tree-sitter** — เป็น parser generator ที่ GitHub, Neovim ใช้สำหรับ code navigation จริง
|
|
33
|
+
|
|
34
|
+
- รองรับ ~100+ ภาษา ผ่าน **API เดียวกัน** (ตรงสเปก "Universal" ของเราเป๊ะ)
|
|
35
|
+
- **Error-tolerant:** โค้ดพังบางส่วนก็ยัง parse ส่วนที่เหลือได้ (สำคัญมากตอนโค้ดกำลังเขียนค้าง)
|
|
36
|
+
- มี **Query API (`.scm` files)** สำหรับ "ดึงเฉพาะ node ที่ต้องการ" (class, func, struct) โดยไม่ต้องเดิน tree เอง
|
|
37
|
+
- เร็วระดับ incremental parsing
|
|
38
|
+
|
|
39
|
+
> **หมายเหตุ:** Regex ยังมีที่ยืนเล็กๆ ในฐานะ *fallback ขั้นสุดท้าย* สำหรับไฟล์ที่ยังไม่มี grammar เท่านั้น (เช่นดึงแค่ comment header) — ไม่ใช่แกนหลัก
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 2. สถาปัตยกรรมระบบ (System Architecture)
|
|
44
|
+
|
|
45
|
+
ยังคงโครง 4 โมดูลของร่างแรกไว้ (ดีอยู่แล้ว) แต่เปลี่ยนไส้ในของ Parser Engine
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
49
|
+
│ 1. Transport Layer (MCP / JSON-RPC 2.0 over stdio) │
|
|
50
|
+
│ ใช้ @modelcontextprotocol/sdk จัดการ handshake/protocol │
|
|
51
|
+
└───────────────┬─────────────────────────────────────────────┘
|
|
52
|
+
│ tool call: generate_skeleton(path, opts)
|
|
53
|
+
┌───────────────▼─────────────────────────────────────────────┐
|
|
54
|
+
│ 2. Controller / Router │
|
|
55
|
+
│ - resolve path, ตรวจ security (อยู่ใน allowed root ไหม) │
|
|
56
|
+
│ - อ่านนามสกุล → เลือก Language Adapter ผ่าน Registry │
|
|
57
|
+
│ - จัดการ fallback เมื่อไม่รู้จักนามสกุล │
|
|
58
|
+
└───────────────┬─────────────────────────────────────────────┘
|
|
59
|
+
│
|
|
60
|
+
┌───────────────▼─────────────────────────────────────────────┐
|
|
61
|
+
│ 3. Parser Engine (Core) │
|
|
62
|
+
│ - Tree-sitter loader (โหลด grammar .wasm ตามภาษา) │
|
|
63
|
+
│ - รัน Query (.scm) ดึง symbol nodes │
|
|
64
|
+
│ - Normalizer: แปลงเป็น Standard Skeleton JSON (schema กลาง) │
|
|
65
|
+
└───────────────┬─────────────────────────────────────────────┘
|
|
66
|
+
│ Standard Skeleton JSON
|
|
67
|
+
┌───────┴────────┐
|
|
68
|
+
▼ ▼
|
|
69
|
+
┌───────────────┐ ┌──────────────────────────────────┐
|
|
70
|
+
│ 4a. Data │ │ 4b. HTML Renderer │
|
|
71
|
+
│ Formatter │ │ - สวม JSON เข้า template (Tailwind)│
|
|
72
|
+
│ (JSON ย่อ →AI) │ │ - collapsible tree, self-contained│
|
|
73
|
+
│ │ │ - เซฟไฟล์ → คืน path กลับไปให้ AI │
|
|
74
|
+
└───────────────┘ └──────────────────────────────────┘
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Language Registry (หัวใจการขยายภาษา):** map `นามสกุล → { grammar, queryFile, kindMap }`
|
|
78
|
+
การเพิ่มภาษาใหม่ = เพิ่ม 1 entry + 1 ไฟล์ query + ตาราง mapping kind ไม่ต้องแตะ core เลย (นี่คือ Factory/Strategy pattern ที่ร่างแรกตั้งใจไว้ แต่ทำได้สะอาดกว่ามากเมื่อ logic อยู่ใน query file ไม่ใช่ใน regex)
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 3. Standard Skeleton JSON (Schema กลาง)
|
|
83
|
+
|
|
84
|
+
นี่คือ "ภาษากลาง" ที่ทุก parser ต้องคืนออกมาให้หน้าตาเหมือนกัน ไม่ว่าต้นทางจะเป็นภาษาอะไร — AI จะได้เห็น vocabulary ชุดเดียว
|
|
85
|
+
|
|
86
|
+
```jsonc
|
|
87
|
+
{
|
|
88
|
+
"schemaVersion": "1.0",
|
|
89
|
+
"file": "services/inventory.go",
|
|
90
|
+
"language": "go",
|
|
91
|
+
"generatedAt": "2026-05-27T10:00:00Z",
|
|
92
|
+
"parser": { "engine": "tree-sitter", "grammar": "go@0.21.0" },
|
|
93
|
+
"symbolCount": 12,
|
|
94
|
+
"symbols": [
|
|
95
|
+
{
|
|
96
|
+
"name": "InventoryService",
|
|
97
|
+
"kind": "struct", // normalized enum (ดูด้านล่าง)
|
|
98
|
+
"signature": null,
|
|
99
|
+
"visibility": "public", // public | private (จากตัวพิมพ์ใหญ่/_underscore ตามภาษา)
|
|
100
|
+
"range": { "startLine": 14, "endLine": 22 },
|
|
101
|
+
"doc": "Handles stock-level operations", // leading comment/docstring (optional)
|
|
102
|
+
"children": [ // ⭐ ใช้ tree (children) ไม่ใช่ scope แบบ string
|
|
103
|
+
{
|
|
104
|
+
"name": "ReserveStock",
|
|
105
|
+
"kind": "method",
|
|
106
|
+
"signature": "(ctx context.Context, sku string, qty int) error",
|
|
107
|
+
"visibility": "public",
|
|
108
|
+
"range": { "startLine": 30, "endLine": 48 },
|
|
109
|
+
"children": []
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**ทำไมใช้ `children` (tree) แทน `scope` (string):** การซ้อนชั้นคือหัวใจของ "โครงสร้าง" ถ้าเก็บเป็น tree ตรงๆ ทั้ง HTML (collapsible) และ AI จะเข้าใจลำดับชั้นทันที โดยไม่ต้อง reconstruct จาก string
|
|
118
|
+
|
|
119
|
+
**Normalized `kind` enum** (แปลงศัพท์แต่ละภาษาให้เป็นชุดเดียว):
|
|
120
|
+
|
|
121
|
+
| kind กลาง | TS/JS | Python | Go |
|
|
122
|
+
|---|---|---|---|
|
|
123
|
+
| `class` | `class` | `class` | — |
|
|
124
|
+
| `struct` | — | — | `struct` |
|
|
125
|
+
| `interface` | `interface` | (Protocol) | `interface` |
|
|
126
|
+
| `function` | `function` / arrow | `def` (top-level) | `func` |
|
|
127
|
+
| `method` | method ใน class | `def` ใน class | method (มี receiver) |
|
|
128
|
+
| `type` | `type` / `enum` | `TypeAlias` | `type` |
|
|
129
|
+
| `const` / `var` | `const`/`let` | module-level assign | `const`/`var` |
|
|
130
|
+
|
|
131
|
+
> เก็บค่าดิบของภาษาไว้ใน field `rawKind` เผื่อ debug ได้ แต่ field หลักที่ AI/HTML ใช้คือ `kind` ที่ normalize แล้ว
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 4. Tool Surface (MCP Tools ที่จะเปิดให้ AI เรียก)
|
|
136
|
+
|
|
137
|
+
| Tool | หน้าที่ | หมายเหตุ |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `generate_skeleton` | input = path (ไฟล์ **หรือ** โฟลเดอร์/glob) → คืน JSON ย่อ + เซฟ HTML | tool หลัก |
|
|
140
|
+
| `get_skeleton_json` | คืนเฉพาะ JSON ไม่เซฟ HTML | สำหรับเคสที่ AI ต้องการแค่ข้อมูล ประหยัด IO |
|
|
141
|
+
| `list_supported_languages` | คืนรายชื่อภาษา + นามสกุลที่รองรับ | ให้ AI เช็คก่อนเรียก |
|
|
142
|
+
|
|
143
|
+
**Options ที่ควรมีใน `generate_skeleton`:**
|
|
144
|
+
|
|
145
|
+
- `detail`: `"outline"` (แค่ name+kind+range — ดีฟอลต์ ประหยัด token) หรือ `"full"` (+signature +doc)
|
|
146
|
+
- `emitHtml`: `true|false` — ปิดได้เมื่อไม่ต้องการไฟล์
|
|
147
|
+
- `outputDir`: ที่เก็บ HTML (ดีฟอลต์ `.ast-map/` ที่ root ของโปรเจกต์)
|
|
148
|
+
- `ignore`: glob ที่ข้าม (ดีฟอลต์ข้าม `node_modules`, `vendor`, `.git`, `dist`)
|
|
149
|
+
|
|
150
|
+
**รองรับโฟลเดอร์/หลายไฟล์:** workflow จริงของคุณ ("ไล่ระบบส่วน Inventory") มักครอบหลายไฟล์ ดังนั้น input ควรรับทั้งไฟล์เดี่ยวและโฟลเดอร์ตั้งแต่แรก
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 5. Dual Output — ปรับให้ใช้จริงได้
|
|
155
|
+
|
|
156
|
+
**4a. JSON สำหรับ AI (ประหยัด token):**
|
|
157
|
+
- ดีฟอลต์ `detail: "outline"` ตัด field ที่ไม่จำเป็นออก, ไม่มี whitespace
|
|
158
|
+
- คืนพร้อม `htmlPath` ที่เซฟไว้ เพื่อให้ AI ส่งลิงก์ต่อให้ผู้ใช้
|
|
159
|
+
|
|
160
|
+
**4b. HTML สำหรับมนุษย์:**
|
|
161
|
+
- ไฟล์ **self-contained** ไฟล์เดียว (inline CSS/JS, Tailwind ผ่าน CDN หรือ inline) เปิดที่ไหนก็ได้
|
|
162
|
+
- **Collapsible tree** (expand/collapse) ตามไอเดียร่างแรก
|
|
163
|
+
- **ตำแหน่งเก็บ:** ดีฟอลต์ `.ast-map/<relative-path>-skeleton.html` ที่ root โปรเจกต์ + แนะนำให้ใส่ `.ast-map/` ใน `.gitignore`
|
|
164
|
+
- ใช้ relative path ในชื่อไฟล์ กัน `inventory.go` ใน 2 โฟลเดอร์ชนกัน
|
|
165
|
+
- **เรื่อง "เด้งขึ้นมาเอง":** server **ไม่** บังคับเปิดไฟล์เอง (ข้ามแพลตฟอร์มไม่ชัวร์) แต่จะ **คืน path กลับไปให้ Claude** แล้ว Claude แปะลิงก์ให้คุณคลิกเปิดเอง — ผลลัพธ์ที่คุณเห็นจะเหมือนเดิม (มีลิงก์ HTML โผล่พร้อมคำอธิบาย) แต่กลไกเชื่อถือได้กว่า
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 6. แผนการพัฒนาใหม่ (Revised Roadmap)
|
|
170
|
+
|
|
171
|
+
### 🔵 Phase 0 — Decisions & Scaffolding (ตัดสินใจ + วางโครง)
|
|
172
|
+
- เลือก stack: **Node + TypeScript + `web-tree-sitter` (WASM)** + `@modelcontextprotocol/sdk`
|
|
173
|
+
*(เหตุผล: รันไทม์เดียวครอบทุกภาษาเป้าหมาย, WASM ไม่ต้อง build native, MCP SDK ฝั่ง TS โตเต็มที่ — ทางเลือกสำรองคือ Python + `tree-sitter` + python MCP SDK)*
|
|
174
|
+
- ล็อก **Skeleton JSON schema v1** (หัวข้อ 3) + เขียน type definitions
|
|
175
|
+
- ตั้ง repo, lint, test runner, โครง MCP handshake (initialize / listTools / callTool)
|
|
176
|
+
|
|
177
|
+
### 🟢 Phase 1 — Core Pipeline E2E (1 ภาษา)
|
|
178
|
+
- ต่อ pipeline ครบเส้น: `path → tree-sitter → query → normalize → JSON`
|
|
179
|
+
- เริ่มที่ **TypeScript/JavaScript**
|
|
180
|
+
- เขียน query file `.scm` แรก + kindMap แรก
|
|
181
|
+
- **ทดสอบ E2E:** AI สั่ง → ระบบอ่านไฟล์ `.ts` → คืน Standard JSON
|
|
182
|
+
- มี golden-file test (fixture .ts → expected .json)
|
|
183
|
+
|
|
184
|
+
### 🟡 Phase 2 — HTML Renderer & Dual Output
|
|
185
|
+
- เขียน renderer: Standard JSON → HTML self-contained (collapsible, Tailwind)
|
|
186
|
+
- ระบบ output dir + naming ตามหัวข้อ 5
|
|
187
|
+
- คืน `htmlPath` กลับไปใน tool response
|
|
188
|
+
- **ทดสอบควบคู่:** AI ได้ JSON ย่อ + ผู้ใช้ได้ลิงก์ HTML สวยๆ
|
|
189
|
+
|
|
190
|
+
### 🟠 Phase 3 — Multi-Language Scaling
|
|
191
|
+
- ทำ **Language Registry** (extension → adapter) ให้สมบูรณ์ (Factory pattern)
|
|
192
|
+
- เพิ่ม **Python** (query: class / def / async def)
|
|
193
|
+
- เพิ่ม **Go** (query: struct / interface / func / method)
|
|
194
|
+
- **Fallback:** เจอนามสกุลที่ไม่รู้จัก → คืนข้อความ `"unsupported: .xyz"` แบบ structured (ไม่ throw error แดง)
|
|
195
|
+
- รองรับ input เป็นโฟลเดอร์/glob (เดินหลายไฟล์ + รวม index)
|
|
196
|
+
|
|
197
|
+
### 🔴 Phase 4 — Advanced (Optional / Stretch)
|
|
198
|
+
> ทำต่อเมื่อ Phase 1–3 ใช้จริงแล้วและเห็นว่าจำเป็น — อย่าเพิ่งลงทุนก่อนเวลา
|
|
199
|
+
- **Semantic enrichment:** import graph, cross-file references, "ใครเรียกใคร"
|
|
200
|
+
- **Local LLM doc summary (Ollama):** สรุป docstring/comment ย่อใส่ HTML
|
|
201
|
+
- **Live Dashboard (Next.js + WebSocket):** แทนการ generate ไฟล์ HTML ดิบ
|
|
202
|
+
- *(หมายเหตุ: Tree-sitter ถูกย้ายมา Phase 1 แล้ว ดังนั้น Phase 4 จึงเหลือแต่ของที่เป็น "อนาคตจริงๆ")*
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 7. ประเด็นที่ร่างแรกยังไม่ได้พูดถึง (ควรเพิ่มในแผน)
|
|
207
|
+
|
|
208
|
+
- **Security / File access:** server อ่าน path อะไรก็ได้ → จำกัดให้อ่านได้เฉพาะใน allowed root (workspace) เท่านั้น กัน path traversal
|
|
209
|
+
- **Performance:** จำกัดขนาดไฟล์สูงสุด, ข้าม `node_modules`/`vendor`/build artifacts โดยดีฟอลต์
|
|
210
|
+
- **Error handling:** parse error → คืน partial result (tree-sitter ทำได้) ไม่ใช่ crash ทั้งคำสั่ง
|
|
211
|
+
- **Testing strategy:** golden-file test ต่อภาษา (fixture → expected JSON) เป็นด่านป้องกันหลัก เพราะ parser คือหัวใจ
|
|
212
|
+
- **Schema versioning:** ใส่ `schemaVersion` ตั้งแต่แรก เผื่อ schema เปลี่ยนในอนาคต
|
|
213
|
+
- **Config file:** `.ast-map.config.json` สำหรับ outputDir / ignore / default detail
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 8. Workflow ปลายทาง (เหมือนเดิม แต่กลไกชัดขึ้น)
|
|
218
|
+
|
|
219
|
+
1. เปิด Claude Desktop: *"ไล่ระบบหลังบ้านส่วน Inventory ให้หน่อย เริ่มจาก `services/inventory.go`"*
|
|
220
|
+
2. Claude เรียก `generate_skeleton("services/inventory.go")` แบบเงียบๆ
|
|
221
|
+
3. Tool: Controller ตรวจ path → Registry เลือก Go adapter → Tree-sitter parse + query → Normalize → คืน **JSON ย่อ** ให้ Claude **พร้อม `htmlPath`**
|
|
222
|
+
4. ระหว่าง Claude อธิบายบั๊กให้ฟัง มันก็แปะ **ลิงก์ `.ast-map/services/inventory.go-skeleton.html`** ให้คุณคลิกเปิดดูโครงสร้างควบคู่ไปด้วย
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## 9. สรุป (ของร่างแรกที่เก็บไว้ vs ที่เปลี่ยน)
|
|
227
|
+
|
|
228
|
+
**เก็บไว้ (ดีอยู่แล้ว):** โครง 4 โมดูล · Dual Output · Token compression · Factory pattern · Fallback แทน error · workflow ปลายทาง
|
|
229
|
+
|
|
230
|
+
**เปลี่ยน (จุดสำคัญ):** Tree-sitter เป็นแกนตั้งแต่ Phase 1 (ไม่ใช่ Phase 4) · เรียก output ว่า Skeleton ไม่ใช่ AST · เก็บโครงเป็น tree (`children`) · HTML ลง output dir ที่ gitignore · ขยายภาษา = เพิ่ม query file ไม่ใช่ regex · เพิ่ม security/testing/perf เข้าแผน
|