vg-coder-cli 1.0.10 → 1.0.12

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