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.
- package/ARCHITECTURE.md +255 -0
- package/README.md +0 -11
- package/change.sh +0 -0
- package/dist/vg-coder-bundle.js +42 -0
- package/gulpfile.js +111 -0
- package/package.json +19 -11
- package/scripts/postinstall.js +13 -3
- package/src/index.js +28 -220
- package/src/server/api-server.js +120 -428
- package/src/server/views/css/bubble.css +81 -0
- package/src/server/views/css/code-viewer.css +58 -0
- package/src/server/views/css/terminal.css +59 -155
- package/src/server/views/dashboard.css +78 -678
- package/src/server/views/dashboard.html +39 -278
- package/src/server/views/js/api.js +2 -22
- package/src/server/views/js/config.js +27 -15
- package/src/server/views/js/event-protocol.js +263 -0
- package/src/server/views/js/features/bubble-features/index.js +125 -0
- package/src/server/views/js/features/bubble-features/paste-run-feature.js +16 -0
- package/src/server/views/js/features/bubble-features/terminal-feature.js +16 -0
- package/src/server/views/js/features/bubble.js +175 -0
- package/src/server/views/js/features/code-viewer.js +90 -0
- package/src/server/views/js/features/commands.js +34 -81
- package/src/server/views/js/features/editor-tabs.js +19 -46
- package/src/server/views/js/features/git-view.js +63 -81
- package/src/server/views/js/features/iframe-manager.js +3 -97
- package/src/server/views/js/features/monaco-manager.js +19 -39
- package/src/server/views/js/features/project-switcher.js +7 -63
- package/src/server/views/js/features/resize.js +5 -16
- package/src/server/views/js/features/structure.js +38 -106
- package/src/server/views/js/features/terminal.js +102 -418
- package/src/server/views/js/handlers.js +60 -43
- package/src/server/views/js/main.js +75 -179
- package/src/server/views/js/shadow-entry.js +21 -0
- package/src/server/views/js/utils.js +48 -28
- package/src/server/views/vg-coder/_metadata/generated_indexed_rulesets/_ruleset1 +0 -0
- package/src/server/views/vg-coder/controller.js +33 -258
- package/vetgo-auto/chrome/src/utils/injector-script.ts +33 -258
- package/vetgo-auto/vg-coder.zip +0 -0
- 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.
|
|
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": "
|
|
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.
|
|
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"
|
package/scripts/postinstall.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
413
|
-
|
|
414
|
-
const
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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();
|