vg-coder-cli 2.0.3 → 2.0.5

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 (84) hide show
  1. package/README.md +318 -92
  2. package/{vg/apps/api/src/assets/.gitkeep → change.sh} +0 -0
  3. package/package.json +4 -11
  4. package/src/index.js +0 -60
  5. package/src/server/views/dashboard.html +432 -619
  6. package/vg-coder-cli-1.0.17.tgz +0 -0
  7. package/vg-coder-cli-2.0.4.tgz +0 -0
  8. package/vg-coder-cli-2.0.5.tgz +0 -0
  9. package/CHANGELOG.md +0 -59
  10. package/PUBLISHING.md +0 -86
  11. package/SYSTEM_PROMPT.md +0 -157
  12. package/init-nx-monorepo.sh +0 -107
  13. package/vg/.vscode/extensions.json +0 -8
  14. package/vg/.vscode/launch.json +0 -23
  15. package/vg/README-WORKSPACE.md +0 -82
  16. package/vg/README.md +0 -85
  17. package/vg/apps/api/project.json +0 -83
  18. package/vg/apps/api/src/app/analyze.controller.ts +0 -17
  19. package/vg/apps/api/src/app/analyze.service.ts +0 -57
  20. package/vg/apps/api/src/app/app.controller.ts +0 -12
  21. package/vg/apps/api/src/app/app.module.ts +0 -29
  22. package/vg/apps/api/src/app/app.service.ts +0 -8
  23. package/vg/apps/api/src/app/clean.controller.ts +0 -40
  24. package/vg/apps/api/src/app/execute.controller.ts +0 -19
  25. package/vg/apps/api/src/app/execute.service.ts +0 -46
  26. package/vg/apps/api/src/app/info.controller.ts +0 -12
  27. package/vg/apps/api/src/app/info.service.ts +0 -65
  28. package/vg/apps/api/src/main.ts +0 -28
  29. package/vg/apps/api/webpack.config.js +0 -25
  30. package/vg/apps/api-e2e/jest.config.cts +0 -18
  31. package/vg/apps/api-e2e/project.json +0 -17
  32. package/vg/apps/api-e2e/src/support/global-setup.ts +0 -16
  33. package/vg/apps/api-e2e/src/support/global-teardown.ts +0 -10
  34. package/vg/apps/api-e2e/src/support/test-setup.ts +0 -9
  35. package/vg/apps/ng-app/jest.config.ts +0 -21
  36. package/vg/apps/ng-app/project.json +0 -110
  37. package/vg/apps/ng-app/proxy.conf.json +0 -8
  38. package/vg/apps/ng-app/public/favicon.ico +0 -0
  39. package/vg/apps/ng-app/src/app/app.config.ts +0 -17
  40. package/vg/apps/ng-app/src/app/app.html +0 -1
  41. package/vg/apps/ng-app/src/app/app.routes.ts +0 -7
  42. package/vg/apps/ng-app/src/app/app.scss +0 -0
  43. package/vg/apps/ng-app/src/app/app.ts +0 -12
  44. package/vg/apps/ng-app/src/app/dashboard/dashboard.component.html +0 -87
  45. package/vg/apps/ng-app/src/app/dashboard/dashboard.component.scss +0 -290
  46. package/vg/apps/ng-app/src/app/dashboard/dashboard.component.ts +0 -236
  47. package/vg/apps/ng-app/src/app/nx-welcome.ts +0 -872
  48. package/vg/apps/ng-app/src/app/services/api.service.ts +0 -28
  49. package/vg/apps/ng-app/src/index.html +0 -13
  50. package/vg/apps/ng-app/src/main.ts +0 -5
  51. package/vg/apps/ng-app/src/styles.scss +0 -1
  52. package/vg/apps/ng-app/src/test-setup.ts +0 -6
  53. package/vg/bin/vg-workspace.js +0 -43
  54. package/vg/jest.config.ts +0 -6
  55. package/vg/nx.json +0 -85
  56. package/vg/package-lock.json +0 -30707
  57. package/vg/package-workspace.json +0 -38
  58. package/vg/package.json +0 -38
  59. package/vg/packages/client/data-access/README.md +0 -7
  60. package/vg/packages/client/data-access/jest.config.ts +0 -21
  61. package/vg/packages/client/data-access/project.json +0 -21
  62. package/vg/packages/client/data-access/src/index.ts +0 -1
  63. package/vg/packages/client/data-access/src/lib/data-access/data-access.html +0 -1
  64. package/vg/packages/client/data-access/src/lib/data-access/data-access.scss +0 -0
  65. package/vg/packages/client/data-access/src/lib/data-access/data-access.ts +0 -9
  66. package/vg/packages/client/data-access/src/test-setup.ts +0 -6
  67. package/vg/packages/core/README.md +0 -11
  68. package/vg/packages/core/jest.config.ts +0 -10
  69. package/vg/packages/core/package.json +0 -11
  70. package/vg/packages/core/project.json +0 -26
  71. package/vg/packages/core/src/index.ts +0 -6
  72. package/vg/packages/core/src/lib/core.ts +0 -3
  73. package/vg/packages/core/src/lib/detectors/project-detector.ts +0 -343
  74. package/vg/packages/core/src/lib/ignore/ignore-manager.ts +0 -315
  75. package/vg/packages/core/src/lib/scanner/file-scanner.ts +0 -675
  76. package/vg/packages/core/src/lib/tokenizer/token-manager.ts +0 -435
  77. package/vg/packages/core/src/lib/utils/bash-executor.ts +0 -146
  78. package/vg/packages/shared/data-types/README.md +0 -11
  79. package/vg/packages/shared/data-types/jest.config.ts +0 -10
  80. package/vg/packages/shared/data-types/package.json +0 -11
  81. package/vg/packages/shared/data-types/project.json +0 -26
  82. package/vg/packages/shared/data-types/src/index.ts +0 -1
  83. package/vg/packages/shared/data-types/src/lib/data-types.ts +0 -3
  84. package/vg/start-dev.sh +0 -22
@@ -1,675 +0,0 @@
1
- import * as fs from 'fs-extra';
2
- import * as path from 'path';
3
- import { IgnoreManager } from '../ignore/ignore-manager';
4
-
5
- export interface ScannerOptions {
6
- maxFileSize?: number;
7
- extensions?: string[];
8
- includeHidden?: boolean;
9
- maxDepth?: number;
10
- [key: string]: any;
11
- }
12
-
13
- export interface FileNode {
14
- path: string;
15
- name: string;
16
- size: number;
17
- type: 'directory' | 'file';
18
- extension?: string;
19
- children?: FileNode[];
20
- }
21
-
22
- export interface FileContent {
23
- path: string;
24
- relativePath: string;
25
- name: string;
26
- extension?: string;
27
- size: number;
28
- content: string;
29
- lines: number;
30
- encoding: string;
31
- }
32
-
33
- export interface ScanResult {
34
- tree: FileNode;
35
- files: FileContent[];
36
- stats: {
37
- totalFiles: number;
38
- filteredFiles: number;
39
- processedFiles: number;
40
- scanTime: number;
41
- projectPath: string;
42
- };
43
- }
44
-
45
- /**
46
- * Scanner to traverse and concatenate source code files
47
- */
48
- export class FileScanner {
49
- private options: ScannerOptions;
50
- private ignoreManager: IgnoreManager;
51
-
52
- constructor(private projectPath: string, options: ScannerOptions = {}) {
53
- this.options = {
54
- maxFileSize: options.maxFileSize || 1024 * 1024, // 1MB default
55
- extensions: options.extensions || this.getDefaultExtensions(),
56
- includeHidden: options.includeHidden || false,
57
- maxDepth: options.maxDepth || 10,
58
- ...options
59
- };
60
- this.ignoreManager = new IgnoreManager(projectPath);
61
- }
62
-
63
- /**
64
- * Get list of default extensions
65
- */
66
- getDefaultExtensions(): string[] {
67
- return [
68
- // Web Frontend
69
- '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte',
70
- '.html', '.htm', '.css', '.scss', '.sass', '.less',
71
-
72
- // Backend
73
- '.java', '.kt', '.scala', '.groovy',
74
- '.py', '.rb', '.php', '.go', '.rs',
75
- '.cs', '.vb', '.fs',
76
- '.cpp', '.c', '.h', '.hpp',
77
-
78
- // Config files
79
- '.json', '.yaml', '.yml', '.toml', '.ini', '.conf',
80
- '.xml', '.properties',
81
-
82
- // Build files
83
- '.gradle', '.maven', '.sbt',
84
-
85
- // Scripts
86
- '.sh', '.bat', '.ps1', '.cmd',
87
-
88
- // Documentation
89
- '.md', '.txt', '.rst',
90
-
91
- // SQL
92
- '.sql', '.ddl', '.dml'
93
- ];
94
- }
95
-
96
- /**
97
- * Scan entire project and return file structure
98
- */
99
- async scanProject(): Promise<ScanResult> {
100
- const startTime = Date.now();
101
-
102
- try {
103
- // Get directory structure
104
- const tree = await this.buildFileTree();
105
-
106
- // Extract files
107
- const files = await this.extractFiles(tree);
108
-
109
- // Filter files by ignore rules
110
- const filteredFiles = await this.filterFiles(files);
111
-
112
- // Read file contents
113
- const filesWithContent = await this.readFilesContent(filteredFiles);
114
-
115
- const endTime = Date.now();
116
-
117
- return {
118
- tree,
119
- files: filesWithContent,
120
- stats: {
121
- totalFiles: files.length,
122
- filteredFiles: filteredFiles.length,
123
- processedFiles: filesWithContent.length,
124
- scanTime: endTime - startTime,
125
- projectPath: this.projectPath
126
- }
127
- };
128
- } catch (error: any) {
129
- throw new Error(`Failed to scan project: ${error.message}`);
130
- }
131
- }
132
-
133
- /**
134
- * Build file tree
135
- */
136
- async buildFileTree(): Promise<FileNode> {
137
- // Use manual scanning instead of directory-tree to ensure all Java files are found
138
- return await this.scanDirectoryManually(this.projectPath);
139
- }
140
-
141
- /**
142
- * Manually scan directory to ensure all files are found
143
- */
144
- async scanDirectoryManually(dirPath: string, relativePath = ''): Promise<FileNode> {
145
- const stats = await fs.stat(dirPath);
146
- const name = path.basename(dirPath);
147
-
148
- const node: FileNode = {
149
- path: dirPath,
150
- name: name,
151
- size: stats.size,
152
- type: stats.isDirectory() ? 'directory' : 'file',
153
- extension: stats.isFile() ? path.extname(name) : undefined
154
- };
155
-
156
- if (stats.isDirectory()) {
157
- try {
158
- const entries = await fs.readdir(dirPath);
159
- const children: FileNode[] = [];
160
-
161
- for (const entry of entries) {
162
- const entryPath = path.join(dirPath, entry);
163
- const entryRelativePath = path.join(relativePath, entry);
164
-
165
- // Skip ignored directories
166
- if (this.shouldIgnoreDirectoryName(entry)) {
167
- continue;
168
- }
169
-
170
- try {
171
- const childNode = await this.scanDirectoryManually(entryPath, entryRelativePath);
172
- if (childNode) {
173
- children.push(childNode);
174
- }
175
- } catch (error) {
176
- // Skip files/directories that can't be accessed
177
- continue;
178
- }
179
- }
180
-
181
- if (children.length > 0) {
182
- node.children = children;
183
- }
184
- } catch (error) {
185
- // Skip directories that can't be read
186
- }
187
- }
188
-
189
- return node;
190
- }
191
-
192
- /**
193
- * Get regex for build directories and hidden files to ignore
194
- */
195
- getBuildIgnoreRegex(): RegExp {
196
- // Create regex pattern for directories to ignore
197
- const ignorePatterns = [
198
- // Hidden directories (starting with .)
199
- '^\\..*',
200
-
201
- // Build directories
202
- '^build$',
203
- '^target$',
204
- '^dist$',
205
- '^out$',
206
- '^bin$',
207
-
208
- // Dependencies
209
- '^node_modules$',
210
- '^vendor$',
211
-
212
- // Temporary files
213
- '^tmp$',
214
- '^temp$',
215
-
216
- // Logs
217
- '^logs$',
218
- '^log$',
219
-
220
- // Coverage reports
221
- '^coverage$'
222
- ];
223
-
224
- // Combine all patterns into one regex
225
- const combinedPattern = ignorePatterns.join('|');
226
- return new RegExp(`(${combinedPattern})`);
227
- }
228
-
229
- /**
230
- * Extract list of files from tree
231
- */
232
- async extractFiles(tree: FileNode): Promise<any[]> {
233
- const files: any[] = [];
234
-
235
- const traverse = (node: FileNode, currentPath = '') => {
236
- // Skip ignored directories
237
- if (this.shouldIgnorePath(node.path || node.name)) {
238
- return;
239
- }
240
-
241
- // If node has children, it's a directory
242
- if (node.children) {
243
- node.children.forEach(child => traverse(child, currentPath));
244
- } else {
245
- // If no children, it's a file
246
- const relativePath = path.relative(this.projectPath, node.path);
247
-
248
- // Skip files in ignored directories
249
- if (!this.shouldIgnorePath(relativePath)) {
250
- // Filter by extension
251
- const extensions = this.options.extensions || this.getDefaultExtensions();
252
- const hasValidExtension = extensions.some(ext =>
253
- node.name.toLowerCase().endsWith(ext.toLowerCase())
254
- );
255
-
256
- if (hasValidExtension) {
257
- files.push({
258
- path: node.path,
259
- relativePath: relativePath,
260
- name: node.name,
261
- extension: node.extension,
262
- size: node.size
263
- });
264
- }
265
- }
266
- }
267
- };
268
-
269
- traverse(tree);
270
- return files;
271
- }
272
-
273
- /**
274
- * Check if path should be ignored
275
- */
276
- shouldIgnorePath(filePath: string): boolean {
277
- const pathParts = filePath.split(path.sep);
278
-
279
- // Check each part of the path
280
- for (const part of pathParts) {
281
- if (this.shouldIgnoreDirectoryName(part)) {
282
- return true;
283
- }
284
- }
285
-
286
- return false;
287
- }
288
-
289
- /**
290
- * Check if directory/file should be ignored
291
- */
292
- shouldIgnoreDirectoryName(name: string): boolean {
293
- // List of directories/files to ignore
294
- const ignoreList = [
295
- // Hidden directories (starting with .)
296
- '.git', '.svn', '.hg',
297
- '.gradle', '.maven', '.cache', '.npm',
298
- '.idea', '.vscode', '.eclipse',
299
- '.DS_Store', '.tmp',
300
-
301
- // Build directories
302
- 'build', 'target', 'dist', 'out', 'bin',
303
-
304
- // Dependencies
305
- 'node_modules', 'vendor',
306
-
307
- // Temporary files
308
- 'tmp', 'temp',
309
-
310
- // Logs
311
- 'logs', 'log',
312
-
313
- // Coverage reports
314
- 'coverage', '.nyc_output'
315
- ];
316
-
317
- // Check if name starts with . (hidden files/dirs)
318
- if (name.startsWith('.')) {
319
- return true;
320
- }
321
-
322
- // Check if name is in ignore list
323
- return ignoreList.includes(name);
324
- }
325
-
326
- /**
327
- * Filter files by ignore rules
328
- */
329
- async filterFiles(files: any[]): Promise<any[]> {
330
- const filtered: any[] = [];
331
-
332
- for (const file of files) {
333
- // Check size
334
- if (this.options.maxFileSize && file.size > this.options.maxFileSize) {
335
- continue;
336
- }
337
-
338
- // Check ignore rules
339
- const shouldIgnore = await this.ignoreManager.shouldIgnore(file.relativePath);
340
- if (!shouldIgnore) {
341
- filtered.push(file);
342
- }
343
- }
344
-
345
- return filtered;
346
- }
347
-
348
- /**
349
- * Read file contents
350
- */
351
- async readFilesContent(files: any[]): Promise<FileContent[]> {
352
- const filesWithContent: FileContent[] = [];
353
-
354
- for (const file of files) {
355
- try {
356
- const content = await this.readFileContent(file.path);
357
- if (content !== null) {
358
- filesWithContent.push({
359
- ...file,
360
- content,
361
- lines: content.split('\n').length,
362
- encoding: 'utf8'
363
- });
364
- }
365
- } catch (error: any) {
366
- // Log error but continue with other files
367
- console.warn(`Warning: Could not read file ${file.relativePath}: ${error.message}`);
368
- }
369
- }
370
-
371
- return filesWithContent;
372
- }
373
-
374
- /**
375
- * Read content of a single file
376
- */
377
- async readFileContent(filePath: string): Promise<string | null> {
378
- try {
379
- // Check if file is binary
380
- if (await this.isBinaryFile(filePath)) {
381
- return null;
382
- }
383
-
384
- const content = await fs.readFile(filePath, 'utf8');
385
- return content;
386
- } catch (error: any) {
387
- if (error.code === 'ENOENT') {
388
- return null;
389
- }
390
- throw error;
391
- }
392
- }
393
-
394
- /**
395
- * Check if file is binary
396
- */
397
- async isBinaryFile(filePath: string): Promise<boolean> {
398
- try {
399
- const buffer = await fs.readFile(filePath);
400
-
401
- // Check first 1024 bytes
402
- const chunk = buffer.slice(0, 1024);
403
-
404
- // If null bytes found, likely binary
405
- for (let i = 0; i < chunk.length; i++) {
406
- if (chunk[i] === 0) {
407
- return true;
408
- }
409
- }
410
-
411
- return false;
412
- } catch (error) {
413
- return true; // Assume binary if can't read
414
- }
415
- }
416
-
417
- /**
418
- * Create combined content from all files
419
- */
420
- async createCombinedContent(files: FileContent[], options: any = {}): Promise<string> {
421
- const {
422
- includeStats = true,
423
- includeTree = true,
424
- headerTemplate = this.getDefaultHeaderTemplate(),
425
- separatorTemplate = this.getDefaultSeparatorTemplate()
426
- } = options;
427
-
428
- let content = '';
429
-
430
- // Header with project info
431
- if (includeStats) {
432
- content += this.generateProjectHeader(files);
433
- content += '\n\n';
434
- }
435
-
436
- // Directory structure
437
- if (includeTree) {
438
- content += this.generateTreeStructure(files);
439
- content += '\n\n';
440
- }
441
-
442
- // File contents
443
- for (let i = 0; i < files.length; i++) {
444
- const file = files[i];
445
-
446
- // File header
447
- content += headerTemplate
448
- .replace('{path}', file.relativePath)
449
- .replace('{name}', file.name)
450
- .replace('{extension}', file.extension || '')
451
- .replace('{size}', file.size)
452
- .replace('{lines}', file.lines);
453
-
454
- content += '\n';
455
- content += file.content;
456
- content += '\n';
457
-
458
- // Separator between files
459
- if (i < files.length - 1) {
460
- content += separatorTemplate;
461
- content += '\n';
462
- }
463
- }
464
-
465
- return content;
466
- }
467
-
468
- /**
469
- * Default file header template
470
- */
471
- getDefaultHeaderTemplate(): string {
472
- return `
473
- ================================================================================
474
- File: {path}
475
- Size: {size} bytes | Lines: {lines}
476
- ================================================================================`;
477
- }
478
-
479
- /**
480
- * Default separator template
481
- */
482
- getDefaultSeparatorTemplate(): string {
483
- return '\n\n';
484
- }
485
-
486
- /**
487
- * Create combined content for AI tools with correct formatting
488
- */
489
- async createCombinedContentForAI(files: FileContent[], options: any = {}): Promise<string> {
490
- const {
491
- includeStats = false,
492
- includeTree = false,
493
- preserveLineNumbers = true
494
- } = options;
495
-
496
- let content = '';
497
-
498
- // Header with project info (optional)
499
- if (includeStats) {
500
- content += this.generateProjectHeader(files);
501
- content += '\n\n';
502
- }
503
-
504
- // Directory structure (optional)
505
- if (includeTree) {
506
- content += this.generateTreeStructure(files);
507
- content += '\n\n';
508
- }
509
-
510
- // File contents with correct formatting
511
- for (let i = 0; i < files.length; i++) {
512
- const file = files[i];
513
-
514
- // File boundary marker - does not affect line numbering
515
- content += `// ===== FILE: ${file.relativePath} =====\n`;
516
-
517
- // Original file content
518
- content += file.content;
519
-
520
- // Ensure file ends with newline
521
- if (!file.content.endsWith('\n')) {
522
- content += '\n';
523
- }
524
-
525
- // Separator between files
526
- if (i < files.length - 1) {
527
- content += '\n';
528
- }
529
- }
530
-
531
- return content;
532
- }
533
-
534
- /**
535
- * Generate project info header
536
- */
537
- generateProjectHeader(files: FileContent[]): string {
538
- const totalSize = files.reduce((sum, file) => sum + file.size, 0);
539
- const totalLines = files.reduce((sum, file) => sum + file.lines, 0);
540
-
541
- return `
542
- # Project Analysis Report
543
- Generated: ${new Date().toISOString()}
544
- Project Path: ${this.projectPath}
545
-
546
- ## Statistics
547
- - Total Files: ${files.length}
548
- - Total Size: ${this.formatBytes(totalSize)}
549
- - Total Lines: ${totalLines.toLocaleString()}
550
- - Extensions: ${[...new Set(files.map(f => f.extension))].filter(Boolean).join(', ')}
551
- `;
552
- }
553
-
554
- /**
555
- * Generate tree structure
556
- */
557
- generateTreeStructure(files: FileContent[]): string {
558
- const tree: any = {};
559
-
560
- // Build tree structure
561
- files.forEach(file => {
562
- const parts = file.relativePath.split(path.sep);
563
- let current = tree;
564
-
565
- parts.forEach((part, index) => {
566
- if (index === parts.length - 1) {
567
- // File
568
- current[part] = file;
569
- } else {
570
- // Directory
571
- if (!current[part]) {
572
- current[part] = {};
573
- }
574
- current = current[part];
575
- }
576
- });
577
- });
578
-
579
- // Generate tree string
580
- let treeStr = '\n## Project Structure\n```\n';
581
- treeStr += this.renderTree(tree, '', true);
582
- treeStr += '```\n';
583
-
584
- return treeStr;
585
- }
586
-
587
- /**
588
- * Render tree structure
589
- */
590
- renderTree(node: any, prefix = '', isLast = true): string {
591
- let result = '';
592
- const entries = Object.entries(node);
593
-
594
- entries.forEach(([name, value]: [string, any], index) => {
595
- const isLastEntry = index === entries.length - 1;
596
- const connector = isLastEntry ? '└── ' : '├── ';
597
-
598
- result += prefix + connector + name + '\n';
599
-
600
- if (typeof value === 'object' && !value.relativePath) {
601
- // Directory
602
- const newPrefix = prefix + (isLastEntry ? ' ' : '│ ');
603
- result += this.renderTree(value, newPrefix, isLastEntry);
604
- }
605
- });
606
-
607
- return result;
608
- }
609
-
610
- /**
611
- * Render project tree structure
612
- */
613
- renderProjectTree(tree: FileNode, maxDepth = 50): string {
614
- if (!tree) return 'No tree structure available';
615
-
616
- const renderNode = (node: FileNode, prefix = '', depth = 0): string => {
617
- if (depth > maxDepth) return '';
618
-
619
- let result = '';
620
-
621
- if (node.children && node.children.length > 0) {
622
- // Filter out ignored directories
623
- const filteredChildren = node.children.filter(child =>
624
- !this.shouldIgnoreDirectoryName(child.name)
625
- );
626
-
627
- // Sort children: directories first, then files
628
- filteredChildren.sort((a, b) => {
629
- const aIsDir = a.children && a.children.length > 0;
630
- const bIsDir = b.children && b.children.length > 0;
631
- if (aIsDir && !bIsDir) return -1;
632
- if (!aIsDir && bIsDir) return 1;
633
- return a.name.localeCompare(b.name);
634
- });
635
-
636
- filteredChildren.forEach((child, index) => {
637
- const isLast = index === filteredChildren.length - 1;
638
- const connector = isLast ? '└── ' : '├── ';
639
- const nextPrefix = prefix + (isLast ? ' ' : '│ ');
640
- const isDirectory = child.children !== undefined;
641
-
642
- result += `${prefix}${connector}${child.name}`;
643
- if (!isDirectory) {
644
- // It's a file, show extension
645
- result += ` (${child.extension || 'file'})`;
646
- }
647
- result += '\n';
648
-
649
- if (isDirectory) {
650
- // Always render all children, no depth limit for full structure
651
- result += renderNode(child, nextPrefix, depth + 1);
652
- }
653
- });
654
- }
655
-
656
- return result;
657
- };
658
-
659
- let result = `${tree.name}/\n`;
660
- result += renderNode(tree);
661
-
662
- return result;
663
- }
664
-
665
- /**
666
- * Format bytes to human readable
667
- */
668
- formatBytes(bytes: number): string {
669
- if (bytes === 0) return '0 Bytes';
670
- const k = 1024;
671
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
672
- const i = Math.floor(Math.log(bytes) / Math.log(k));
673
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
674
- }
675
- }