vg-coder-cli 2.0.30 → 2.0.32

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.
Files changed (40) hide show
  1. package/ARCHITECTURE.md +255 -0
  2. package/README.md +0 -11
  3. package/change.sh +0 -0
  4. package/dist/vg-coder-bundle.js +42 -0
  5. package/gulpfile.js +111 -0
  6. package/package.json +19 -11
  7. package/scripts/postinstall.js +13 -3
  8. package/src/index.js +28 -220
  9. package/src/server/api-server.js +120 -428
  10. package/src/server/views/css/bubble.css +81 -0
  11. package/src/server/views/css/code-viewer.css +58 -0
  12. package/src/server/views/css/terminal.css +59 -155
  13. package/src/server/views/dashboard.css +78 -678
  14. package/src/server/views/dashboard.html +39 -278
  15. package/src/server/views/js/api.js +2 -22
  16. package/src/server/views/js/config.js +27 -15
  17. package/src/server/views/js/event-protocol.js +263 -0
  18. package/src/server/views/js/features/bubble-features/index.js +125 -0
  19. package/src/server/views/js/features/bubble-features/paste-run-feature.js +16 -0
  20. package/src/server/views/js/features/bubble-features/terminal-feature.js +16 -0
  21. package/src/server/views/js/features/bubble.js +175 -0
  22. package/src/server/views/js/features/code-viewer.js +90 -0
  23. package/src/server/views/js/features/commands.js +34 -81
  24. package/src/server/views/js/features/editor-tabs.js +19 -46
  25. package/src/server/views/js/features/git-view.js +63 -81
  26. package/src/server/views/js/features/iframe-manager.js +3 -97
  27. package/src/server/views/js/features/monaco-manager.js +19 -39
  28. package/src/server/views/js/features/project-switcher.js +7 -63
  29. package/src/server/views/js/features/resize.js +5 -16
  30. package/src/server/views/js/features/structure.js +38 -106
  31. package/src/server/views/js/features/terminal.js +102 -418
  32. package/src/server/views/js/handlers.js +60 -43
  33. package/src/server/views/js/main.js +75 -179
  34. package/src/server/views/js/shadow-entry.js +21 -0
  35. package/src/server/views/js/utils.js +48 -28
  36. package/src/server/views/vg-coder/_metadata/generated_indexed_rulesets/_ruleset1 +0 -0
  37. package/src/server/views/vg-coder/controller.js +33 -258
  38. package/vetgo-auto/chrome/src/utils/injector-script.ts +33 -258
  39. package/vetgo-auto/vg-coder.zip +0 -0
  40. package/src/server/views/dashboard.js +0 -457
package/gulpfile.js ADDED
@@ -0,0 +1,111 @@
1
+ const gulp = require('gulp');
2
+ const concat = require('gulp-concat');
3
+ const htmlmin = require('gulp-htmlmin');
4
+ const cleanCss = require('gulp-clean-css');
5
+ const webpack = require('webpack-stream');
6
+ const replace = require('gulp-replace');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ const PATHS = {
11
+ src: 'src/server/views',
12
+ dist: 'dist',
13
+ entryJs: 'src/server/views/js/shadow-entry.js',
14
+ outputFile: 'vg-coder-bundle.js'
15
+ };
16
+
17
+ // 1. Bundle & Minify CSS
18
+ let cssContent = '';
19
+ gulp.task('css', function() {
20
+ return gulp.src([
21
+ 'node_modules/xterm/css/xterm.css',
22
+ 'node_modules/diff2html/bundles/css/diff2html.min.css',
23
+ 'node_modules/highlight.js/styles/github-dark.css', // ADDED: Highlight.js CSS
24
+ `${PATHS.src}/dashboard.css`,
25
+ `${PATHS.src}/css/*.css`
26
+ ])
27
+ .pipe(concat('bundle.css'))
28
+ .pipe(cleanCss())
29
+ .on('data', function(file) {
30
+ cssContent = file.contents.toString();
31
+ });
32
+ });
33
+
34
+ // 2. Bundle & Minify HTML
35
+ let htmlContent = '';
36
+ gulp.task('html', function() {
37
+ return gulp.src(`${PATHS.src}/dashboard.html`)
38
+ .pipe(htmlmin({
39
+ collapseWhitespace: true,
40
+ removeComments: true,
41
+ removeRedundantAttributes: true,
42
+ minifyCSS: true,
43
+ minifyJS: true
44
+ }))
45
+ .pipe(replace(/<link rel="stylesheet".*?>/g, ''))
46
+ .pipe(replace(/<script.*?>.*?<\/script>/g, ''))
47
+ .on('data', function(file) {
48
+ htmlContent = file.contents.toString();
49
+ });
50
+ });
51
+
52
+ // 3. Bundle JS using Webpack
53
+ let jsContent = '';
54
+ gulp.task('js', function() {
55
+ return gulp.src(PATHS.entryJs)
56
+ .pipe(webpack({
57
+ mode: 'production',
58
+ output: { filename: 'bundle.js' },
59
+ resolve: { extensions: ['.js'] }
60
+ }))
61
+ .on('data', function(file) {
62
+ jsContent = file.contents.toString();
63
+ });
64
+ });
65
+
66
+ // 4. Build Final Injector
67
+ gulp.task('build', gulp.series('css', 'html', 'js', function(done) {
68
+ const finalScript = `
69
+ (function() {
70
+ const CONTAINER_ID = 'vg-coder-shadow-host';
71
+ if (document.getElementById(CONTAINER_ID)) {
72
+ console.log('VG Coder already injected');
73
+ return;
74
+ }
75
+
76
+ const host = document.createElement('div');
77
+ host.id = CONTAINER_ID;
78
+ host.style.cssText = "position:fixed; top:0; left:0; width:100%; height:100%; z-index:2147483647; pointer-events:none;";
79
+ document.body.appendChild(host);
80
+
81
+ const shadow = host.attachShadow({ mode: 'open' });
82
+ window.__VG_CODER_ROOT__ = shadow;
83
+
84
+ const style = document.createElement('style');
85
+ style.textContent = \`${cssContent.replace(/`/g, '\\`').replace(/\\/g, '\\\\')}\`;
86
+ shadow.appendChild(style);
87
+
88
+ const wrapper = document.createElement('div');
89
+ wrapper.id = 'vg-app-root';
90
+ wrapper.style.cssText = "pointer-events: auto; position: absolute; top: 0; left: 0; width: 0; height: 0; overflow: hidden; background: var(--ios-bg, #F2F2F7); display: flex; flex-direction: column; transition: opacity 0.3s ease;";
91
+ wrapper.innerHTML = \`${htmlContent.replace(/`/g, '\\`').replace(/\\/g, '\\\\')}\`;
92
+ shadow.appendChild(wrapper);
93
+
94
+ try {
95
+ ${jsContent}
96
+ } catch (e) {
97
+ console.error('VG Coder Start Error:', e);
98
+ }
99
+ })();
100
+ `;
101
+
102
+ if (!fs.existsSync(PATHS.dist)) fs.mkdirSync(PATHS.dist);
103
+ fs.writeFileSync(path.join(PATHS.dist, PATHS.outputFile), finalScript);
104
+
105
+ console.log('--------------------------------------------------');
106
+ console.log(`✅ Build Complete: ${path.join(PATHS.dist, PATHS.outputFile)}`);
107
+ console.log('--------------------------------------------------');
108
+ done();
109
+ }));
110
+
111
+ gulp.task('default', gulp.series('build'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vg-coder-cli",
3
- "version": "2.0.30",
3
+ "version": "2.0.32",
4
4
  "description": "🚀 CLI tool to analyze projects, concatenate source files, count tokens, and export HTML with syntax highlighting and copy functionality",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -16,8 +16,9 @@
16
16
  "dev": "nodemon src/index.js",
17
17
  "build:extension": "cd vetgo-auto && npm run build",
18
18
  "build:copy": "node scripts/build.js",
19
- "build": "node scripts/build.js",
20
- "push": "npm run build && npm pack && npm publish"
19
+ "build": "npm run build:inject && npm run install:local",
20
+ "push": "npm run build && npm pack && npm publish",
21
+ "build:inject": "gulp"
21
22
  },
22
23
  "keywords": [
23
24
  "cli",
@@ -29,33 +30,40 @@
29
30
  ],
30
31
  "author": "VG Coder",
31
32
  "license": "MIT",
32
- "repository": {
33
- "type": "git",
34
- "url": "https://github.com/tinhthanh/vg-coder-cli.git"
35
- },
36
33
  "dependencies": {
37
34
  "body-parser": "^1.20.2",
38
35
  "chalk": "^4.1.2",
39
36
  "commander": "^11.1.0",
40
37
  "cors": "^2.8.5",
38
+ "diff2html": "^3.4.52",
41
39
  "directory-tree": "^3.5.1",
42
40
  "express": "^4.18.2",
43
- "fs-extra": "^11.2.0",
44
41
  "highlight.js": "^11.9.0",
45
42
  "ignore": "^5.3.0",
46
43
  "node-pty": "^1.1.0",
47
44
  "ora": "^5.4.1",
48
45
  "path": "^0.12.7",
49
46
  "socket.io": "^4.7.2",
50
- "socket.io-client": "^4.7.2",
47
+ "socket.io-client": "^4.8.3",
51
48
  "tiktoken": "^1.0.10",
52
- "vg-coder-cli": "^2.0.15"
49
+ "vg-coder-cli": "^2.0.15",
50
+ "xterm": "^5.3.0",
51
+ "xterm-addon-fit": "^0.8.0"
53
52
  },
54
53
  "devDependencies": {
55
54
  "@types/jest": "^29.5.8",
56
55
  "adm-zip": "^0.5.10",
56
+ "fs-extra": "^11.3.3",
57
+ "gulp": "^5.0.1",
58
+ "gulp-clean-css": "^4.3.0",
59
+ "gulp-concat": "^2.6.1",
60
+ "gulp-htmlmin": "^5.0.1",
61
+ "gulp-replace": "^1.1.4",
62
+ "gulp-terser": "^2.1.0",
57
63
  "jest": "^29.7.0",
58
- "nodemon": "^3.0.2"
64
+ "nodemon": "^3.0.2",
65
+ "webpack": "^5.104.1",
66
+ "webpack-stream": "^7.0.0"
59
67
  },
60
68
  "engines": {
61
69
  "node": ">=16.0.0"
@@ -13,17 +13,27 @@ if (!fs.existsSync(nodePtyPath)) {
13
13
  process.exit(0);
14
14
  }
15
15
 
16
- console.log('🔨 Building node-pty native binaries...');
16
+ console.log('🔨 Building node-pty native binaries from source...');
17
17
 
18
18
  try {
19
- // Run npm install in node-pty directory (which triggers its install scripts)
19
+ // Remove prebuilds directory to force rebuild from source
20
+ const prebuildsPath = path.join(nodePtyPath, 'prebuilds');
21
+ if (fs.existsSync(prebuildsPath)) {
22
+ console.log(' Removing prebuilds directory to force source build...');
23
+ fs.rmSync(prebuildsPath, { recursive: true, force: true });
24
+ }
25
+
26
+ // Force rebuild from source using node-gyp
20
27
  execSync('npm run install', {
21
28
  cwd: nodePtyPath,
22
29
  stdio: 'inherit'
23
30
  });
24
- console.log('✅ node-pty built successfully!');
31
+ console.log('✅ node-pty built successfully from source!');
25
32
  } catch (error) {
26
33
  console.warn('⚠️ node-pty rebuild failed. Terminal functionality may not work.');
34
+ console.warn(' Please ensure you have:');
35
+ console.warn(' - Python 3.x installed');
36
+ console.warn(' - C++ compiler (Xcode Command Line Tools on macOS, Visual Studio Build Tools on Windows)');
27
37
  console.warn(' Run manually: cd node_modules/node-pty && npm run install');
28
38
  // Don't fail the installation
29
39
  process.exit(0);
package/src/index.js CHANGED
@@ -12,25 +12,18 @@ const HtmlExporter = require('./exporter/html-exporter');
12
12
  const ClipboardManager = require('./utils/clipboard');
13
13
  const ApiServer = require('./server/api-server');
14
14
 
15
- /**
16
- * Main CLI Application
17
- */
18
15
  class VGCoderCLI {
19
16
  constructor() {
20
17
  this.program = new Command();
21
18
  this.setupCommands();
22
19
  }
23
20
 
24
- /**
25
- * Setup CLI commands
26
- */
27
21
  setupCommands() {
28
22
  this.program
29
23
  .name('vg')
30
24
  .description('CLI tool để phân tích dự án, nối file mã nguồn, đếm token và xuất HTML')
31
25
  .version(packageJson.version);
32
26
 
33
- // Analyze command
34
27
  this.program
35
28
  .command('analyze [path]')
36
29
  .alias('a')
@@ -47,20 +40,17 @@ class VGCoderCLI {
47
40
  .option('--save-txt', 'Save AI-friendly content to vg-projects.txt file')
48
41
  .action(this.handleAnalyze.bind(this));
49
42
 
50
- // Info command
51
43
  this.program
52
44
  .command('info [path]')
53
45
  .description('Hiển thị thông tin về dự án')
54
46
  .action(this.handleInfo.bind(this));
55
47
 
56
- // Clean command
57
48
  this.program
58
49
  .command('clean')
59
50
  .description('Xóa thư mục output')
60
51
  .option('-o, --output <path>', 'Thư mục output', './vg-output')
61
52
  .action(this.handleClean.bind(this));
62
53
 
63
- // Start server command
64
54
  this.program
65
55
  .command('start')
66
56
  .alias('s')
@@ -69,182 +59,81 @@ class VGCoderCLI {
69
59
  .action(this.handleStart.bind(this));
70
60
  }
71
61
 
72
- /**
73
- * Handle analyze command
74
- */
75
62
  async handleAnalyze(projectPath, options) {
76
63
  const spinner = ora('Initializing analysis...').start();
77
-
78
64
  try {
79
- // Resolve project path
80
65
  projectPath = path.resolve(projectPath || process.cwd());
81
-
82
- // Check special modes
83
66
  const clipboardMode = options.clipboardOnly || options.clipboard;
84
67
  const saveTxtMode = options.saveTxt;
85
68
  const specialMode = clipboardMode || saveTxtMode;
86
69
  const outputPath = specialMode ? null : path.resolve(options.output || './vg-output');
87
70
 
88
- // Validate project path
89
71
  if (!await fs.pathExists(projectPath)) {
90
72
  throw new Error(`Project path does not exist: ${projectPath}`);
91
73
  }
92
-
93
- // Validate output path for normal mode
94
74
  if (!specialMode && !outputPath) {
95
75
  throw new Error('Output path is required for normal mode');
96
76
  }
97
77
 
98
78
  spinner.text = 'Detecting project type...';
99
-
100
- // Detect project type
101
79
  const detector = new ProjectDetector(projectPath);
102
80
  const projectInfo = await detector.detectAll();
103
81
 
104
82
  console.log(chalk.blue('\n📁 Project Detection:'));
105
83
  console.log(`Primary Type: ${chalk.green(projectInfo.primary)}`);
106
- if (Object.keys(projectInfo.detected).length > 1) {
107
- console.log(`Other Types: ${Object.keys(projectInfo.detected).filter(t => t !== projectInfo.primary).join(', ')}`);
108
- }
109
84
 
110
85
  spinner.text = 'Scanning files...';
111
-
112
- // Scan files
113
86
  const scannerOptions = {
114
87
  maxTokens: parseInt(options.maxTokens),
115
88
  extensions: options.extensions ? options.extensions.split(',').map(ext => ext.trim()) : undefined,
116
89
  includeHidden: options.includeHidden
117
90
  };
118
-
119
91
  const scanner = new FileScanner(projectPath, scannerOptions);
120
92
  const scanResult = await scanner.scanProject();
121
93
 
122
94
  console.log(chalk.blue('\n📊 Scan Results:'));
123
95
  console.log(`Files Found: ${scanResult.stats.totalFiles}`);
124
- console.log(`Files Processed: ${scanResult.stats.processedFiles}`);
125
- console.log(`Scan Time: ${scanResult.stats.scanTime}ms`);
126
-
127
- // Hiển thị cấu trúc thư mục được scan
128
- console.log(chalk.blue('\n📁 Directory Structure:'));
129
96
  const treeStructure = scanner.renderProjectTree(scanResult.tree);
130
- console.log(treeStructure);
131
97
 
132
98
  spinner.text = 'Analyzing tokens...';
133
-
134
- // Analyze tokens
135
99
  const tokenManager = new TokenManager({
136
100
  model: options.model,
137
101
  maxTokens: parseInt(options.maxTokens),
138
102
  preserveStructure: options.structure !== false
139
103
  });
140
-
141
104
  const tokenAnalysis = tokenManager.analyzeFiles(scanResult.files);
142
105
 
143
106
  console.log(chalk.blue('\n🧮 Token Analysis:'));
144
107
  console.log(`Total Tokens: ${chalk.yellow(tokenAnalysis.summary.totalTokens.toLocaleString())}`);
145
- console.log(`Average Tokens/File: ${tokenAnalysis.summary.averageTokensPerFile.toLocaleString()}`);
146
- console.log(`Files Exceeding Limit: ${tokenAnalysis.summary.filesExceedingLimit}`);
147
- console.log(`Estimated Chunks: ${tokenAnalysis.summary.estimatedChunks}`);
148
108
 
149
109
  spinner.text = 'Creating content chunks...';
150
110
 
151
111
  if (clipboardMode) {
152
- // Clipboard mode: create AI-friendly content and copy to clipboard
153
- spinner.text = 'Creating AI-friendly content...';
154
-
155
- const aiContent = await scanner.createCombinedContentForAI(scanResult.files, {
156
- includeStats: false,
157
- includeTree: false,
158
- preserveLineNumbers: true
159
- });
160
-
161
- spinner.text = 'Copying to clipboard...';
162
-
112
+ const aiContent = await scanner.createCombinedContentForAI(scanResult.files, { includeStats: false, includeTree: false, preserveLineNumbers: true });
163
113
  await ClipboardManager.copyToClipboard(aiContent);
164
- const contentInfo = ClipboardManager.getContentInfo(aiContent);
165
-
166
- // Cleanup
167
114
  tokenManager.cleanup();
168
-
169
115
  spinner.succeed('Content copied to clipboard successfully!');
170
-
171
- console.log(chalk.green('\n📋 Clipboard Content:'));
172
- console.log(`Files: ${chalk.cyan(scanResult.files.length)}`);
173
- console.log(`Lines: ${chalk.cyan(contentInfo.lines.toLocaleString())}`);
174
- console.log(`Characters: ${chalk.cyan(contentInfo.characters.toLocaleString())}`);
175
- console.log(`Size: ${chalk.cyan(contentInfo.size)}`);
176
-
177
- console.log(chalk.blue('\n💡 Ready for AI analysis!'));
178
- console.log('Content is now in your clipboard and ready to paste into AI tools.');
179
-
180
- return; // Exit early for clipboard mode
116
+ return;
181
117
  }
182
118
 
183
119
  if (saveTxtMode) {
184
- // Save-txt mode: create AI-friendly content and save to vg-projects.txt
185
- spinner.text = 'Creating AI-friendly content...';
186
-
187
- const aiContent = await scanner.createCombinedContentForAI(scanResult.files, {
188
- includeStats: false,
189
- includeTree: false,
190
- preserveLineNumbers: true
191
- });
192
-
193
- spinner.text = 'Saving to vg-projects.txt...';
194
-
120
+ const aiContent = await scanner.createCombinedContentForAI(scanResult.files, { includeStats: false, includeTree: false, preserveLineNumbers: true });
195
121
  const outputFilePath = path.resolve('vg-projects.txt');
196
-
197
- // Ensure file is deleted first
198
- try {
199
- await fs.unlink(outputFilePath);
200
- } catch (error) {
201
- // File doesn't exist, that's fine
202
- }
203
-
204
122
  await fs.writeFile(outputFilePath, aiContent, 'utf8');
205
- const contentInfo = ClipboardManager.getContentInfo(aiContent);
206
-
207
- // Cleanup
208
123
  tokenManager.cleanup();
209
-
210
124
  spinner.succeed('Content saved to vg-projects.txt successfully!');
211
-
212
- console.log(chalk.green('\n📄 File Content:'));
213
- console.log(`Output: ${chalk.cyan(outputFilePath)}`);
214
- console.log(`Files: ${chalk.cyan(scanResult.files.length)}`);
215
- console.log(`Lines: ${chalk.cyan(contentInfo.lines.toLocaleString())}`);
216
- console.log(`Characters: ${chalk.cyan(contentInfo.characters.toLocaleString())}`);
217
- console.log(`Size: ${chalk.cyan(contentInfo.size)}`);
218
-
219
- console.log(chalk.blue('\n💡 Ready for AI analysis!'));
220
- console.log('Content is now saved in vg-projects.txt and ready for AI tools.');
221
-
222
- return; // Exit early for save-txt mode
125
+ return;
223
126
  }
224
127
 
225
- // Normal mode: create HTML output
226
128
  const combinedContent = await scanner.createCombinedContent(scanResult.files);
227
-
228
- // Chunk content
229
129
  const chunks = await tokenManager.chunkContent(combinedContent, {
230
130
  projectType: projectInfo.primary,
231
131
  projectPath: projectPath,
232
132
  totalFiles: scanResult.files.length
233
133
  });
234
134
 
235
- console.log(chalk.blue('\n✂️ Chunking Results:'));
236
- console.log(`Total Chunks: ${chunks.length}`);
237
- chunks.forEach((chunk, index) => {
238
- console.log(` Chunk ${index + 1}: ${chunk.tokens.toLocaleString()} tokens (${chunk.metadata?.type || 'unknown'})`);
239
- });
240
-
241
135
  spinner.text = 'Generating HTML output...';
242
-
243
- // Export HTML
244
- const exporter = new HtmlExporter(outputPath, {
245
- theme: options.theme,
246
- title: `VG Coder Analysis - ${path.basename(projectPath)}`
247
- });
136
+ const exporter = new HtmlExporter(outputPath, { theme: options.theme, title: `VG Coder Analysis - ${path.basename(projectPath)}` });
248
137
 
249
138
  const exportResult = await exporter.exportChunks(chunks, {
250
139
  projectType: projectInfo.primary,
@@ -252,23 +141,11 @@ class VGCoderCLI {
252
141
  scanStats: scanResult.stats,
253
142
  tokenStats: tokenAnalysis.summary,
254
143
  directoryStructure: treeStructure,
255
- files: scanResult.files // Thêm files gốc để tạo combined.txt
144
+ files: scanResult.files
256
145
  });
257
146
 
258
- // Cleanup
259
147
  tokenManager.cleanup();
260
-
261
148
  spinner.succeed('Analysis completed successfully!');
262
-
263
- console.log(chalk.green('\n✅ Output Generated:'));
264
- console.log(`Index: ${chalk.cyan(exportResult.indexPath)}`);
265
- console.log(`Combined: ${chalk.cyan(exportResult.combinedPath)}`);
266
- if (exportResult.combinedTxtPath) {
267
- console.log(`Combined.txt: ${chalk.cyan(exportResult.combinedTxtPath)}`);
268
- }
269
- console.log(`Chunks: ${chalk.cyan(exportResult.chunksPath)}`);
270
- console.log(`Total Files: ${exportResult.totalFiles}`);
271
-
272
149
  console.log(chalk.blue('\n🌐 Open in browser:'));
273
150
  console.log(`file://${exportResult.indexPath}`);
274
151
 
@@ -279,64 +156,29 @@ class VGCoderCLI {
279
156
  }
280
157
  }
281
158
 
282
- /**
283
- * Handle info command
284
- */
285
159
  async handleInfo(projectPath, options) {
286
160
  const spinner = ora('Gathering project information...').start();
287
-
288
161
  try {
289
- // Resolve project path
290
162
  projectPath = path.resolve(projectPath || process.cwd());
291
-
292
163
  if (!await fs.pathExists(projectPath)) {
293
164
  throw new Error(`Project path does not exist: ${projectPath}`);
294
165
  }
295
166
 
296
- // Detect project
297
167
  const detector = new ProjectDetector(projectPath);
298
168
  const projectInfo = await detector.detectAll();
299
-
300
- // Quick scan
301
169
  const scanner = new FileScanner(projectPath);
302
170
  const scanResult = await scanner.scanProject();
303
-
304
- // Token analysis
305
171
  const tokenManager = new TokenManager();
306
172
  const tokenAnalysis = tokenManager.analyzeFiles(scanResult.files);
307
173
 
308
174
  spinner.succeed('Information gathered');
309
-
310
175
  console.log(chalk.blue('\n📁 Project Information:'));
311
176
  console.log(`Path: ${chalk.cyan(projectPath)}`);
312
177
  console.log(`Primary Type: ${chalk.green(projectInfo.primary)}`);
313
-
314
- if (Object.keys(projectInfo.detected).length > 0) {
315
- console.log('\nDetected Technologies:');
316
- Object.entries(projectInfo.detected).forEach(([type, info]) => {
317
- console.log(` ${chalk.yellow(type)}: ${info.confidence} confidence`);
318
- if (info.version) {
319
- console.log(` Version: ${info.version}`);
320
- }
321
- });
322
- }
323
-
324
- console.log(chalk.blue('\n📊 File Statistics:'));
325
178
  console.log(`Total Files: ${scanResult.stats.processedFiles}`);
326
- console.log(`Total Size: ${scanner.formatBytes(scanResult.files.reduce((sum, f) => sum + f.size, 0))}`);
327
- console.log(`Total Lines: ${scanResult.files.reduce((sum, f) => sum + f.lines, 0).toLocaleString()}`);
328
-
329
- const extensions = [...new Set(scanResult.files.map(f => f.extension))].filter(Boolean);
330
- console.log(`Extensions: ${extensions.join(', ')}`);
331
-
332
- console.log(chalk.blue('\n🧮 Token Statistics:'));
333
179
  console.log(`Total Tokens: ${tokenAnalysis.summary.totalTokens.toLocaleString()}`);
334
- console.log(`Average Tokens/File: ${tokenAnalysis.summary.averageTokensPerFile.toLocaleString()}`);
335
- console.log(`Files Exceeding 8K Limit: ${tokenAnalysis.summary.filesExceedingLimit}`);
336
- console.log(`Estimated Chunks (8K): ${tokenAnalysis.summary.estimatedChunks}`);
337
180
 
338
181
  tokenManager.cleanup();
339
-
340
182
  } catch (error) {
341
183
  spinner.fail('Failed to gather information');
342
184
  console.error(chalk.red('\n❌ Error:'), error.message);
@@ -344,22 +186,16 @@ class VGCoderCLI {
344
186
  }
345
187
  }
346
188
 
347
- /**
348
- * Handle clean command
349
- */
350
189
  async handleClean(options) {
351
190
  const spinner = ora('Cleaning output directory...').start();
352
-
353
191
  try {
354
192
  const outputPath = path.resolve(options.output);
355
-
356
193
  if (await fs.pathExists(outputPath)) {
357
194
  await fs.remove(outputPath);
358
195
  spinner.succeed(`Cleaned: ${outputPath}`);
359
196
  } else {
360
197
  spinner.succeed('Output directory does not exist');
361
198
  }
362
-
363
199
  } catch (error) {
364
200
  spinner.fail('Failed to clean');
365
201
  console.error(chalk.red('\n❌ Error:'), error.message);
@@ -367,77 +203,54 @@ class VGCoderCLI {
367
203
  }
368
204
  }
369
205
 
370
- /**
371
- * Handle start command
372
- */
373
206
  async handleStart(options) {
374
207
  try {
375
208
  const projectPath = process.cwd();
376
209
  const initialPort = parseInt(options.port);
377
210
  const projectManager = require('./server/project-manager');
378
211
 
379
- // Check if leader exists
380
212
  const leaderInfo = await projectManager.checkLeader();
381
213
 
382
214
  if (leaderInfo) {
383
- // Leader exists - join as follower
384
215
  console.log(chalk.blue(`\n🔍 Found existing server at port ${leaderInfo.port}`));
385
216
  const joined = await projectManager.joinLeader(leaderInfo, projectPath);
386
-
387
- if (joined) {
388
- // Successfully joined, no need to start new server
389
- return;
390
- } else {
391
- // Failed to join, fallback to starting new server
392
- console.log(chalk.yellow('⚠️ Failed to join leader, starting new server...'));
393
- }
217
+ if (joined) return;
218
+ console.log(chalk.yellow('⚠️ Failed to join leader, starting new server...'));
394
219
  }
395
220
 
396
- // No leader or failed to join - become leader
397
- console.log(chalk.blue('\n🚀 No existing server found, becoming leader...'));
221
+ console.log(chalk.blue('\n🚀 Starting VG Coder Server...'));
398
222
 
399
223
  const server = new ApiServer(initialPort);
400
-
401
- // Try to acquire lock before starting
402
224
  const lockAcquired = await projectManager.acquireLock(initialPort);
403
- if (!lockAcquired) {
404
- throw new Error('Failed to acquire leader lock');
405
- }
225
+ if (!lockAcquired) throw new Error('Failed to acquire leader lock');
406
226
 
407
227
  await server.start();
408
-
409
- // Register current project
410
228
  server.projectManager.registerProject(projectPath);
411
229
 
412
- // Auto-open browser to dashboard using actual port from server
413
- const dashboardUrl = `http://localhost:${server.port}`;
414
- const { exec } = require('child_process');
415
- const platform = process.platform;
230
+ // --- EXTENSION INSTALLATION GUIDE ---
231
+ // Calculate absolute path to the extension folder inside the package
232
+ const extensionPath = path.resolve(__dirname, 'server', 'views', 'vg-coder');
416
233
 
417
- let openCommand;
418
- if (platform === 'darwin') {
419
- openCommand = `open ${dashboardUrl}`;
420
- } else if (platform === 'win32') {
421
- openCommand = `start ${dashboardUrl}`;
422
- } else {
423
- openCommand = `xdg-open ${dashboardUrl}`;
424
- }
234
+ console.log(chalk.green('\n✅ Server is running!'));
235
+ console.log(chalk.cyan('──────────────────────────────────────────────────'));
236
+ console.log(`📡 URL: http://localhost:${server.port}`);
237
+ console.log(chalk.cyan('──────────────────────────────────────────────────'));
425
238
 
426
- exec(openCommand, (error) => {
427
- if (error) {
428
- console.log(chalk.yellow(`\n💡 Open your browser manually: ${dashboardUrl}`));
429
- } else {
430
- console.log(chalk.green(`\n✓ Dashboard opened in browser`));
431
- }
432
- });
239
+ console.log(chalk.yellow('\n🧩 CHƯA CÀI EXTENSION?'));
240
+ console.log('1. Truy cập: chrome://extensions');
241
+ console.log('2. Bật "Developer mode" (Góc phải trên)');
242
+ console.log('3. Bấm "Load unpacked" và chọn thư mục dưới đây:');
243
+ console.log(chalk.bgBlue.white.bold(` ${extensionPath} `));
244
+
245
+ console.log(chalk.yellow('\n👉 HƯỚNG DẪN SỬ DỤNG:'));
246
+ console.log('1. Mở trang chat AI (ChatGPT, Claude, v.v.)');
247
+ console.log('2. Click vào bong bóng 🚀 ở góc phải dưới để mở Dashboard.');
248
+ console.log('3. Bắt đầu code!');
249
+ console.log(chalk.gray('\n(Nhấn Ctrl+C để dừng server)'));
433
250
 
434
- // Handle graceful shutdown
435
251
  const shutdown = async () => {
436
252
  console.log(chalk.yellow('\n\nShutting down server...'));
437
-
438
- // Release lock
439
253
  await projectManager.releaseLock();
440
-
441
254
  await server.stop();
442
255
  process.exit(0);
443
256
  };
@@ -451,18 +264,13 @@ class VGCoderCLI {
451
264
  }
452
265
  }
453
266
 
454
- /**
455
- * Run CLI
456
- */
457
267
  run() {
458
268
  this.program.parse();
459
269
  }
460
270
  }
461
271
 
462
- // Export for testing
463
272
  module.exports = VGCoderCLI;
464
273
 
465
- // Run if called directly
466
274
  if (require.main === module) {
467
275
  const cli = new VGCoderCLI();
468
276
  cli.run();