vg-coder-cli 1.0.6 → 1.0.8
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 +89 -76
- package/package.json +1 -1
- package/src/exporter/html-exporter.js +125 -1
- package/src/index.js +53 -6
- package/src/scanner/file-scanner.js +48 -0
- package/src/utils/clipboard.js +170 -0
- package/vg-coder-cli-1.0.8.tgz +0 -0
- package/test-small/package-lock.json +0 -1
- package/test-small/package.json +0 -1
- package/test-small/src/index.js +0 -1
- package/vg-coder-cli-1.0.2.tgz +0 -0
- package/vg-coder-cli-1.0.3.tgz +0 -0
- package/vg-coder-cli-1.0.4.tgz +0 -0
- package/vg-coder-cli-1.0.5.tgz +0 -0
- package/vg-coder-cli-1.0.6.tgz +0 -0
package/README.md
CHANGED
|
@@ -4,14 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
## ✨ Tính năng
|
|
6
6
|
|
|
7
|
-
- 🔍 **Phát hiện loại dự án**: Tự động nhận diện Angular, Spring Boot, React, Vue, Node.js, Python, Java, .NET
|
|
8
|
-
- 📁 **Xử lý
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
7
|
+
- 🔍 **Phát hiện loại dự án**: Tự động nhận diện Angular, Spring Boot, React, Vue, Node.js, Python, Java, .NET.
|
|
8
|
+
- 📁 **Xử lý `.gitignore`**: Tuân thủ chuẩn Git với multi-level ignore rules.
|
|
9
|
+
- 🛡️ **Hỗ trợ `.vgignore`**: Có độ ưu tiên cao hơn `.gitignore`, với cú pháp giống hệt.
|
|
10
|
+
- 📜 **Bỏ qua file mặc định**: Tự động bỏ qua các thư mục phổ biến như `node_modules`, `dist`, `.git`, `build`, `target` và các file cấu hình IDE.
|
|
11
|
+
- 📄 **Scan và nối file**: Quét toàn bộ dự án và nối các file mã nguồn lại với nhau.
|
|
12
|
+
- 🧮 **Đếm token**: Sử dụng `tiktoken` để đếm token chính xác cho các mô hình AI.
|
|
13
|
+
- ✂️ **Chia nhỏ nội dung**: Chia nội dung thông minh thành các chunk nhỏ hơn mà vẫn giữ cấu trúc file.
|
|
14
|
+
- 🌐 **Xuất HTML**: Tạo báo cáo HTML tương tác với syntax highlighting và các nút bấm sao chép.
|
|
15
|
+
- 📋 **Sao chép vào Clipboard**: Chế độ `--clipboard-only` giúp sao chép toàn bộ mã nguồn đã xử lý vào clipboard, không cần tạo file.
|
|
16
|
+
- 🤖 **Tối ưu cho AI**: Xuất file `combined.txt` với định dạng thân thiện cho các mô hình AI và cung cấp mẫu script để hướng dẫn AI.
|
|
17
|
+
- 🌳 **Cây thư mục**: Hiển thị và cho phép sao chép cấu trúc cây thư mục của dự án trong giao diện HTML.
|
|
18
|
+
- 🔍 **Tìm kiếm tích hợp**: Giao diện HTML đi kèm chức năng tìm kiếm nội dung trực tiếp trong code.
|
|
15
19
|
|
|
16
20
|
## 📦 Cài đặt
|
|
17
21
|
|
|
@@ -39,7 +43,7 @@ chmod +x bin/vg-coder.js
|
|
|
39
43
|
|
|
40
44
|
## 🚀 Sử dụng
|
|
41
45
|
|
|
42
|
-
### Phân tích dự án
|
|
46
|
+
### Phân tích dự án và xuất HTML
|
|
43
47
|
```bash
|
|
44
48
|
# Nếu cài global
|
|
45
49
|
vg-coder analyze
|
|
@@ -54,6 +58,16 @@ vg-coder analyze /path/to/project
|
|
|
54
58
|
vg-coder analyze /path/to/project --max-tokens 8192 --output ./my-output --theme monokai
|
|
55
59
|
```
|
|
56
60
|
|
|
61
|
+
### Sao chép nhanh vào Clipboard (Không tạo file)
|
|
62
|
+
Chế độ này rất hữu ích để nhanh chóng đưa toàn bộ ngữ cảnh dự án vào các công cụ AI.
|
|
63
|
+
```bash
|
|
64
|
+
# Phân tích và sao chép toàn bộ code vào clipboard
|
|
65
|
+
vg-coder analyze --clipboard-only
|
|
66
|
+
|
|
67
|
+
# Hoặc dùng alias ngắn gọn
|
|
68
|
+
vg-coder analyze --clipboard
|
|
69
|
+
```
|
|
70
|
+
|
|
57
71
|
### Xem thông tin dự án
|
|
58
72
|
```bash
|
|
59
73
|
# Thông tin dự án hiện tại
|
|
@@ -66,10 +80,43 @@ vg-coder info /path/to/project
|
|
|
66
80
|
### Xóa output
|
|
67
81
|
```bash
|
|
68
82
|
# Xóa output mặc định
|
|
69
|
-
|
|
83
|
+
vg-coder clean
|
|
70
84
|
|
|
71
85
|
# Xóa output tùy chỉnh
|
|
72
|
-
|
|
86
|
+
vg-coder clean --output ./my-output
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 📜 Trợ giúp (Help)
|
|
90
|
+
|
|
91
|
+
Bạn có thể xem tất cả các lệnh và tùy chọn có sẵn bằng cách sử dụng cờ `--help` hoặc `-h`.
|
|
92
|
+
|
|
93
|
+
### Trợ giúp chung
|
|
94
|
+
Để xem danh sách các lệnh chính:
|
|
95
|
+
```bash
|
|
96
|
+
vg-coder --help
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Output (ví dụ):**
|
|
100
|
+
```
|
|
101
|
+
Usage: vg-coder [command] [options]
|
|
102
|
+
|
|
103
|
+
CLI tool để phân tích dự án, nối file mã nguồn, đếm token và xuất HTML
|
|
104
|
+
|
|
105
|
+
Options:
|
|
106
|
+
-V, --version output the version number
|
|
107
|
+
-h, --help display help for command
|
|
108
|
+
|
|
109
|
+
Commands:
|
|
110
|
+
analyze [path] Phân tích dự án và tạo output HTML
|
|
111
|
+
info [path] Hiển thị thông tin về dự án
|
|
112
|
+
clean Xóa thư mục output
|
|
113
|
+
help [command] display help for command
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Trợ giúp cho lệnh cụ thể
|
|
117
|
+
Để xem chi tiết các tùy chọn cho một lệnh cụ thể (ví dụ: `analyze`):
|
|
118
|
+
```bash
|
|
119
|
+
vg-coder analyze --help
|
|
73
120
|
```
|
|
74
121
|
|
|
75
122
|
## ⚙️ Options
|
|
@@ -79,68 +126,42 @@ node src/index.js clean --output ./my-output
|
|
|
79
126
|
| `--max-tokens <number>` | Số token tối đa mỗi chunk | 8000 |
|
|
80
127
|
| `--model <model>` | Model AI để đếm token | gpt-4 |
|
|
81
128
|
| `--output <path>` | Thư mục output | ./vg-output |
|
|
82
|
-
| `--extensions <list>` | Danh sách extensions (comma-separated) |
|
|
83
|
-
| `--include-hidden` | Bao gồm file ẩn | false |
|
|
84
|
-
| `--no-structure` | Không ưu tiên giữ cấu trúc file | false |
|
|
129
|
+
| `--extensions <list>` | Danh sách extensions (comma-separated) | Tự động phát hiện |
|
|
130
|
+
| `--include-hidden` | Bao gồm file ẩn (bị bỏ qua mặc định) | false |
|
|
131
|
+
| `--no-structure` | Không ưu tiên giữ cấu trúc file khi chia chunk | false |
|
|
85
132
|
| `--theme <theme>` | Theme cho syntax highlighting | github |
|
|
133
|
+
| `--clipboard-only` | Sao chép nội dung vào clipboard thay vì tạo file output. | false |
|
|
134
|
+
| `--clipboard` | Alias cho `--clipboard-only` | false |
|
|
86
135
|
|
|
87
|
-
## 📋 Ví dụ chi tiết
|
|
88
136
|
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
node src/index.js analyze ./my-angular-app --max-tokens 6000 --theme monokai
|
|
92
|
-
```
|
|
137
|
+
## 🤖 Tối ưu cho AI (AI Optimization)
|
|
93
138
|
|
|
94
|
-
###
|
|
95
|
-
|
|
96
|
-
node src/index.js analyze ./my-project --extensions "js,ts,vue,css" --include-hidden
|
|
97
|
-
```
|
|
139
|
+
### File `combined.txt`
|
|
140
|
+
Công cụ tạo ra file `combined.txt` được định dạng đặc biệt để dễ dàng đưa vào các mô hình ngôn ngữ lớn. Mỗi file được phân tách rõ ràng bằng một header duy nhất, giúp AI nhận biết và xử lý chính xác từng file.
|
|
98
141
|
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
node src/index.js info ./my-project
|
|
102
|
-
# Output:
|
|
103
|
-
# 📁 Project Information:
|
|
104
|
-
# Path: /path/to/my-project
|
|
105
|
-
# Primary Type: nodejs
|
|
106
|
-
#
|
|
107
|
-
# Detected Technologies:
|
|
108
|
-
# nodejs: high confidence
|
|
109
|
-
#
|
|
110
|
-
# 📊 File Statistics:
|
|
111
|
-
# Total Files: 25
|
|
112
|
-
# Total Size: 156.7 KB
|
|
113
|
-
# Total Lines: 4,523
|
|
114
|
-
# Extensions: js, ts, json, md
|
|
142
|
+
**Ví dụ định dạng:**
|
|
115
143
|
```
|
|
144
|
+
// ===== FILE: src/index.js =====
|
|
145
|
+
... nội dung file index.js ...
|
|
116
146
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
# Chạy tất cả tests
|
|
121
|
-
npm test
|
|
122
|
-
|
|
123
|
-
# Chạy tests với coverage
|
|
124
|
-
npm run test:coverage
|
|
125
|
-
|
|
126
|
-
# Chạy specific test
|
|
127
|
-
npm test -- --testNamePattern="project detection"
|
|
147
|
+
// ===== FILE: src/utils.js =====
|
|
148
|
+
... nội dung file utils.js ...
|
|
128
149
|
```
|
|
129
150
|
|
|
151
|
+
### Mẫu Script Hướng Dẫn AI
|
|
152
|
+
Trang `combined.html` có sẵn một mẫu hướng dẫn (prompt template) để yêu cầu AI trả về các thay đổi dưới dạng script shell. Điều này giúp tự động hóa việc áp dụng các thay đổi do AI đề xuất một cách an toàn và có thể kiểm soát.
|
|
153
|
+
|
|
130
154
|
## 📁 Cấu trúc Output
|
|
131
155
|
|
|
132
156
|
```
|
|
133
157
|
vg-output/
|
|
134
|
-
├── index.html # Trang chính với navigation
|
|
135
|
-
├── combined.html # Tất cả code trong một file
|
|
136
|
-
├──
|
|
158
|
+
├── index.html # Trang chính với navigation và cây thư mục
|
|
159
|
+
├── combined.html # Tất cả code trong một file HTML, có chức năng tìm kiếm
|
|
160
|
+
├── combined.txt # Tất cả code trong một file text, tối ưu cho AI
|
|
161
|
+
├── chunks/ # Các chunk riêng biệt (nếu nội dung lớn)
|
|
137
162
|
│ ├── chunk-1.html
|
|
138
|
-
│ ├── chunk-2.html
|
|
139
163
|
│ └── ...
|
|
140
|
-
└── assets/ # CSS, JS
|
|
141
|
-
├── styles.css
|
|
142
|
-
├── scripts.js
|
|
143
|
-
└── highlight.css
|
|
164
|
+
└── assets/ # CSS, JS cho trang HTML
|
|
144
165
|
```
|
|
145
166
|
|
|
146
167
|
## 🎯 Các loại dự án được hỗ trợ
|
|
@@ -151,28 +172,20 @@ vg-output/
|
|
|
151
172
|
- **Languages**: JavaScript, TypeScript, Java, Python, C#, Go, Rust
|
|
152
173
|
- **Config**: JSON, YAML, XML, TOML
|
|
153
174
|
|
|
154
|
-
##
|
|
155
|
-
|
|
156
|
-
### Extensions mặc định
|
|
157
|
-
Tool tự động detect extensions phù hợp với loại dự án:
|
|
158
|
-
- **Web**: .js, .jsx, .ts, .tsx, .vue, .html, .css, .scss
|
|
159
|
-
- **Backend**: .java, .py, .cs, .go, .rs, .php
|
|
160
|
-
- **Config**: .json, .yaml, .xml, .toml, .env
|
|
175
|
+
## 🛡️ Quy tắc bỏ qua file (Ignoring Files)
|
|
161
176
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
- vs2015
|
|
167
|
-
- rainbow
|
|
177
|
+
Công cụ tuân thủ các quy tắc bỏ qua file theo thứ tự ưu tiên sau:
|
|
178
|
+
1. **`.vgignore`**: Các quy tắc trong file này có độ ưu tiên cao nhất.
|
|
179
|
+
2. **`.gitignore`**: Các quy tắc trong file `.gitignore` sẽ được áp dụng.
|
|
180
|
+
3. **Quy tắc mặc định**: Nếu không có các file trên, công cụ sẽ tự động bỏ qua các thư mục và file phổ biến như `node_modules`, `.git`, `dist`, `build`, `target`, các file log, và các thư mục cấu hình của IDE (`.vscode`, `.idea`).
|
|
168
181
|
|
|
169
182
|
## 🤝 Đóng góp
|
|
170
183
|
|
|
171
|
-
1.
|
|
172
|
-
2.
|
|
173
|
-
3.
|
|
174
|
-
4.
|
|
175
|
-
5.
|
|
184
|
+
1. Fork repository
|
|
185
|
+
2. Tạo feature branch (`git checkout -b feature/amazing-feature`)
|
|
186
|
+
3. Commit changes (`git commit -m 'Add amazing feature'`)
|
|
187
|
+
4. Push to branch (`git push origin feature/amazing-feature`)
|
|
188
|
+
5. Tạo Pull Request
|
|
176
189
|
|
|
177
190
|
## 📄 License
|
|
178
191
|
|
package/package.json
CHANGED
|
@@ -39,11 +39,15 @@ class HtmlExporter {
|
|
|
39
39
|
|
|
40
40
|
// Tạo combined view
|
|
41
41
|
await this.createCombinedPage(chunks, metadata);
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
// Tạo combined.txt cho AI tools
|
|
44
|
+
await this.createCombinedTxtFile(chunks, metadata);
|
|
45
|
+
|
|
43
46
|
return {
|
|
44
47
|
indexPath: path.join(this.outputPath, 'index.html'),
|
|
45
48
|
chunksPath: path.join(this.outputPath, 'chunks'),
|
|
46
49
|
combinedPath: path.join(this.outputPath, 'combined.html'),
|
|
50
|
+
combinedTxtPath: path.join(this.outputPath, 'combined.txt'),
|
|
47
51
|
totalFiles: chunks.length
|
|
48
52
|
};
|
|
49
53
|
}
|
|
@@ -250,6 +254,126 @@ Nếu file chưa tồn tại, script sẽ tự tạo file và thư mục cha.</c
|
|
|
250
254
|
await fs.writeFile(path.join(this.outputPath, 'combined.html'), html);
|
|
251
255
|
}
|
|
252
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Tạo combined.txt file cho AI tools với formatting tối ưu
|
|
259
|
+
*/
|
|
260
|
+
async createCombinedTxtFile(chunks, metadata) {
|
|
261
|
+
// Sử dụng files gốc nếu có, nếu không thì dùng chunks
|
|
262
|
+
const files = metadata.files;
|
|
263
|
+
|
|
264
|
+
if (files && files.length > 0) {
|
|
265
|
+
// Tạo content từ files gốc với formatting AI-friendly
|
|
266
|
+
let content = '';
|
|
267
|
+
|
|
268
|
+
// Minimal header cho AI context
|
|
269
|
+
content += `// VG Coder Analysis - ${metadata.projectInfo?.primary || 'Unknown'} Project\n`;
|
|
270
|
+
content += `// Files: ${files.length} | Generated: ${new Date().toISOString()}\n\n`;
|
|
271
|
+
|
|
272
|
+
// Nội dung từng file với boundaries rõ ràng
|
|
273
|
+
for (let i = 0; i < files.length; i++) {
|
|
274
|
+
const file = files[i];
|
|
275
|
+
|
|
276
|
+
// File boundary marker
|
|
277
|
+
content += `// ===== FILE: ${file.relativePath} =====\n`;
|
|
278
|
+
|
|
279
|
+
// Nội dung file nguyên bản
|
|
280
|
+
content += file.content;
|
|
281
|
+
|
|
282
|
+
// Đảm bảo file kết thúc bằng newline
|
|
283
|
+
if (!file.content.endsWith('\n')) {
|
|
284
|
+
content += '\n';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Separator giữa các files
|
|
288
|
+
if (i < files.length - 1) {
|
|
289
|
+
content += '\n';
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
await fs.writeFile(path.join(this.outputPath, 'combined.txt'), content, 'utf8');
|
|
294
|
+
} else {
|
|
295
|
+
// Fallback: sử dụng chunks (legacy)
|
|
296
|
+
let content = '';
|
|
297
|
+
|
|
298
|
+
// Minimal header cho AI context
|
|
299
|
+
content += `// VG Coder Analysis - ${metadata.projectInfo?.primary || 'Unknown'} Project\n`;
|
|
300
|
+
content += `// Files: ${chunks.length} | Generated: ${new Date().toISOString()}\n\n`;
|
|
301
|
+
|
|
302
|
+
// Parse chunks để extract file content với boundaries rõ ràng
|
|
303
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
304
|
+
const chunk = chunks[i];
|
|
305
|
+
|
|
306
|
+
// Extract file content từ chunk, bỏ qua headers và separators
|
|
307
|
+
const cleanContent = this.extractCleanContent(chunk.content);
|
|
308
|
+
|
|
309
|
+
if (cleanContent.trim()) {
|
|
310
|
+
content += cleanContent;
|
|
311
|
+
|
|
312
|
+
// Đảm bảo kết thúc bằng newline
|
|
313
|
+
if (!cleanContent.endsWith('\n')) {
|
|
314
|
+
content += '\n';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Separator giữa các chunks (minimal)
|
|
318
|
+
if (i < chunks.length - 1) {
|
|
319
|
+
content += '\n';
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
await fs.writeFile(path.join(this.outputPath, 'combined.txt'), content, 'utf8');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Extract clean content từ chunk, loại bỏ headers và formatting
|
|
330
|
+
*/
|
|
331
|
+
extractCleanContent(chunkContent) {
|
|
332
|
+
const lines = chunkContent.split('\n');
|
|
333
|
+
let cleanLines = [];
|
|
334
|
+
let inFileContent = false;
|
|
335
|
+
let currentFilePath = '';
|
|
336
|
+
|
|
337
|
+
for (let i = 0; i < lines.length; i++) {
|
|
338
|
+
const line = lines[i];
|
|
339
|
+
|
|
340
|
+
// Detect file header
|
|
341
|
+
if (line.includes('================================================================================')) {
|
|
342
|
+
if (i + 1 < lines.length && lines[i + 1].startsWith('File: ')) {
|
|
343
|
+
// Start of new file
|
|
344
|
+
currentFilePath = lines[i + 1].replace('File: ', '').trim();
|
|
345
|
+
cleanLines.push(`// ===== FILE: ${currentFilePath} =====`);
|
|
346
|
+
inFileContent = false;
|
|
347
|
+
i += 2; // Skip header lines
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Skip project headers and structure
|
|
353
|
+
if (line.startsWith('# Project Analysis Report') ||
|
|
354
|
+
line.startsWith('Generated:') ||
|
|
355
|
+
line.startsWith('Project Path:') ||
|
|
356
|
+
line.startsWith('## Statistics') ||
|
|
357
|
+
line.startsWith('## Project Structure') ||
|
|
358
|
+
line.startsWith('- Total') ||
|
|
359
|
+
line.startsWith('- Extensions:') ||
|
|
360
|
+
line.startsWith('```') ||
|
|
361
|
+
line.startsWith('├──') ||
|
|
362
|
+
line.startsWith('└──') ||
|
|
363
|
+
line.trim() === '') {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Add actual file content
|
|
368
|
+
if (currentFilePath && !line.includes('================================================================================')) {
|
|
369
|
+
cleanLines.push(line);
|
|
370
|
+
inFileContent = true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return cleanLines.join('\n');
|
|
375
|
+
}
|
|
376
|
+
|
|
253
377
|
/**
|
|
254
378
|
* Copy static assets
|
|
255
379
|
*/
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const ProjectDetector = require('./detectors/project-detector');
|
|
|
9
9
|
const FileScanner = require('./scanner/file-scanner');
|
|
10
10
|
const TokenManager = require('./tokenizer/token-manager');
|
|
11
11
|
const HtmlExporter = require('./exporter/html-exporter');
|
|
12
|
+
const ClipboardManager = require('./utils/clipboard');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Main CLI Application
|
|
@@ -39,6 +40,8 @@ class VGCoderCLI {
|
|
|
39
40
|
.option('--include-hidden', 'Bao gồm file ẩn')
|
|
40
41
|
.option('--no-structure', 'Không ưu tiên giữ cấu trúc file')
|
|
41
42
|
.option('--theme <theme>', 'Theme cho syntax highlighting', 'github')
|
|
43
|
+
.option('--clipboard-only', 'Copy content to clipboard without creating files')
|
|
44
|
+
.option('--clipboard', 'Alias for --clipboard-only')
|
|
42
45
|
.action(this.handleAnalyze.bind(this));
|
|
43
46
|
|
|
44
47
|
// Info command
|
|
@@ -60,17 +63,25 @@ class VGCoderCLI {
|
|
|
60
63
|
*/
|
|
61
64
|
async handleAnalyze(projectPath, options) {
|
|
62
65
|
const spinner = ora('Initializing analysis...').start();
|
|
63
|
-
|
|
66
|
+
|
|
64
67
|
try {
|
|
65
68
|
// Resolve project path
|
|
66
69
|
projectPath = path.resolve(projectPath || process.cwd());
|
|
67
|
-
|
|
70
|
+
|
|
71
|
+
// Check if clipboard-only mode
|
|
72
|
+
const clipboardMode = options.clipboardOnly || options.clipboard;
|
|
73
|
+
const outputPath = clipboardMode ? null : path.resolve(options.output || './vg-output');
|
|
68
74
|
|
|
69
75
|
// Validate project path
|
|
70
76
|
if (!await fs.pathExists(projectPath)) {
|
|
71
77
|
throw new Error(`Project path does not exist: ${projectPath}`);
|
|
72
78
|
}
|
|
73
79
|
|
|
80
|
+
// Validate output path for non-clipboard mode
|
|
81
|
+
if (!clipboardMode && !outputPath) {
|
|
82
|
+
throw new Error('Output path is required for non-clipboard mode');
|
|
83
|
+
}
|
|
84
|
+
|
|
74
85
|
spinner.text = 'Detecting project type...';
|
|
75
86
|
|
|
76
87
|
// Detect project type
|
|
@@ -123,10 +134,42 @@ class VGCoderCLI {
|
|
|
123
134
|
console.log(`Estimated Chunks: ${tokenAnalysis.summary.estimatedChunks}`);
|
|
124
135
|
|
|
125
136
|
spinner.text = 'Creating content chunks...';
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
|
|
138
|
+
if (clipboardMode) {
|
|
139
|
+
// Clipboard mode: create AI-friendly content and copy to clipboard
|
|
140
|
+
spinner.text = 'Creating AI-friendly content...';
|
|
141
|
+
|
|
142
|
+
const aiContent = await scanner.createCombinedContentForAI(scanResult.files, {
|
|
143
|
+
includeStats: false,
|
|
144
|
+
includeTree: false,
|
|
145
|
+
preserveLineNumbers: true
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
spinner.text = 'Copying to clipboard...';
|
|
149
|
+
|
|
150
|
+
await ClipboardManager.copyToClipboard(aiContent);
|
|
151
|
+
const contentInfo = ClipboardManager.getContentInfo(aiContent);
|
|
152
|
+
|
|
153
|
+
// Cleanup
|
|
154
|
+
tokenManager.cleanup();
|
|
155
|
+
|
|
156
|
+
spinner.succeed('Content copied to clipboard successfully!');
|
|
157
|
+
|
|
158
|
+
console.log(chalk.green('\n📋 Clipboard Content:'));
|
|
159
|
+
console.log(`Files: ${chalk.cyan(scanResult.files.length)}`);
|
|
160
|
+
console.log(`Lines: ${chalk.cyan(contentInfo.lines.toLocaleString())}`);
|
|
161
|
+
console.log(`Characters: ${chalk.cyan(contentInfo.characters.toLocaleString())}`);
|
|
162
|
+
console.log(`Size: ${chalk.cyan(contentInfo.size)}`);
|
|
163
|
+
|
|
164
|
+
console.log(chalk.blue('\n💡 Ready for AI analysis!'));
|
|
165
|
+
console.log('Content is now in your clipboard and ready to paste into AI tools.');
|
|
166
|
+
|
|
167
|
+
return; // Exit early for clipboard mode
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Normal mode: create HTML output
|
|
128
171
|
const combinedContent = await scanner.createCombinedContent(scanResult.files);
|
|
129
|
-
|
|
172
|
+
|
|
130
173
|
// Chunk content
|
|
131
174
|
const chunks = await tokenManager.chunkContent(combinedContent, {
|
|
132
175
|
projectType: projectInfo.primary,
|
|
@@ -153,7 +196,8 @@ class VGCoderCLI {
|
|
|
153
196
|
projectInfo: projectInfo,
|
|
154
197
|
scanStats: scanResult.stats,
|
|
155
198
|
tokenStats: tokenAnalysis.summary,
|
|
156
|
-
directoryStructure: treeStructure
|
|
199
|
+
directoryStructure: treeStructure,
|
|
200
|
+
files: scanResult.files // Thêm files gốc để tạo combined.txt
|
|
157
201
|
});
|
|
158
202
|
|
|
159
203
|
// Cleanup
|
|
@@ -164,6 +208,9 @@ class VGCoderCLI {
|
|
|
164
208
|
console.log(chalk.green('\n✅ Output Generated:'));
|
|
165
209
|
console.log(`Index: ${chalk.cyan(exportResult.indexPath)}`);
|
|
166
210
|
console.log(`Combined: ${chalk.cyan(exportResult.combinedPath)}`);
|
|
211
|
+
if (exportResult.combinedTxtPath) {
|
|
212
|
+
console.log(`Combined.txt: ${chalk.cyan(exportResult.combinedTxtPath)}`);
|
|
213
|
+
}
|
|
167
214
|
console.log(`Chunks: ${chalk.cyan(exportResult.chunksPath)}`);
|
|
168
215
|
console.log(`Total Files: ${exportResult.totalFiles}`);
|
|
169
216
|
|
|
@@ -446,6 +446,54 @@ Size: {size} bytes | Lines: {lines}
|
|
|
446
446
|
return '\n\n';
|
|
447
447
|
}
|
|
448
448
|
|
|
449
|
+
/**
|
|
450
|
+
* Tạo nội dung kết hợp cho AI tools với formatting chính xác
|
|
451
|
+
*/
|
|
452
|
+
async createCombinedContentForAI(files, options = {}) {
|
|
453
|
+
const {
|
|
454
|
+
includeStats = false,
|
|
455
|
+
includeTree = false,
|
|
456
|
+
preserveLineNumbers = true
|
|
457
|
+
} = options;
|
|
458
|
+
|
|
459
|
+
let content = '';
|
|
460
|
+
|
|
461
|
+
// Header với thông tin project (tùy chọn)
|
|
462
|
+
if (includeStats) {
|
|
463
|
+
content += this.generateProjectHeader(files);
|
|
464
|
+
content += '\n\n';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Cấu trúc thư mục (tùy chọn)
|
|
468
|
+
if (includeTree) {
|
|
469
|
+
content += this.generateTreeStructure(files);
|
|
470
|
+
content += '\n\n';
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Nội dung từng file với formatting chính xác
|
|
474
|
+
for (let i = 0; i < files.length; i++) {
|
|
475
|
+
const file = files[i];
|
|
476
|
+
|
|
477
|
+
// File boundary marker - không ảnh hưởng line numbering
|
|
478
|
+
content += `// ===== FILE: ${file.relativePath} =====\n`;
|
|
479
|
+
|
|
480
|
+
// Nội dung file nguyên bản
|
|
481
|
+
content += file.content;
|
|
482
|
+
|
|
483
|
+
// Đảm bảo file kết thúc bằng newline
|
|
484
|
+
if (!file.content.endsWith('\n')) {
|
|
485
|
+
content += '\n';
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Separator giữa các files
|
|
489
|
+
if (i < files.length - 1) {
|
|
490
|
+
content += '\n';
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return content;
|
|
495
|
+
}
|
|
496
|
+
|
|
449
497
|
/**
|
|
450
498
|
* Tạo header thông tin project
|
|
451
499
|
*/
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Cross-platform clipboard utility
|
|
6
|
+
*/
|
|
7
|
+
class ClipboardManager {
|
|
8
|
+
/**
|
|
9
|
+
* Copy text to clipboard
|
|
10
|
+
*/
|
|
11
|
+
static async copyToClipboard(text) {
|
|
12
|
+
const platform = os.platform();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
switch (platform) {
|
|
16
|
+
case 'darwin': // macOS
|
|
17
|
+
return await this.copyMacOS(text);
|
|
18
|
+
case 'win32': // Windows
|
|
19
|
+
return await this.copyWindows(text);
|
|
20
|
+
case 'linux': // Linux
|
|
21
|
+
return await this.copyLinux(text);
|
|
22
|
+
default:
|
|
23
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw new Error(`Failed to copy to clipboard: ${error.message}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Copy to clipboard on macOS
|
|
32
|
+
*/
|
|
33
|
+
static async copyMacOS(text) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const pbcopy = spawn('pbcopy');
|
|
36
|
+
|
|
37
|
+
pbcopy.stdin.write(text);
|
|
38
|
+
pbcopy.stdin.end();
|
|
39
|
+
|
|
40
|
+
pbcopy.on('close', (code) => {
|
|
41
|
+
if (code === 0) {
|
|
42
|
+
resolve();
|
|
43
|
+
} else {
|
|
44
|
+
reject(new Error(`pbcopy exited with code ${code}`));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
pbcopy.on('error', (error) => {
|
|
49
|
+
reject(error);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Copy to clipboard on Windows
|
|
56
|
+
*/
|
|
57
|
+
static async copyWindows(text) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const clip = spawn('clip');
|
|
60
|
+
|
|
61
|
+
clip.stdin.write(text);
|
|
62
|
+
clip.stdin.end();
|
|
63
|
+
|
|
64
|
+
clip.on('close', (code) => {
|
|
65
|
+
if (code === 0) {
|
|
66
|
+
resolve();
|
|
67
|
+
} else {
|
|
68
|
+
reject(new Error(`clip exited with code ${code}`));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
clip.on('error', (error) => {
|
|
73
|
+
reject(error);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Copy to clipboard on Linux
|
|
80
|
+
*/
|
|
81
|
+
static async copyLinux(text) {
|
|
82
|
+
// Try xclip first, then xsel as fallback
|
|
83
|
+
try {
|
|
84
|
+
return await this.copyLinuxXclip(text);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
try {
|
|
87
|
+
return await this.copyLinuxXsel(text);
|
|
88
|
+
} catch (xselError) {
|
|
89
|
+
throw new Error('Neither xclip nor xsel is available. Please install one of them.');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Copy using xclip
|
|
96
|
+
*/
|
|
97
|
+
static async copyLinuxXclip(text) {
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
const xclip = spawn('xclip', ['-selection', 'clipboard']);
|
|
100
|
+
|
|
101
|
+
xclip.stdin.write(text);
|
|
102
|
+
xclip.stdin.end();
|
|
103
|
+
|
|
104
|
+
xclip.on('close', (code) => {
|
|
105
|
+
if (code === 0) {
|
|
106
|
+
resolve();
|
|
107
|
+
} else {
|
|
108
|
+
reject(new Error(`xclip exited with code ${code}`));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
xclip.on('error', (error) => {
|
|
113
|
+
reject(error);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Copy using xsel
|
|
120
|
+
*/
|
|
121
|
+
static async copyLinuxXsel(text) {
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
const xsel = spawn('xsel', ['--clipboard', '--input']);
|
|
124
|
+
|
|
125
|
+
xsel.stdin.write(text);
|
|
126
|
+
xsel.stdin.end();
|
|
127
|
+
|
|
128
|
+
xsel.on('close', (code) => {
|
|
129
|
+
if (code === 0) {
|
|
130
|
+
resolve();
|
|
131
|
+
} else {
|
|
132
|
+
reject(new Error(`xsel exited with code ${code}`));
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
xsel.on('error', (error) => {
|
|
137
|
+
reject(error);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get clipboard content size info
|
|
144
|
+
*/
|
|
145
|
+
static getContentInfo(text) {
|
|
146
|
+
const lines = text.split('\n').length;
|
|
147
|
+
const chars = text.length;
|
|
148
|
+
const bytes = Buffer.byteLength(text, 'utf8');
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
lines,
|
|
152
|
+
characters: chars,
|
|
153
|
+
bytes,
|
|
154
|
+
size: this.formatBytes(bytes)
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Format bytes to human readable
|
|
160
|
+
*/
|
|
161
|
+
static formatBytes(bytes) {
|
|
162
|
+
if (bytes === 0) return '0 Bytes';
|
|
163
|
+
const k = 1024;
|
|
164
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
165
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
166
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = ClipboardManager;
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"name": "test-large", "version": "1.0.0"}
|
package/test-small/package.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"name": "test-small", "version": "1.0.0"}
|
package/test-small/src/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
console.log("Hello World");
|
package/vg-coder-cli-1.0.2.tgz
DELETED
|
Binary file
|
package/vg-coder-cli-1.0.3.tgz
DELETED
|
Binary file
|
package/vg-coder-cli-1.0.4.tgz
DELETED
|
Binary file
|
package/vg-coder-cli-1.0.5.tgz
DELETED
|
Binary file
|
package/vg-coder-cli-1.0.6.tgz
DELETED
|
Binary file
|