vg-coder-cli 1.0.11 → 1.0.13
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/package.json +1 -1
- package/src/server/api-server.js +1 -2
- package/src/server/views/dashboard.html +440 -149
package/package.json
CHANGED
package/src/server/api-server.js
CHANGED
|
@@ -83,9 +83,8 @@ class ApiServer {
|
|
|
83
83
|
const detector = new ProjectDetector(resolvedPath);
|
|
84
84
|
const projectInfo = await detector.detectAll();
|
|
85
85
|
|
|
86
|
-
// Scan files
|
|
86
|
+
// Scan files (no token limit, get all files)
|
|
87
87
|
const scannerOptions = {
|
|
88
|
-
maxTokens: parseInt(options.maxTokens || 8000),
|
|
89
88
|
extensions: options.extensions ? options.extensions.split(',').map(ext => ext.trim()) : undefined,
|
|
90
89
|
includeHidden: options.includeHidden || false
|
|
91
90
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
|
+
|
|
3
4
|
<head>
|
|
4
5
|
<meta charset="UTF-8">
|
|
5
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
.container {
|
|
22
|
-
max-width:
|
|
23
|
+
max-width: 1000px;
|
|
23
24
|
margin: 0 auto;
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
border-radius: 12px;
|
|
29
30
|
padding: 30px;
|
|
30
31
|
margin-bottom: 30px;
|
|
31
|
-
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
32
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
.header h1 {
|
|
@@ -52,105 +53,192 @@
|
|
|
52
53
|
margin-top: 10px;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
.system-prompt-card {
|
|
57
|
+
background: white;
|
|
58
|
+
border-radius: 12px;
|
|
59
|
+
padding: 25px;
|
|
60
|
+
margin-bottom: 30px;
|
|
61
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.system-prompt-header {
|
|
65
|
+
display: flex;
|
|
66
|
+
justify-content: space-between;
|
|
67
|
+
align-items: center;
|
|
68
|
+
margin-bottom: 15px;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
user-select: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.system-prompt-header h2 {
|
|
74
|
+
color: #667eea;
|
|
75
|
+
font-size: 1.5em;
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: 10px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.toggle-icon {
|
|
82
|
+
transition: transform 0.3s ease;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.toggle-icon.open {
|
|
86
|
+
transform: rotate(180deg);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.system-prompt-content {
|
|
90
|
+
max-height: 0;
|
|
91
|
+
overflow: hidden;
|
|
92
|
+
transition: max-height 0.3s ease;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.system-prompt-content.open {
|
|
96
|
+
max-height: 2000px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.prompt-text {
|
|
100
|
+
background: #f9fafb;
|
|
101
|
+
border: 2px solid #e5e7eb;
|
|
102
|
+
border-radius: 8px;
|
|
103
|
+
padding: 20px;
|
|
104
|
+
font-family: 'Courier New', monospace;
|
|
105
|
+
font-size: 0.9em;
|
|
106
|
+
line-height: 1.6;
|
|
107
|
+
white-space: pre-wrap;
|
|
108
|
+
max-height: 400px;
|
|
109
|
+
overflow-y: auto;
|
|
110
|
+
margin-bottom: 15px;
|
|
111
|
+
}
|
|
112
|
+
|
|
55
113
|
.endpoints {
|
|
56
114
|
display: grid;
|
|
57
|
-
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
|
58
115
|
gap: 20px;
|
|
59
116
|
}
|
|
60
117
|
|
|
61
118
|
.endpoint-card {
|
|
62
119
|
background: white;
|
|
63
120
|
border-radius: 12px;
|
|
64
|
-
padding:
|
|
65
|
-
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
66
|
-
transition: transform 0.3s ease;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.endpoint-card:hover {
|
|
70
|
-
transform: translateY(-5px);
|
|
121
|
+
padding: 30px;
|
|
122
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
71
123
|
}
|
|
72
124
|
|
|
73
125
|
.endpoint-header {
|
|
74
126
|
display: flex;
|
|
75
127
|
align-items: center;
|
|
76
128
|
gap: 15px;
|
|
77
|
-
margin-bottom:
|
|
129
|
+
margin-bottom: 20px;
|
|
78
130
|
}
|
|
79
131
|
|
|
80
132
|
.method {
|
|
81
|
-
padding:
|
|
133
|
+
padding: 8px 16px;
|
|
82
134
|
border-radius: 6px;
|
|
83
135
|
font-weight: bold;
|
|
84
|
-
font-size: 0.
|
|
136
|
+
font-size: 0.9em;
|
|
85
137
|
}
|
|
86
138
|
|
|
87
|
-
.method.
|
|
88
|
-
|
|
89
|
-
|
|
139
|
+
.method.post {
|
|
140
|
+
background: #10b981;
|
|
141
|
+
color: white;
|
|
142
|
+
}
|
|
90
143
|
|
|
91
144
|
.endpoint-path {
|
|
92
145
|
font-family: 'Courier New', monospace;
|
|
93
146
|
color: #333;
|
|
94
|
-
font-size: 1.
|
|
147
|
+
font-size: 1.2em;
|
|
148
|
+
font-weight: 600;
|
|
95
149
|
}
|
|
96
150
|
|
|
97
151
|
.endpoint-desc {
|
|
98
152
|
color: #666;
|
|
99
|
-
margin-bottom:
|
|
153
|
+
margin-bottom: 20px;
|
|
100
154
|
line-height: 1.6;
|
|
155
|
+
font-size: 1.05em;
|
|
101
156
|
}
|
|
102
157
|
|
|
103
158
|
.form-group {
|
|
104
|
-
margin-bottom:
|
|
159
|
+
margin-bottom: 20px;
|
|
105
160
|
}
|
|
106
161
|
|
|
107
162
|
.form-group label {
|
|
108
163
|
display: block;
|
|
109
|
-
margin-bottom:
|
|
164
|
+
margin-bottom: 8px;
|
|
110
165
|
color: #333;
|
|
111
|
-
font-weight:
|
|
166
|
+
font-weight: 600;
|
|
167
|
+
font-size: 1em;
|
|
112
168
|
}
|
|
113
169
|
|
|
114
170
|
.form-group input,
|
|
115
171
|
.form-group textarea {
|
|
116
172
|
width: 100%;
|
|
117
|
-
padding:
|
|
173
|
+
padding: 12px;
|
|
118
174
|
border: 2px solid #e5e7eb;
|
|
119
|
-
border-radius:
|
|
120
|
-
font-size:
|
|
175
|
+
border-radius: 8px;
|
|
176
|
+
font-size: 1em;
|
|
121
177
|
font-family: 'Courier New', monospace;
|
|
178
|
+
transition: border-color 0.3s ease;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.form-group input:focus,
|
|
182
|
+
.form-group textarea:focus {
|
|
183
|
+
outline: none;
|
|
184
|
+
border-color: #667eea;
|
|
122
185
|
}
|
|
123
186
|
|
|
124
187
|
.form-group textarea {
|
|
125
|
-
min-height:
|
|
188
|
+
min-height: 150px;
|
|
126
189
|
resize: vertical;
|
|
127
190
|
}
|
|
128
191
|
|
|
192
|
+
.btn-group {
|
|
193
|
+
display: flex;
|
|
194
|
+
gap: 10px;
|
|
195
|
+
margin-top: 20px;
|
|
196
|
+
}
|
|
197
|
+
|
|
129
198
|
.btn {
|
|
130
199
|
background: #667eea;
|
|
131
200
|
color: white;
|
|
132
201
|
border: none;
|
|
133
|
-
padding:
|
|
134
|
-
border-radius:
|
|
202
|
+
padding: 14px 28px;
|
|
203
|
+
border-radius: 8px;
|
|
135
204
|
cursor: pointer;
|
|
136
205
|
font-size: 1em;
|
|
137
206
|
font-weight: 600;
|
|
138
|
-
transition:
|
|
207
|
+
transition: all 0.3s ease;
|
|
208
|
+
display: inline-flex;
|
|
209
|
+
align-items: center;
|
|
210
|
+
gap: 8px;
|
|
139
211
|
}
|
|
140
212
|
|
|
141
213
|
.btn:hover {
|
|
142
214
|
background: #5568d3;
|
|
215
|
+
transform: translateY(-2px);
|
|
216
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
|
143
217
|
}
|
|
144
218
|
|
|
145
219
|
.btn:disabled {
|
|
146
220
|
background: #9ca3af;
|
|
147
221
|
cursor: not-allowed;
|
|
222
|
+
transform: none;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.btn-copy {
|
|
226
|
+
background: #10b981;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.btn-copy:hover {
|
|
230
|
+
background: #059669;
|
|
231
|
+
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.btn-copy.copied {
|
|
235
|
+
background: #6366f1;
|
|
148
236
|
}
|
|
149
237
|
|
|
150
238
|
.response {
|
|
151
|
-
margin-top:
|
|
152
|
-
padding:
|
|
153
|
-
border-radius:
|
|
239
|
+
margin-top: 20px;
|
|
240
|
+
padding: 20px;
|
|
241
|
+
border-radius: 8px;
|
|
154
242
|
background: #f9fafb;
|
|
155
243
|
border-left: 4px solid #667eea;
|
|
156
244
|
display: none;
|
|
@@ -173,7 +261,7 @@
|
|
|
173
261
|
.response pre {
|
|
174
262
|
margin: 0;
|
|
175
263
|
font-family: 'Courier New', monospace;
|
|
176
|
-
font-size: 0.
|
|
264
|
+
font-size: 0.95em;
|
|
177
265
|
white-space: pre-wrap;
|
|
178
266
|
word-wrap: break-word;
|
|
179
267
|
}
|
|
@@ -182,105 +270,278 @@
|
|
|
182
270
|
display: inline-block;
|
|
183
271
|
width: 16px;
|
|
184
272
|
height: 16px;
|
|
185
|
-
border: 3px solid rgba(255,255,255
|
|
273
|
+
border: 3px solid rgba(255, 255, 255, .3);
|
|
186
274
|
border-radius: 50%;
|
|
187
275
|
border-top-color: white;
|
|
188
276
|
animation: spin 1s ease-in-out infinite;
|
|
189
277
|
}
|
|
190
278
|
|
|
191
279
|
@keyframes spin {
|
|
192
|
-
to {
|
|
280
|
+
to {
|
|
281
|
+
transform: rotate(360deg);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.toast {
|
|
286
|
+
position: fixed;
|
|
287
|
+
top: 20px;
|
|
288
|
+
right: 20px;
|
|
289
|
+
padding: 16px 24px;
|
|
290
|
+
border-radius: 8px;
|
|
291
|
+
color: white;
|
|
292
|
+
font-weight: 600;
|
|
293
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
|
294
|
+
opacity: 0;
|
|
295
|
+
transform: translateY(-20px);
|
|
296
|
+
transition: all 0.3s ease;
|
|
297
|
+
z-index: 1000;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.toast.show {
|
|
301
|
+
opacity: 1;
|
|
302
|
+
transform: translateY(0);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.toast.success {
|
|
306
|
+
background: #10b981;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.toast.error {
|
|
310
|
+
background: #ef4444;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.toast.info {
|
|
314
|
+
background: #3b82f6;
|
|
193
315
|
}
|
|
194
316
|
</style>
|
|
195
317
|
</head>
|
|
318
|
+
|
|
196
319
|
<body>
|
|
197
320
|
<div class="container">
|
|
198
321
|
<div class="header">
|
|
199
322
|
<h1>🚀 VG Coder API Dashboard</h1>
|
|
200
|
-
<p>
|
|
323
|
+
<p>Phân tích dự án và thực thi bash scripts</p>
|
|
201
324
|
<span class="status" id="status">● Server Running</span>
|
|
202
325
|
</div>
|
|
203
326
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
<div class="
|
|
207
|
-
<
|
|
208
|
-
<span
|
|
209
|
-
<span
|
|
210
|
-
</
|
|
211
|
-
<
|
|
212
|
-
<button class="btn" onclick="testHealth()">Test Health Check</button>
|
|
213
|
-
<div class="response" id="health-response"></div>
|
|
327
|
+
<!-- System Prompt Section -->
|
|
328
|
+
<div class="system-prompt-card">
|
|
329
|
+
<div class="system-prompt-header" onclick="toggleSystemPrompt()">
|
|
330
|
+
<h2>
|
|
331
|
+
<span>📝</span>
|
|
332
|
+
<span>AI System Prompt</span>
|
|
333
|
+
</h2>
|
|
334
|
+
<span class="toggle-icon" id="toggle-icon">▼</span>
|
|
214
335
|
</div>
|
|
336
|
+
<div class="system-prompt-content" id="system-prompt-content">
|
|
337
|
+
<div class="prompt-text" id="prompt-text"></div>
|
|
338
|
+
<button class="btn btn-copy" onclick="copySystemPrompt()">
|
|
339
|
+
<span id="copy-icon">📋</span>
|
|
340
|
+
<span id="copy-text">Copy System Prompt</span>
|
|
341
|
+
</button>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
215
344
|
|
|
345
|
+
<div class="endpoints">
|
|
216
346
|
<!-- Analyze -->
|
|
217
347
|
<div class="endpoint-card">
|
|
218
348
|
<div class="endpoint-header">
|
|
219
349
|
<span class="method post">POST</span>
|
|
220
350
|
<span class="endpoint-path">/api/analyze</span>
|
|
221
351
|
</div>
|
|
222
|
-
<p class="endpoint-desc"
|
|
352
|
+
<p class="endpoint-desc">📊 Phân tích dự án và lấy toàn bộ source code</p>
|
|
223
353
|
<div class="form-group">
|
|
224
|
-
<label
|
|
354
|
+
<label>📁 Project Path:</label>
|
|
225
355
|
<input type="text" id="analyze-path" value="." placeholder=".">
|
|
226
356
|
</div>
|
|
227
|
-
<div class="
|
|
228
|
-
<
|
|
229
|
-
|
|
357
|
+
<div class="btn-group">
|
|
358
|
+
<button class="btn" onclick="testAnalyze()">
|
|
359
|
+
<span>📥</span>
|
|
360
|
+
<span>Download File</span>
|
|
361
|
+
</button>
|
|
362
|
+
<button class="btn btn-copy" onclick="copyAnalyzeResult()">
|
|
363
|
+
<span id="analyze-copy-icon">📋</span>
|
|
364
|
+
<span id="analyze-copy-text">Copy to Clipboard</span>
|
|
365
|
+
</button>
|
|
230
366
|
</div>
|
|
231
|
-
<button class="btn" onclick="testAnalyze()">Analyze & Download</button>
|
|
232
367
|
<div class="response" id="analyze-response"></div>
|
|
233
368
|
</div>
|
|
234
369
|
|
|
235
|
-
<!-- Info -->
|
|
236
|
-
<div class="endpoint-card">
|
|
237
|
-
<div class="endpoint-header">
|
|
238
|
-
<span class="method get">GET</span>
|
|
239
|
-
<span class="endpoint-path">/api/info</span>
|
|
240
|
-
</div>
|
|
241
|
-
<p class="endpoint-desc">Get project information and statistics</p>
|
|
242
|
-
<div class="form-group">
|
|
243
|
-
<label>Project Path:</label>
|
|
244
|
-
<input type="text" id="info-path" value="." placeholder=".">
|
|
245
|
-
</div>
|
|
246
|
-
<button class="btn" onclick="testInfo()">Get Info</button>
|
|
247
|
-
<div class="response" id="info-response"></div>
|
|
248
|
-
</div>
|
|
249
|
-
|
|
250
370
|
<!-- Execute Bash -->
|
|
251
371
|
<div class="endpoint-card">
|
|
252
372
|
<div class="endpoint-header">
|
|
253
373
|
<span class="method post">POST</span>
|
|
254
374
|
<span class="endpoint-path">/api/execute</span>
|
|
255
375
|
</div>
|
|
256
|
-
<p class="endpoint-desc"
|
|
376
|
+
<p class="endpoint-desc">⚡ Thực thi bash script với syntax validation</p>
|
|
257
377
|
<div class="form-group">
|
|
258
|
-
<label
|
|
259
|
-
<textarea id="execute-bash"
|
|
378
|
+
<label>💻 Bash Script:</label>
|
|
379
|
+
<textarea id="execute-bash"
|
|
380
|
+
placeholder="mkdir -p $(dirname "src/test.js") cat <<'EOF' > src/test.js console.log('Hello World'); EOF"></textarea>
|
|
260
381
|
</div>
|
|
261
|
-
<button class="btn" onclick="testExecute()">
|
|
382
|
+
<button class="btn" onclick="testExecute()">
|
|
383
|
+
<span>▶️</span>
|
|
384
|
+
<span>Execute Script</span>
|
|
385
|
+
</button>
|
|
262
386
|
<div class="response" id="execute-response"></div>
|
|
263
387
|
</div>
|
|
264
|
-
|
|
265
|
-
<!-- Clean -->
|
|
266
|
-
<div class="endpoint-card">
|
|
267
|
-
<div class="endpoint-header">
|
|
268
|
-
<span class="method delete">DELETE</span>
|
|
269
|
-
<span class="endpoint-path">/api/clean</span>
|
|
270
|
-
</div>
|
|
271
|
-
<p class="endpoint-desc">Clean output directory</p>
|
|
272
|
-
<div class="form-group">
|
|
273
|
-
<label>Output Path:</label>
|
|
274
|
-
<input type="text" id="clean-output" value="./vg-output" placeholder="./vg-output">
|
|
275
|
-
</div>
|
|
276
|
-
<button class="btn" onclick="testClean()">Clean Directory</button>
|
|
277
|
-
<div class="response" id="clean-response"></div>
|
|
278
|
-
</div>
|
|
279
388
|
</div>
|
|
280
389
|
</div>
|
|
281
390
|
|
|
391
|
+
<div class="toast" id="toast"></div>
|
|
392
|
+
|
|
282
393
|
<script>
|
|
283
394
|
const API_BASE = window.location.origin;
|
|
395
|
+
let lastAnalyzeResult = null;
|
|
396
|
+
|
|
397
|
+
// System Prompt
|
|
398
|
+
const SYSTEM_PROMPT = `# VG Coder AI System Prompt
|
|
399
|
+
|
|
400
|
+
## Command Prefixes
|
|
401
|
+
|
|
402
|
+
### /ask - Question & Answer Mode
|
|
403
|
+
Khi người dùng hỏi với prefix /ask, họ đang muốn tìm hiểu hoặc được giải thích về một vấn đề.
|
|
404
|
+
|
|
405
|
+
**Response Format:** Markdown
|
|
406
|
+
- Trả lời chi tiết, rõ ràng
|
|
407
|
+
- Sử dụng code blocks, lists, tables khi cần
|
|
408
|
+
- Cung cấp ví dụ minh họa
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
### /plan - Planning Mode
|
|
413
|
+
Khi người dùng muốn lên kế hoạch với prefix /plan, tạo một implementation plan chi tiết.
|
|
414
|
+
|
|
415
|
+
**Response Format:** Markdown checklist với bash commands
|
|
416
|
+
- Chia nhỏ thành các bước cụ thể
|
|
417
|
+
- Mỗi bước có bash command tương ứng
|
|
418
|
+
- Sắp xếp theo thứ tự logic
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
### /fix - Bug Fix Mode
|
|
423
|
+
Khi người dùng cần fix bug với prefix /fix, phân tích lỗi và đưa ra giải pháp.
|
|
424
|
+
|
|
425
|
+
**Response Format:** Markdown + Bash script
|
|
426
|
+
1. **Phân tích lỗi:** Giải thích nguyên nhân
|
|
427
|
+
2. **Giải pháp:** Mô tả cách fix
|
|
428
|
+
3. **Bash script:** Code để fix (nếu cần)
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
### /code - Code Generation Mode
|
|
433
|
+
Khi người dùng hỏi với prefix /code, trả về **BASH SCRIPT DUY NHẤT** để tạo/cập nhật files.
|
|
434
|
+
|
|
435
|
+
## ⚠️ QUY TẮC BẮT BUỘC
|
|
436
|
+
|
|
437
|
+
### 1. Chỉ bao gồm files có thay đổi
|
|
438
|
+
- ❌ **KHÔNG** bao gồm files không có sự thay đổi nội dung
|
|
439
|
+
- ✅ Nếu nội dung file sau chỉnh sửa giống 100% bản cũ → **BỎ QUA**
|
|
440
|
+
|
|
441
|
+
### 2. Format Script Chuẩn
|
|
442
|
+
|
|
443
|
+
**Mỗi file PHẢI theo cú pháp:**
|
|
444
|
+
\`\`\`bash
|
|
445
|
+
mkdir -p $(dirname "path/to/file.ext")
|
|
446
|
+
cat <<'EOF' > path/to/file.ext
|
|
447
|
+
... toàn bộ nội dung file sau khi chỉnh sửa ...
|
|
448
|
+
EOF
|
|
449
|
+
\`\`\`
|
|
450
|
+
|
|
451
|
+
### 3. Chi tiết quan trọng
|
|
452
|
+
- ✅ **LUÔN** có \`mkdir -p $(dirname "...")\` trước mỗi file
|
|
453
|
+
- ✅ Sử dụng \`<<'EOF'\` (có dấu nháy đơn) để tránh bash expansion
|
|
454
|
+
- ✅ Ghi đè hoàn toàn file bằng nội dung mới
|
|
455
|
+
- ✅ Tự động tạo file và thư mục cha nếu chưa tồn tại
|
|
456
|
+
- ✅ Đường dẫn giống với file mẫu đính kèm
|
|
457
|
+
|
|
458
|
+
### 4. Example Output
|
|
459
|
+
|
|
460
|
+
\`\`\`bash
|
|
461
|
+
# Create/Update component file
|
|
462
|
+
mkdir -p $(dirname "src/components/Button/index.tsx")
|
|
463
|
+
cat <<'EOF' > src/components/Button/index.tsx
|
|
464
|
+
import React from 'react';
|
|
465
|
+
|
|
466
|
+
export const Button = () => {
|
|
467
|
+
return <button>Click me</button>;
|
|
468
|
+
};
|
|
469
|
+
EOF
|
|
470
|
+
|
|
471
|
+
# Create/Update styles
|
|
472
|
+
mkdir -p $(dirname "src/components/Button/styles.css")
|
|
473
|
+
cat <<'EOF' > src/components/Button/styles.css
|
|
474
|
+
.button {
|
|
475
|
+
padding: 10px 20px;
|
|
476
|
+
background: blue;
|
|
477
|
+
}
|
|
478
|
+
EOF
|
|
479
|
+
\`\`\`
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Integration với VG Coder CLI
|
|
484
|
+
|
|
485
|
+
Bash scripts được generate sẽ được thực thi qua:
|
|
486
|
+
\`\`\`bash
|
|
487
|
+
POST http://localhost:6868/api/execute
|
|
488
|
+
{
|
|
489
|
+
"bash": "mkdir -p $(dirname \\"src/...\\")\\\\ncat <<'EOF' > ..."
|
|
490
|
+
}
|
|
491
|
+
\`\`\`
|
|
492
|
+
|
|
493
|
+
API sẽ:
|
|
494
|
+
1. ✅ Validate bash syntax trong \`.vg/temp-execute\`
|
|
495
|
+
2. ✅ Execute tại working directory nếu syntax OK
|
|
496
|
+
3. ✅ Trả về stdout/stderr/exitCode
|
|
497
|
+
4. ✅ Auto cleanup temp directory
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Best Practices
|
|
502
|
+
|
|
503
|
+
### DO ✅
|
|
504
|
+
- Luôn dùng \`mkdir -p $(dirname "...")\` trước mỗi file
|
|
505
|
+
- Sử dụng \`<<'EOF'\` để tránh variable expansion
|
|
506
|
+
- Ghi đè toàn bộ nội dung file
|
|
507
|
+
- Chỉ include files có thay đổi thực sự
|
|
508
|
+
|
|
509
|
+
### DON'T ❌
|
|
510
|
+
- Không tạo file mà không tạo thư mục cha
|
|
511
|
+
- Không dùng \`<<EOF\` (thiếu quotes) nếu có \`$\` trong content
|
|
512
|
+
- Không include files không thay đổi
|
|
513
|
+
- Không dùng relative paths phức tạp`;
|
|
514
|
+
|
|
515
|
+
// Load system prompt on page load
|
|
516
|
+
document.getElementById('prompt-text').textContent = SYSTEM_PROMPT;
|
|
517
|
+
|
|
518
|
+
function toggleSystemPrompt() {
|
|
519
|
+
const content = document.getElementById('system-prompt-content');
|
|
520
|
+
const icon = document.getElementById('toggle-icon');
|
|
521
|
+
content.classList.toggle('open');
|
|
522
|
+
icon.classList.toggle('open');
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function copySystemPrompt() {
|
|
526
|
+
const copyBtn = event.target.closest('.btn-copy');
|
|
527
|
+
const copyIcon = document.getElementById('copy-icon');
|
|
528
|
+
const copyText = document.getElementById('copy-text');
|
|
529
|
+
|
|
530
|
+
navigator.clipboard.writeText(SYSTEM_PROMPT).then(() => {
|
|
531
|
+
copyBtn.classList.add('copied');
|
|
532
|
+
copyIcon.textContent = '✓';
|
|
533
|
+
copyText.textContent = 'Copied!';
|
|
534
|
+
showToast('✅ Đã copy System Prompt!', 'success');
|
|
535
|
+
|
|
536
|
+
setTimeout(() => {
|
|
537
|
+
copyBtn.classList.remove('copied');
|
|
538
|
+
copyIcon.textContent = '📋';
|
|
539
|
+
copyText.textContent = 'Copy System Prompt';
|
|
540
|
+
}, 2000);
|
|
541
|
+
}).catch(err => {
|
|
542
|
+
showToast('❌ Lỗi copy: ' + err.message, 'error');
|
|
543
|
+
});
|
|
544
|
+
}
|
|
284
545
|
|
|
285
546
|
function showResponse(elementId, data, isError = false) {
|
|
286
547
|
const el = document.getElementById(elementId);
|
|
@@ -288,88 +549,121 @@
|
|
|
288
549
|
el.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
|
289
550
|
}
|
|
290
551
|
|
|
291
|
-
function showLoading(button) {
|
|
552
|
+
function showLoading(button, originalText) {
|
|
292
553
|
button.disabled = true;
|
|
293
554
|
button.innerHTML = '<span class="loading"></span> Loading...';
|
|
555
|
+
button.dataset.originalText = originalText;
|
|
294
556
|
}
|
|
295
557
|
|
|
296
|
-
function resetButton(button
|
|
558
|
+
function resetButton(button) {
|
|
297
559
|
button.disabled = false;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
async function testHealth() {
|
|
302
|
-
const btn = event.target;
|
|
303
|
-
showLoading(btn);
|
|
304
|
-
try {
|
|
305
|
-
const res = await fetch(`${API_BASE}/health`);
|
|
306
|
-
const data = await res.json();
|
|
307
|
-
showResponse('health-response', data);
|
|
308
|
-
} catch (err) {
|
|
309
|
-
showResponse('health-response', { error: err.message }, true);
|
|
310
|
-
}
|
|
311
|
-
resetButton(btn, 'Test Health Check');
|
|
560
|
+
const originalText = button.dataset.originalText;
|
|
561
|
+
button.innerHTML = originalText;
|
|
312
562
|
}
|
|
313
563
|
|
|
314
564
|
async function testAnalyze() {
|
|
315
|
-
const btn = event.target;
|
|
565
|
+
const btn = event.target.closest('.btn');
|
|
316
566
|
const path = document.getElementById('analyze-path').value;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
showLoading(btn);
|
|
567
|
+
|
|
568
|
+
showLoading(btn, '<span>📥</span><span>Download File</span>');
|
|
320
569
|
try {
|
|
321
570
|
const res = await fetch(`${API_BASE}/api/analyze`, {
|
|
322
571
|
method: 'POST',
|
|
323
572
|
headers: { 'Content-Type': 'application/json' },
|
|
324
|
-
body: JSON.stringify({
|
|
325
|
-
path,
|
|
326
|
-
options: { maxTokens: parseInt(maxTokens) }
|
|
327
|
-
})
|
|
573
|
+
body: JSON.stringify({ path })
|
|
328
574
|
});
|
|
329
575
|
|
|
330
576
|
if (res.ok) {
|
|
331
|
-
const
|
|
577
|
+
const text = await res.text();
|
|
578
|
+
lastAnalyzeResult = text;
|
|
579
|
+
|
|
580
|
+
// Download file
|
|
581
|
+
const blob = new Blob([text], { type: 'text/plain' });
|
|
332
582
|
const url = window.URL.createObjectURL(blob);
|
|
333
583
|
const a = document.createElement('a');
|
|
334
584
|
a.href = url;
|
|
335
585
|
a.download = 'project.txt';
|
|
336
586
|
a.click();
|
|
337
|
-
|
|
587
|
+
|
|
588
|
+
showResponse('analyze-response', {
|
|
589
|
+
success: true,
|
|
590
|
+
message: 'File downloaded!',
|
|
591
|
+
files: text.split('\n').filter(l => l.includes('===== FILE:')).length,
|
|
592
|
+
size: (text.length / 1024).toFixed(2) + ' KB'
|
|
593
|
+
});
|
|
594
|
+
showToast('✅ Đã download file!', 'success');
|
|
338
595
|
} else {
|
|
339
596
|
const data = await res.json();
|
|
340
597
|
showResponse('analyze-response', data, true);
|
|
598
|
+
showToast('❌ Lỗi analyze!', 'error');
|
|
341
599
|
}
|
|
342
600
|
} catch (err) {
|
|
343
601
|
showResponse('analyze-response', { error: err.message }, true);
|
|
602
|
+
showToast('❌ Lỗi: ' + err.message, 'error');
|
|
603
|
+
}
|
|
604
|
+
resetButton(btn);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async function copyAnalyzeResult() {
|
|
608
|
+
const copyBtn = event.target.closest('.btn-copy');
|
|
609
|
+
const copyIcon = document.getElementById('analyze-copy-icon');
|
|
610
|
+
const copyText = document.getElementById('analyze-copy-text');
|
|
611
|
+
|
|
612
|
+
if (!lastAnalyzeResult) {
|
|
613
|
+
// Fetch if not already analyzed
|
|
614
|
+
const path = document.getElementById('analyze-path').value;
|
|
615
|
+
showLoading(copyBtn, '<span id="analyze-copy-icon">📋</span><span id="analyze-copy-text">Copy to Clipboard</span>');
|
|
616
|
+
|
|
617
|
+
try {
|
|
618
|
+
const res = await fetch(`${API_BASE}/api/analyze`, {
|
|
619
|
+
method: 'POST',
|
|
620
|
+
headers: { 'Content-Type': 'application/json' },
|
|
621
|
+
body: JSON.stringify({ path })
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
if (res.ok) {
|
|
625
|
+
lastAnalyzeResult = await res.text();
|
|
626
|
+
} else {
|
|
627
|
+
showToast('❌ Lỗi analyze!', 'error');
|
|
628
|
+
resetButton(copyBtn);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
} catch (err) {
|
|
632
|
+
showToast('❌ Lỗi: ' + err.message, 'error');
|
|
633
|
+
resetButton(copyBtn);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
resetButton(copyBtn);
|
|
344
637
|
}
|
|
345
|
-
resetButton(btn, 'Analyze & Download');
|
|
346
|
-
}
|
|
347
638
|
|
|
348
|
-
|
|
349
|
-
const btn = event.target;
|
|
350
|
-
const path = document.getElementById('info-path').value;
|
|
351
|
-
|
|
352
|
-
showLoading(btn);
|
|
639
|
+
// Copy to clipboard
|
|
353
640
|
try {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
641
|
+
await navigator.clipboard.writeText(lastAnalyzeResult);
|
|
642
|
+
copyBtn.classList.add('copied');
|
|
643
|
+
copyIcon.textContent = '✓';
|
|
644
|
+
copyText.textContent = 'Copied!';
|
|
645
|
+
showToast('✅ Đã copy vào clipboard!', 'success');
|
|
646
|
+
|
|
647
|
+
setTimeout(() => {
|
|
648
|
+
copyBtn.classList.remove('copied');
|
|
649
|
+
copyIcon.textContent = '📋';
|
|
650
|
+
copyText.textContent = 'Copy to Clipboard';
|
|
651
|
+
}, 2000);
|
|
357
652
|
} catch (err) {
|
|
358
|
-
|
|
653
|
+
showToast('❌ Lỗi copy: ' + err.message, 'error');
|
|
359
654
|
}
|
|
360
|
-
resetButton(btn, 'Get Info');
|
|
361
655
|
}
|
|
362
656
|
|
|
363
657
|
async function testExecute() {
|
|
364
|
-
const btn = event.target;
|
|
658
|
+
const btn = event.target.closest('.btn');
|
|
365
659
|
const bash = document.getElementById('execute-bash').value;
|
|
366
|
-
|
|
660
|
+
|
|
367
661
|
if (!bash.trim()) {
|
|
368
|
-
|
|
662
|
+
showToast('⚠️ Vui lòng nhập bash script!', 'error');
|
|
369
663
|
return;
|
|
370
664
|
}
|
|
371
665
|
|
|
372
|
-
showLoading(btn);
|
|
666
|
+
showLoading(btn, '<span>▶️</span><span>Execute Script</span>');
|
|
373
667
|
try {
|
|
374
668
|
const res = await fetch(`${API_BASE}/api/execute`, {
|
|
375
669
|
method: 'POST',
|
|
@@ -378,33 +672,29 @@
|
|
|
378
672
|
});
|
|
379
673
|
const data = await res.json();
|
|
380
674
|
showResponse('execute-response', data, !res.ok || !data.success);
|
|
675
|
+
|
|
676
|
+
if (data.success) {
|
|
677
|
+
showToast('✅ Thực thi thành công!', 'success');
|
|
678
|
+
} else {
|
|
679
|
+
showToast('❌ Thực thi thất bại!', 'error');
|
|
680
|
+
}
|
|
381
681
|
} catch (err) {
|
|
382
682
|
showResponse('execute-response', { error: err.message }, true);
|
|
683
|
+
showToast('❌ Lỗi: ' + err.message, 'error');
|
|
383
684
|
}
|
|
384
|
-
resetButton(btn
|
|
685
|
+
resetButton(btn);
|
|
385
686
|
}
|
|
386
687
|
|
|
387
|
-
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const res = await fetch(`${API_BASE}/api/clean`, {
|
|
394
|
-
method: 'DELETE',
|
|
395
|
-
headers: { 'Content-Type': 'application/json' },
|
|
396
|
-
body: JSON.stringify({ output })
|
|
397
|
-
});
|
|
398
|
-
const data = await res.json();
|
|
399
|
-
showResponse('clean-response', data, !res.ok);
|
|
400
|
-
} catch (err) {
|
|
401
|
-
showResponse('clean-response', { error: err.message }, true);
|
|
402
|
-
}
|
|
403
|
-
resetButton(btn, 'Clean Directory');
|
|
688
|
+
function showToast(message, type = 'success') {
|
|
689
|
+
const toast = document.getElementById('toast');
|
|
690
|
+
toast.textContent = message;
|
|
691
|
+
toast.className = `toast ${type}`;
|
|
692
|
+
toast.classList.add('show');
|
|
693
|
+
setTimeout(() => toast.classList.remove('show'), 3000);
|
|
404
694
|
}
|
|
405
695
|
|
|
406
|
-
// Check server status
|
|
407
|
-
|
|
696
|
+
// Check server status ONCE on page load
|
|
697
|
+
(async () => {
|
|
408
698
|
try {
|
|
409
699
|
const res = await fetch(`${API_BASE}/health`);
|
|
410
700
|
if (res.ok) {
|
|
@@ -415,7 +705,8 @@
|
|
|
415
705
|
document.getElementById('status').textContent = '● Server Offline';
|
|
416
706
|
document.getElementById('status').style.background = '#ef4444';
|
|
417
707
|
}
|
|
418
|
-
}
|
|
708
|
+
})();
|
|
419
709
|
</script>
|
|
420
710
|
</body>
|
|
421
|
-
|
|
711
|
+
|
|
712
|
+
</html>
|