@archznn/xavva 2.2.0 β†’ 2.2.1

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 CHANGED
@@ -1 +1,3 @@
1
- # XAVVA CLI πŸš€> Ultra-fast development toolkit for Java Enterprise (Tomcat) on Windows[![Version](https://img.shields.io/badge/version-2.2.0-blue.svg)](https://github.com/leorsousa05/Xavva)[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)Xavva is a high-performance CLI built with **Bun** that transforms the Java/Tomcat development experience. It brings modern development workflows (like Node.js/Vite) to the Java Enterprise ecosystem with hot-reload, smart logging, and automated deployment.---## ✨ Features- ⚑ **Hot Reload** β€” Incremental compilation and class injection without server restart- πŸ“Š **Interactive Dashboard** β€” Real-time TUI with system metrics and shortcuts- 🧠 **Smart Log Analyzer** β€” Stack trace folding and root cause highlighting- πŸ”’ **Security Audit** β€” Automated vulnerability scanning via OSV.dev- πŸ“¦ **Dependency Analysis** β€” Detect conflicts and outdated dependencies- 🎯 **Maven & Gradle** β€” Native support for both build tools- πŸ”§ **Auto-Healing** β€” Automatic diagnosis and repair of common issues---## πŸ“¦ Installation```powershell# Via NPMnpm install -g @archznn/xavva# Or run directly with Bunbunx @archznn/xavva dev```---## πŸš€ Quick Start```bash# Start development mode with dashboardxavva dev --tui# Deploy to Tomcatxavva deploy# Analyze dependencies for issuesxavva deps# Check for security vulnerabilitiesxavva audit```---## πŸ“– Commands### Core Development| Command | Description ||---------|-------------|| `xavva dev` | Full development mode (build + deploy + watch + debug) || `xavva deploy` | Build and deploy application to Tomcat || `xavva build` | Compile project only || `xavva start` | Start Tomcat server only |### Code Execution| Command | Description ||---------|-------------|| `xavva run <class>` | Execute a Java class with automatic classpath || `xavva debug <class>` | Debug a Java class (port 5005) |### Analysis & Monitoring| Command | Description ||---------|-------------|| `xavva logs` | Stream and analyze Tomcat logs in real-time || `xavva deps` | **Analyze dependencies** β€” detect conflicts, find updates || `xavva audit` | Security audit of JAR files via OSV.dev || `xavva doctor` | Diagnose environment issues (JAVA_HOME, DCEVM) || `xavva profiles` | List available Maven/Gradle profiles || `xavva docs` | Generate endpoint documentation |---## πŸ” Dependency AnalysisThe `xavva deps` command provides comprehensive dependency analysis:```bash# Basic analysisxavva deps# With verbose output for debuggingxavva deps --verbose# Show fix suggestions for conflictsxavva deps --fix# Export report as JSONxavva deps --output report.json# Fail on critical conflicts (useful in CI/CD)xavva deps --strict```### What it detects:- ⚠️ **Version Conflicts** β€” Same dependency with different versions- ⬆️ **Available Updates** β€” Newer versions in Maven Central- πŸ”΄ **Major Updates** β€” Breaking changes that need attention- πŸ“Š **Statistics** β€” Direct vs transitive dependencies### Sample output:```β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•πŸ“Š DEPENDENCY ANALYSIS══════════════════════════════════════════════════════════Statistics: Total: 183 dependencies Direct: 45 | Transitive: 138⚠️ VERSION CONFLICTS (2) βœ– com.fasterxml.jackson.core:jackson-databind Versions: 2.13.0, 2.12.6⬆️ UPDATES AVAILABLE (5) ↑ org.postgresql:postgresql 42.2.5 β†’ 42.7.1⚠️ MAJOR UPDATES (1) ! org.springframework.boot:spring-boot-starter 2.5.0 β†’ 3.1.0```---## βš™οΈ ConfigurationCreate `xavva.json` in your project root:```json{ "project": { "appName": "my-application", "buildTool": "maven", "profile": "dev", "tui": false }, "tomcat": { "path": "C:/apache-tomcat", "port": 8080 }}```### CLI Options| Option | Description ||--------|-------------|| `-p, --path <path>` | Tomcat installation path || `-t, --tool <tool>` | Build tool: `maven` or `gradle` || `-n, --name <name>` | Application name (WAR context) || `--port <port>` | Tomcat port (default: 8080) || `-P, --profile <prof>` | Maven/Gradle profile || `-e, --encoding <enc>` | Source encoding (utf8, cp1252) || `-w, --watch` | Enable file watching || `--tui` | Interactive dashboard mode || `-d, --debug` | Enable JPDA debugger || `-c, --clean` | Clean logs before start || `-s, --no-build` | Skip initial build || `-V, --verbose` | Detailed output |---## πŸ—οΈ ArchitectureXavva uses a modular service-oriented architecture:- **DashboardService** β€” TUI management and interactivity- **LogAnalyzer** β€” Intelligent log processing- **DependencyAnalyzerService** β€” Dependency conflict detection- **ProjectService** β€” Project structure discovery- **BuildService** β€” Maven/Gradle integration- **TomcatService** β€” Server lifecycle management---## 🀝 ContributingContributions are welcome! Please feel free to submit a Pull Request.---## πŸ“„ LicenseMIT License β€” see [LICENSE](LICENSE) for details.---<p align="center"> <sub>Built with ❀️ for Java developers who miss modern tooling</sub></p>
1
+ # XAVVA CLI πŸš€> Ultra-fast development toolkit for Java Enterprise (Tomcat) on Windows[![Version](https://img.shields.io/badge/version-2.2.1-blue.svg)](https://github.com/leorsousa05/Xavva)[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)Xavva is a high-performance CLI built with **Bun** that transforms the Java/Tomcat development experience. It brings modern development workflows (like Node.js/Vite) to the Java Enterprise ecosystem with hot-reload, smart logging, and automated deployment.---## ✨ Features- ⚑ **Hot Reload** β€” Incremental compilation and class injection without server restart- πŸ“Š **Interactive Dashboard** β€” Real-time TUI with system metrics and shortcuts- 🧠 **Smart Log Analyzer** β€” Stack trace folding and root cause highlighting- πŸ”’ **Security Audit** β€” Automated vulnerability scanning via OSV.dev- πŸ“¦ **Dependency Analysis** β€” Detect conflicts and outdated dependencies- 🎯 **Maven & Gradle** β€” Native support for both build tools- πŸ”§ **Auto-Healing** β€” Automatic diagnosis and repair of common issues---## πŸ“¦ Installation```powershell# Via NPMnpm install -g @archznn/xavva# Or run directly with Bunbunx @archznn/xavva dev```---## πŸš€ Quick Start```bash# Start development mode with dashboardxavva dev --tui# Deploy to Tomcatxavva deploy# Analyze dependencies for issuesxavva deps
2
+ # Update safe dependencies (non-breaking)
3
+ xavva deps --update-safe# Check for security vulnerabilitiesxavva audit```---## πŸ“– Commands### Core Development| Command | Description ||---------|-------------|| `xavva dev` | Full development mode (build + deploy + watch + debug) || `xavva deploy` | Build and deploy application to Tomcat || `xavva build` | Compile project only || `xavva start` | Start Tomcat server only |### Code Execution| Command | Description ||---------|-------------|| `xavva run <class>` | Execute a Java class with automatic classpath || `xavva debug <class>` | Debug a Java class (port 5005) |### Analysis & Monitoring| Command | Description ||---------|-------------|| `xavva logs` | Stream and analyze Tomcat logs in real-time || `xavva deps` | **Analyze dependencies** β€” detect conflicts, find updates || `xavva audit` | Security audit of JAR files via OSV.dev || `xavva doctor` | Diagnose environment issues (JAVA_HOME, DCEVM) || `xavva profiles` | List available Maven/Gradle profiles || `xavva docs` | Generate endpoint documentation |---## πŸ” Dependency AnalysisThe `xavva deps` command provides comprehensive dependency analysis:```bash# Basic analysisxavva deps# With verbose output for debuggingxavva deps --verbose# Show fix suggestions for conflictsxavva deps --fix# Export report as JSONxavva deps --output report.json# Fail on critical conflicts (useful in CI/CD)xavva deps --strict```### What it detects:- ⚠️ **Version Conflicts** β€” Same dependency with different versions- ⬆️ **Available Updates** β€” Newer versions in Maven Central- πŸ”΄ **Major Updates** β€” Breaking changes that need attention- πŸ“Š **Statistics** β€” Direct vs transitive dependencies### Sample output:```β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•πŸ“Š DEPENDENCY ANALYSIS══════════════════════════════════════════════════════════Statistics: Total: 183 dependencies Direct: 45 | Transitive: 138⚠️ VERSION CONFLICTS (2) βœ– com.fasterxml.jackson.core:jackson-databind Versions: 2.13.0, 2.12.6⬆️ UPDATES AVAILABLE (5) ↑ org.postgresql:postgresql 42.2.5 β†’ 42.7.1⚠️ MAJOR UPDATES (1) ! org.springframework.boot:spring-boot-starter 2.5.0 β†’ 3.1.0```---## βš™οΈ ConfigurationCreate `xavva.json` in your project root:```json{ "project": { "appName": "my-application", "buildTool": "maven", "profile": "dev", "tui": false }, "tomcat": { "path": "C:/apache-tomcat", "port": 8080 }}```### CLI Options| Option | Description ||--------|-------------|| `-p, --path <path>` | Tomcat installation path || `-t, --tool <tool>` | Build tool: `maven` or `gradle` || `-n, --name <name>` | Application name (WAR context) || `--port <port>` | Tomcat port (default: 8080) || `-P, --profile <prof>` | Maven/Gradle profile || `-e, --encoding <enc>` | Source encoding (utf8, cp1252) || `-w, --watch` | Enable file watching || `--tui` | Interactive dashboard mode || `-d, --debug` | Enable JPDA debugger || `-c, --clean` | Clean logs before start || `-s, --no-build` | Skip initial build || `-V, --verbose` | Detailed output |---## πŸ—οΈ ArchitectureXavva uses a modular service-oriented architecture:- **DashboardService** β€” TUI management and interactivity- **LogAnalyzer** β€” Intelligent log processing- **DependencyAnalyzerService** β€” Dependency conflict detection- **ProjectService** β€” Project structure discovery- **BuildService** β€” Maven/Gradle integration- **TomcatService** β€” Server lifecycle management---## 🀝 ContributingContributions are welcome! Please feel free to submit a Pull Request.---## πŸ“„ LicenseMIT License β€” see [LICENSE](LICENSE) for details.---<p align="center"> <sub>Built with ❀️ for Java developers who miss modern tooling</sub></p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archznn/xavva",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Ultra-fast CLI tool for Java/Tomcat development on Windows with Hot-Reload and Zero Config.",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -50,6 +50,10 @@ export class DepsCommand implements Command {
50
50
  console.log(report);
51
51
 
52
52
  // AΓ§Γ΅es adicionais baseadas em flags
53
+ if (args?.["update-safe"] || args?.["updateSafe"]) {
54
+ await this.performUpdateSafe(analyzer, result, config.project.buildTool);
55
+ }
56
+
53
57
  if (args?.["fix"]) {
54
58
  await this.suggestFixes(result, config.project.buildTool);
55
59
  }
@@ -103,6 +107,62 @@ export class DepsCommand implements Command {
103
107
  }
104
108
  }
105
109
 
110
+ private async performUpdateSafe(
111
+ analyzer: DependencyAnalyzerService,
112
+ result: DependencyAnalysisResult,
113
+ buildTool: string
114
+ ): Promise<void> {
115
+ const safeUpdates = result.updates.filter(u => !u.isMajor);
116
+
117
+ if (safeUpdates.length === 0) {
118
+ Logger.info("", "Nenhuma atualizaΓ§Γ£o segura disponΓ­vel");
119
+ return;
120
+ }
121
+
122
+ Logger.newline();
123
+ Logger.section("Atualizando DependΓͺncias (Safe Mode)");
124
+ Logger.info("AtualizaΓ§Γ΅es a aplicar", String(safeUpdates.length));
125
+
126
+ const spinner = Logger.spinner("Atualizando arquivos de configuraΓ§Γ£o...");
127
+
128
+ try {
129
+ const updateResult = await analyzer.updateSafe(safeUpdates);
130
+ spinner();
131
+
132
+ if (updateResult.updated > 0) {
133
+ Logger.success(`${updateResult.updated} dependΓͺncias atualizadas`);
134
+ Logger.info("", `Backup criado: ${buildTool === "maven" ? "pom.xml.backup" : "build.gradle.backup"}`);
135
+
136
+ // Listar o que foi atualizado
137
+ for (const update of safeUpdates.slice(0, 5)) {
138
+ Logger.log(` ${Logger.C.success}↑${Logger.C.reset} ${update.groupId}:${update.artifactId} ${update.currentVersion} β†’ ${Logger.C.success}${update.latestVersion}${Logger.C.reset}`);
139
+ }
140
+ if (safeUpdates.length > 5) {
141
+ Logger.log(` ${Logger.C.dim}... e mais ${safeUpdates.length - 5}${Logger.C.reset}`);
142
+ }
143
+
144
+ Logger.newline();
145
+ Logger.log(`${Logger.C.warning}⚠️ Execute 'mvn compile' ou 'gradle build' para aplicar as mudanças${Logger.C.reset}`);
146
+ } else {
147
+ Logger.warn("Nenhuma dependΓͺncia foi atualizada");
148
+ }
149
+
150
+ if (updateResult.skipped > 0) {
151
+ Logger.info("DependΓͺncias ignoradas", `${updateResult.skipped} (definidas via propriedades)`);
152
+ }
153
+
154
+ if (updateResult.errors.length > 0) {
155
+ for (const error of updateResult.errors) {
156
+ Logger.warn(error);
157
+ }
158
+ }
159
+ } catch (error) {
160
+ spinner(false);
161
+ const message = error instanceof Error ? error.message : String(error);
162
+ Logger.error(`Falha na atualizaΓ§Γ£o: ${message}`);
163
+ }
164
+ }
165
+
106
166
  private exportReport(
107
167
  result: DependencyAnalysisResult,
108
168
  outputPath: string
@@ -63,6 +63,9 @@ export class HelpCommand implements Command {
63
63
  ${this.c("dim", "# Analyze dependencies for conflicts")}
64
64
  xavva deps --verbose
65
65
 
66
+ ${this.c("dim", "# Update safe dependencies (non-breaking only)")}
67
+ xavva deps --update-safe
68
+
66
69
  ${this.c("dim", "# Security audit with auto-fix suggestions")}
67
70
  xavva audit --fix
68
71
 
@@ -162,6 +162,7 @@ export class BuildService {
162
162
  const decoder = new TextDecoder();
163
163
  let errorCount = 0;
164
164
  const maxErrors = 15;
165
+ const buildTool = this.projectConfig.buildTool as 'maven' | 'gradle';
165
166
 
166
167
  while (true) {
167
168
  const { done, value } = await reader.read();
@@ -174,20 +175,27 @@ export class BuildService {
174
175
  const cleanLine = line.trim();
175
176
  if (!cleanLine) continue;
176
177
 
177
- if (cleanLine.includes("[ERROR]")) {
178
+ if (cleanLine.includes("[ERROR]") || cleanLine.includes("error:")) {
178
179
  errorCount++;
179
180
  if (errorCount > maxErrors && !this.projectConfig.verbose) {
180
181
  if (errorCount === maxErrors + 1) {
181
- Logger.warn("... e mais erros ocultos. Use -V para ver todos.");
182
+ Logger.warn("... and more errors hidden. Use -V to see all.");
182
183
  }
183
184
  continue;
184
185
  }
185
186
  }
186
187
 
187
188
  if (!this.projectConfig.verbose) {
188
- // LΓ³gica de sumarizaΓ§Γ£o omitida para brevidade
189
+ // Modo nΓ£o-verbose: usa sumarizaΓ§Γ£o existente
190
+ const formatted = Logger.formatBuildLog(cleanLine, buildTool);
191
+ if (formatted) console.log(formatted);
189
192
  } else {
190
- process.stdout.write(line + "\n");
193
+ // Modo verbose: formata mas mantΓ©m estrutura
194
+ const formatted = Logger.formatBuildLog(cleanLine, buildTool);
195
+ if (formatted) {
196
+ console.log(formatted);
197
+ }
198
+ // Silencia linhas que sΓ£o noise puro
191
199
  }
192
200
  }
193
201
  }
@@ -471,12 +471,120 @@ export class DependencyAnalyzerService {
471
471
  return 0;
472
472
  }
473
473
 
474
+ async updateSafe(updates: DependencyUpdate[]): Promise<{ updated: number; skipped: number; errors: string[] }> {
475
+ const safeUpdates = updates.filter(u => !u.isMajor);
476
+ const result = { updated: 0, skipped: 0, errors: [] as string[] };
477
+
478
+ if (safeUpdates.length === 0) {
479
+ return result;
480
+ }
481
+
482
+ if (this.projectConfig.buildTool === "maven") {
483
+ return this.updateMavenSafe(safeUpdates);
484
+ } else {
485
+ return this.updateGradleSafe(safeUpdates);
486
+ }
487
+ }
488
+
489
+ private async updateMavenSafe(updates: DependencyUpdate[]): Promise<{ updated: number; skipped: number; errors: string[] }> {
490
+ const result = { updated: 0, skipped: 0, errors: [] as string[] };
491
+ const pomPath = path.join(process.cwd(), "pom.xml");
492
+
493
+ if (!fs.existsSync(pomPath)) {
494
+ result.errors.push("pom.xml nΓ£o encontrado");
495
+ return result;
496
+ }
497
+
498
+ let content = fs.readFileSync(pomPath, "utf-8");
499
+ let modified = false;
500
+
501
+ for (const update of updates) {
502
+ // Pattern para encontrar a versΓ£o especΓ­fica desta dependΓͺncia
503
+ const groupId = update.groupId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
504
+ const artifactId = update.artifactId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
505
+ const currentVersion = update.currentVersion.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
506
+
507
+ // Regex para encontrar <version> dentro do bloco da dependΓͺncia
508
+ const depPattern = new RegExp(
509
+ `(<dependency>\\s*<groupId>${groupId}</groupId>\\s*<artifactId>${artifactId}</artifactId>(?:\\s*<version>)${currentVersion}(</version>))`,
510
+ 'g'
511
+ );
512
+
513
+ if (depPattern.test(content)) {
514
+ content = content.replace(depPattern, `$1${update.latestVersion}$2`);
515
+ result.updated++;
516
+ modified = true;
517
+ } else {
518
+ // Pode ser definida via property
519
+ result.skipped++;
520
+ }
521
+ }
522
+
523
+ if (modified) {
524
+ // Backup do pom.xml
525
+ fs.writeFileSync(`${pomPath}.backup`, fs.readFileSync(pomPath));
526
+ fs.writeFileSync(pomPath, content);
527
+ }
528
+
529
+ return result;
530
+ }
531
+
532
+ private async updateGradleSafe(updates: DependencyUpdate[]): Promise<{ updated: number; skipped: number; errors: string[] }> {
533
+ const result = { updated: 0, skipped: 0, errors: [] as string[] };
534
+ const gradlePath = path.join(process.cwd(), "build.gradle");
535
+
536
+ if (!fs.existsSync(gradlePath)) {
537
+ result.errors.push("build.gradle nΓ£o encontrado");
538
+ return result;
539
+ }
540
+
541
+ let content = fs.readFileSync(gradlePath, "utf-8");
542
+ let modified = false;
543
+
544
+ for (const update of updates) {
545
+ const groupId = update.groupId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
546
+ const artifactId = update.artifactId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
547
+ const currentVersion = update.currentVersion.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
548
+
549
+ // Pattern: implementation("group:artifact:version") ou implementation 'group:artifact:version'
550
+ const patterns = [
551
+ new RegExp(`(implementation\\s*\\(\\s*["']${groupId}:${artifactId}:)${currentVersion}(["']\\s*\\))`, 'g'),
552
+ new RegExp(`(implementation\\s+["']${groupId}:${artifactId}:)${currentVersion}(["'])`, 'g'),
553
+ new RegExp(`(compile\\s*\\(\\s*["']${groupId}:${artifactId}:)${currentVersion}(["']\\s*\\))`, 'g'),
554
+ new RegExp(`(compile\\s+["']${groupId}:${artifactId}:)${currentVersion}(["'])`, 'g'),
555
+ ];
556
+
557
+ let updated = false;
558
+ for (const pattern of patterns) {
559
+ if (pattern.test(content)) {
560
+ content = content.replace(pattern, `$1${update.latestVersion}$2`);
561
+ updated = true;
562
+ break;
563
+ }
564
+ }
565
+
566
+ if (updated) {
567
+ result.updated++;
568
+ modified = true;
569
+ } else {
570
+ result.skipped++;
571
+ }
572
+ }
573
+
574
+ if (modified) {
575
+ fs.writeFileSync(`${gradlePath}.backup`, fs.readFileSync(gradlePath));
576
+ fs.writeFileSync(gradlePath, content);
577
+ }
578
+
579
+ return result;
580
+ }
581
+
474
582
  generateReport(result: DependencyAnalysisResult): string {
475
583
  const lines: string[] = [];
476
584
  lines.push("");
477
- lines.push(`${Logger.C.cyan}══════════════════════════════════════════════════════════${Logger.C.reset}`);
585
+ lines.push(`${Logger.C.primary}══════════════════════════════════════════════════════════${Logger.C.reset}`);
478
586
  lines.push(`${Logger.C.bold}πŸ“Š ANÁLISE DE DEPENDÊNCIAS${Logger.C.reset}`);
479
- lines.push(`${Logger.C.cyan}══════════════════════════════════════════════════════════${Logger.C.reset}`);
587
+ lines.push(`${Logger.C.primary}══════════════════════════════════════════════════════════${Logger.C.reset}`);
480
588
  lines.push("");
481
589
 
482
590
  // EstatΓ­sticas
@@ -487,10 +595,10 @@ export class DependencyAnalyzerService {
487
595
 
488
596
  // Conflitos
489
597
  if (result.conflicts.length > 0) {
490
- lines.push(`${Logger.C.yellow}⚠️ CONFLITOS DE VERSΓƒO (${result.conflicts.length})${Logger.C.reset}`);
598
+ lines.push(`${Logger.C.warning}⚠️ CONFLITOS DE VERSΓƒO (${result.conflicts.length})${Logger.C.reset}`);
491
599
  for (const conflict of result.conflicts) {
492
600
  const icon = conflict.severity === "error" ? "βœ–" : "β–²";
493
- const color = conflict.severity === "error" ? Logger.C.red : Logger.C.yellow;
601
+ const color = conflict.severity === "error" ? Logger.C.error : Logger.C.warning;
494
602
  lines.push(` ${color}${icon}${Logger.C.reset} ${conflict.groupId}:${conflict.artifactId}`);
495
603
  lines.push(` VersΓ΅es: ${conflict.versions.join(", ")}`);
496
604
  }
@@ -503,10 +611,10 @@ export class DependencyAnalyzerService {
503
611
  const minorUpdates = result.updates.filter(u => !u.isMajor);
504
612
 
505
613
  if (minorUpdates.length > 0) {
506
- lines.push(`${Logger.C.green}⬆️ ATUALIZAÇÕES DISPONÍVEIS (${minorUpdates.length})${Logger.C.reset}`);
614
+ lines.push(`${Logger.C.success}⬆️ ATUALIZAÇÕES DISPONÍVEIS (${minorUpdates.length})${Logger.C.reset}`);
507
615
  for (const update of minorUpdates.slice(0, 5)) {
508
- lines.push(` ${Logger.C.green}↑${Logger.C.reset} ${update.groupId}:${update.artifactId}`);
509
- lines.push(` ${update.currentVersion} β†’ ${Logger.C.green}${update.latestVersion}${Logger.C.reset}`);
616
+ lines.push(` ${Logger.C.success}↑${Logger.C.reset} ${update.groupId}:${update.artifactId}`);
617
+ lines.push(` ${update.currentVersion} β†’ ${Logger.C.success}${update.latestVersion}${Logger.C.reset}`);
510
618
  }
511
619
  if (minorUpdates.length > 5) {
512
620
  lines.push(` ${Logger.C.dim}... e mais ${minorUpdates.length - 5}${Logger.C.reset}`);
@@ -515,11 +623,11 @@ export class DependencyAnalyzerService {
515
623
  }
516
624
 
517
625
  if (majorUpdates.length > 0) {
518
- lines.push(`${Logger.C.yellow}⚠️ ATUALIZAÇÕES MAJOR (${majorUpdates.length})${Logger.C.reset}`);
626
+ lines.push(`${Logger.C.warning}⚠️ ATUALIZAÇÕES MAJOR (${majorUpdates.length})${Logger.C.reset}`);
519
627
  lines.push(` ${Logger.C.dim}Podem conter breaking changes${Logger.C.reset}`);
520
628
  for (const update of majorUpdates.slice(0, 3)) {
521
- lines.push(` ${Logger.C.yellow}!${Logger.C.reset} ${update.groupId}:${update.artifactId}`);
522
- lines.push(` ${update.currentVersion} β†’ ${Logger.C.yellow}${update.latestVersion}${Logger.C.reset}`);
629
+ lines.push(` ${Logger.C.warning}!${Logger.C.reset} ${update.groupId}:${update.artifactId}`);
630
+ lines.push(` ${update.currentVersion} β†’ ${Logger.C.warning}${update.latestVersion}${Logger.C.reset}`);
523
631
  }
524
632
  lines.push("");
525
633
  }
@@ -527,7 +635,7 @@ export class DependencyAnalyzerService {
527
635
 
528
636
  // Resumo
529
637
  if (result.conflicts.length === 0 && result.updates.length === 0) {
530
- lines.push(`${Logger.C.green}βœ” Todas as dependΓͺncias estΓ£o atualizadas!${Logger.C.reset}`);
638
+ lines.push(`${Logger.C.success}βœ” Todas as dependΓͺncias estΓ£o atualizadas!${Logger.C.reset}`);
531
639
  }
532
640
 
533
641
  lines.push("");
@@ -2,7 +2,7 @@ import type { TomcatConfig, AppConfig } from "../types/config";
2
2
  import { Logger } from "../utils/ui";
3
3
  import type { Subprocess } from "bun";
4
4
  import { ProjectService } from "./ProjectService";
5
- import { existsSync, mkdirSync, writeFileSync, promises as fs } from "fs";
5
+ import { existsSync, mkdirSync, writeFileSync, statSync, promises as fs } from "fs";
6
6
  import path from "path";
7
7
  import os from "os";
8
8
 
@@ -98,10 +98,10 @@ export class TomcatService {
98
98
  const agentDir = path.join(os.homedir(), ".xavva", "agents");
99
99
  const agentPath = path.join(agentDir, "hotswap-agent-2.0.3.jar");
100
100
 
101
- if (fs.existsSync(agentPath) && fs.statSync(agentPath).size > 1000) return agentPath;
101
+ if (existsSync(agentPath) && statSync(agentPath).size > 1000) return agentPath;
102
102
 
103
103
  try {
104
- if (!fs.existsSync(agentDir)) fs.mkdirSync(agentDir, { recursive: true });
104
+ if (!existsSync(agentDir)) mkdirSync(agentDir, { recursive: true });
105
105
 
106
106
  Logger.step("Downloading HotswapAgent v2.0.3 (Global)...");
107
107
  const url = "https://github.com/HotswapProjects/HotswapAgent/releases/download/RELEASE-2.0.3/hotswap-agent-2.0.3.jar";
@@ -109,7 +109,7 @@ export class TomcatService {
109
109
  if (!response.ok) throw new Error(`Status: ${response.status}`);
110
110
 
111
111
  const buffer = await response.arrayBuffer();
112
- fs.writeFileSync(agentPath, Buffer.from(buffer));
112
+ writeFileSync(agentPath, Buffer.from(buffer));
113
113
  Logger.success("HotswapAgent v2.0.3 installed globally!");
114
114
  return agentPath;
115
115
  } catch (e) {
@@ -237,18 +237,21 @@ export class TomcatService {
237
237
  private async processLogStream(stream: ReadableStream, clean: boolean, quiet: boolean, verbose: boolean, grep: string) {
238
238
  const reader = stream.getReader();
239
239
  const decoder = new TextDecoder();
240
+ let buffer = "";
240
241
 
241
242
  while (true) {
242
243
  const { done, value } = await reader.read();
243
244
  if (done) break;
244
245
 
245
- const chunk = decoder.decode(value);
246
- const lines = chunk.split(/[\r\n]+/);
246
+ buffer += decoder.decode(value, { stream: true });
247
+ const lines = buffer.split(/\r?\n/);
248
+ buffer = lines.pop() || ""; // Keep incomplete line in buffer
247
249
 
248
250
  for (const line of lines) {
249
251
  const cleanLine = line.trim();
250
252
  if (!cleanLine || cleanLine.startsWith("Listening for transport")) continue;
251
253
 
254
+ // Detect startup completion
252
255
  if (cleanLine.includes("Server startup in") || cleanLine.includes("SEVERE") || cleanLine.includes("Exception")) {
253
256
  const isSuccess = cleanLine.includes("Server startup in");
254
257
  if (this.stopStartupSpinner) {
@@ -260,26 +263,44 @@ export class TomcatService {
260
263
  }
261
264
  }
262
265
 
266
+ // Verbose: formata logs do Tomcat
263
267
  if (verbose) {
264
- Logger.log(cleanLine);
268
+ if (Logger.isTomcatNoise(cleanLine)) {
269
+ continue; // Silencia noise completamente
270
+ }
271
+ const formatted = Logger.formatTomcatLog(cleanLine);
272
+ if (formatted) {
273
+ console.log(formatted);
274
+ }
265
275
  continue;
266
276
  }
267
277
 
278
+ // Clean mode: filtra noise
268
279
  if (clean) {
280
+ // Sempre filtra noise do sistema
281
+ if (Logger.isSystemNoise(cleanLine)) continue;
282
+
283
+ // Quiet mode: sΓ³ mostra essencial
269
284
  if (quiet && !Logger.isEssential(cleanLine)) {
270
- if (Logger.isSystemNoise(cleanLine)) continue;
271
- if (cleanLine.includes("INFO")) continue;
272
- } else if (Logger.isSystemNoise(cleanLine)) {
273
- continue;
285
+ if (cleanLine.includes("INFO") && !cleanLine.includes("ERROR")) continue;
274
286
  }
275
287
 
288
+ // Grep filter
276
289
  if (grep && !cleanLine.toLowerCase().includes(grep.toLowerCase())) {
277
290
  if (!Logger.isEssential(cleanLine)) continue;
278
291
  }
279
292
 
280
293
  const summarized = Logger.summarize(cleanLine);
281
- if (summarized) Logger.log(summarized);
294
+ if (summarized) {
295
+ // SΓ³ mostra se nΓ£o for vazio (rate limiting)
296
+ if (summarized.trim()) Logger.log(summarized);
297
+ }
282
298
  } else {
299
+ // Non-clean: mostra tudo mas formata
300
+ if (Logger.isSystemNoise(cleanLine)) {
301
+ // Silencia completamente noise em non-clean tambΓ©m
302
+ continue;
303
+ }
283
304
  Logger.log(cleanLine);
284
305
  }
285
306
  }
package/src/utils/ui.ts CHANGED
@@ -39,34 +39,110 @@ export class Logger {
39
39
  this.dashboard = dashboard;
40
40
  }
41
41
 
42
- // Banner moderno e clean
42
+ // Banner completo com informaΓ§Γ΅es do ambiente
43
43
  static banner(command?: string, profile?: string, encoding?: string) {
44
44
  console.clear();
45
45
  const git = this.getGitContext();
46
46
  const name = process.cwd().split(/[/\\]/).pop() || "project";
47
+ const now = new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
48
+ const W = 52; // Largura interna do box (espaΓ§o entre os β•‘)
47
49
 
48
- // Linha superior decorativa
49
- console.log(`${C.gray}β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”${C.reset}`);
50
+ // Remove ANSI codes para calcular tamanho
51
+ const plain = (s: string) => s.replace(/\x1b\[\d+m/g, '');
50
52
 
51
- // Logo e projeto
52
- console.log(`${C.gray}β”‚${C.reset} ${C.primary}${C.bold}XAVVA${C.reset}${C.gray}.${C.reset}${C.dim}v${pkg.version}${C.reset} ${C.gray}β”‚${C.reset} ${C.white}${C.bold}${name}${C.reset}`);
53
+ // Cria uma linha com conteΓΊdo alinhado Γ  esquerda
54
+ const row = (content: string) => {
55
+ const pad = W - plain(content).length;
56
+ return `${C.gray}β•‘${C.reset} ${content}${' '.repeat(Math.max(0, pad))}${C.gray}β•‘${C.reset}`;
57
+ };
58
+
59
+ // Linha superior
60
+ console.log(`${C.gray}╔══════════════════════════════════════════════════════╗${C.reset}`);
61
+
62
+ // Linha 1: XAVVA v2.2.0 + hora alinhada Γ  direita
63
+ const verPlain = `XAVVA v${pkg.version}`; // "XAVVA v2.2.0" = 12 chars
64
+ const timePlain = now; // 5 chars
65
+ const gap1 = W - verPlain.length - timePlain.length - 1; // -1 para deixar 1 espaΓ§o antes do β•‘
66
+ const line1Content = `${C.primary}${C.bold}XAVVA${C.reset}${C.gray} v${pkg.version}${C.reset}${' '.repeat(Math.max(1, gap1))}${C.dim}${now}${C.reset}`;
67
+ console.log(row(line1Content));
53
68
 
54
- // Info adicional em uma linha
55
- const parts: string[] = [];
56
- if (command) parts.push(`${C.primary}${command}${C.reset}`);
57
- if (profile) parts.push(`${C.warning}profile:${profile}${C.reset}`);
58
- if (encoding) parts.push(`${C.info}${encoding}${C.reset}`);
59
- if (git.branch) parts.push(`${C.secondary}git:${git.branch}${C.reset}`);
69
+ // Linha 2: Nome do projeto
70
+ console.log(row(`${C.white}${C.bold}${name}${C.reset}`));
60
71
 
61
- if (parts.length > 0) {
62
- console.log(`${C.gray}β”‚${C.reset} ${C.dim}mode${C.reset} ${C.gray}β”‚${C.reset} ${parts.join(` ${C.gray}β€’${C.reset} `)}`);
72
+ // Linha 3: Git info
73
+ if (git.branch) {
74
+ const gitStatus = this.getGitStatus();
75
+ const dirty = gitStatus.dirty ? '*' : '';
76
+ const author = git.author ? git.author.split(' ')[0].slice(0, 10) : '';
77
+ const hash = git.hash ? git.hash.slice(0, 7) : '';
78
+ const branchDisplay = git.branch.slice(0, 20); // Limita branch
79
+ const gitLine = `${C.gray}git:${C.reset}${C.secondary}${branchDisplay}${dirty}${C.reset} ${C.dim}${hash}${C.reset} ${C.gray}by${C.reset} ${C.dim}${author}${C.reset}`;
80
+ console.log(row(gitLine));
63
81
  }
64
82
 
65
- // Linha inferior
66
- console.log(`${C.gray}β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜${C.reset}`);
83
+ // Divisor
84
+ console.log(`${C.gray}╠══════════════════════════════════════════════════════╣${C.reset}`);
85
+
86
+ // Config: mode, profile, java, encoding
87
+ const cfg: string[] = [];
88
+ if (command) cfg.push(`${C.primary}${command}${C.reset}`);
89
+ if (profile) cfg.push(`${C.warning}${profile}${C.reset}`);
90
+ const jv = this.getJavaVersion();
91
+ if (jv) cfg.push(`${C.info}java:${jv}${C.reset}`);
92
+ if (encoding) cfg.push(`${C.gray}${encoding}${C.reset}`);
93
+
94
+ if (cfg.length) {
95
+ const sep = `${C.gray} β”‚ ${C.reset}`;
96
+ const cfgLine = `${C.dim}mode${C.reset} : ${cfg.join(sep)}`;
97
+ console.log(row(cfgLine));
98
+ }
99
+
100
+ // Memory
101
+ const mem = process.memoryUsage();
102
+ const mb = Math.round((mem.heapUsed || mem.rss || 0) / 1024 / 1024);
103
+ console.log(row(`${C.dim}mem${C.reset} : ${mb}MB ${C.gray}heap${C.reset}`));
104
+
105
+ // OS
106
+ const plat = process.platform === 'win32' ? 'windows' : process.platform;
107
+ console.log(row(`${C.dim}os${C.reset} : ${plat} ${C.gray}|${C.reset} ${process.arch}`));
108
+
109
+ // RodapΓ©
110
+ console.log(`${C.gray}β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•${C.reset}`);
67
111
  console.log();
68
112
  }
69
113
 
114
+ private static getGitStatus(): { dirty: boolean; modified: number } {
115
+ try {
116
+ const result = Bun.spawnSync(["git", "status", "--porcelain"]);
117
+ const lines = result.stdout.toString().trim().split('\n').filter(l => l.trim());
118
+ return { dirty: lines.length > 0, modified: lines.length };
119
+ } catch {
120
+ return { dirty: false, modified: 0 };
121
+ }
122
+ }
123
+
124
+ private static getJavaVersion(): string | null {
125
+ try {
126
+ const javaBin = process.env.JAVA_HOME
127
+ ? `${process.env.JAVA_HOME}/bin/java`
128
+ : 'java';
129
+ const result = Bun.spawnSync([javaBin, "-version"]);
130
+ const output = (result.stderr?.toString() || result.stdout?.toString() || '');
131
+ const match = output.match(/version "?(\d+(?:\.\d+)?)/);
132
+ if (match) {
133
+ const v = match[1];
134
+ // Check for DCEVM
135
+ if (output.toLowerCase().includes('dcevm') || output.toLowerCase().includes('jbr')) {
136
+ return `${v}+dcevm`;
137
+ }
138
+ return v;
139
+ }
140
+ return null;
141
+ } catch {
142
+ return null;
143
+ }
144
+ }
145
+
70
146
  // SeΓ§Γ΅es com divisΓ³rias clean
71
147
  static section(title: string) {
72
148
  console.log(`${C.gray}β”Œβ”€ ${C.white}${C.bold}${title}${C.reset}`);
@@ -112,18 +188,46 @@ export class Logger {
112
188
  console.log(`${C.gray}β”‚${C.reset} ${C.warning}⚠${C.reset} ${msg}`);
113
189
  }
114
190
 
115
- static build(msg: string) {
116
- console.log(`${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}build${C.reset} ${C.gray}:${C.reset} ${msg}`);
191
+ static debug(msg: string) {
192
+ console.log(`${C.gray}β”‚${C.reset} ${C.gray}πŸ› ${msg}${C.reset}`);
117
193
  }
118
194
 
119
- static server(msg: string) {
120
- console.log(`${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}server${C.reset} ${C.gray}:${C.reset} ${msg}`);
195
+ static step(msg: string) {
196
+ console.log(`${C.gray}β”‚${C.reset} ${C.gray}β–Έ ${msg}${C.reset}`);
197
+ }
198
+
199
+ static log(msg: string) {
200
+ console.log(msg);
201
+ }
202
+
203
+ static dim(msg: string) {
204
+ console.log(`${C.dim}${msg}${C.reset}`);
205
+ }
206
+
207
+ static newline() {
208
+ console.log();
209
+ }
210
+
211
+ static watcher(msg: string, _type?: string) {
212
+ console.log(`${C.gray}β”‚${C.reset} ${C.secondary}β—‰${C.reset} ${C.dim}watch${C.reset} ${C.gray}:${C.reset} ${msg}`);
121
213
  }
122
214
 
123
215
  static watch(msg: string) {
124
216
  console.log(`${C.gray}β”‚${C.reset} ${C.secondary}β—‰${C.reset} ${C.dim}watch${C.reset} ${C.gray}:${C.reset} ${msg}`);
125
217
  }
126
218
 
219
+ static process(msg: string) {
220
+ console.log(`${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}process${C.reset} ${C.gray}:${C.reset} ${msg}`);
221
+ }
222
+
223
+ static build(msg: string) {
224
+ console.log(`${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}build${C.reset} ${C.gray}:${C.reset} ${msg}`);
225
+ }
226
+
227
+ static server(msg: string) {
228
+ console.log(`${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}server${C.reset} ${C.gray}:${C.reset} ${msg}`);
229
+ }
230
+
127
231
  static hotswap(msg: string) {
128
232
  console.log(`${C.gray}β”‚${C.reset} ${C.secondary}↻${C.reset} ${C.dim}hotswap${C.reset} ${C.gray}:${C.reset} ${msg}`);
129
233
  }
@@ -161,11 +265,6 @@ export class Logger {
161
265
  };
162
266
  }
163
267
 
164
- // Linha em branco
165
- static newline() {
166
- console.log();
167
- }
168
-
169
268
  // DivisΓ³ria simples
170
269
  static divider() {
171
270
  console.log(`${C.gray}β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€${C.reset}`);
@@ -180,8 +279,8 @@ export class Logger {
180
279
  // Helper para contexto git
181
280
  static getGitContext(): { branch: string; author: string; hash: string } {
182
281
  try {
183
- const branch = Bun.spawnSync(["git", "rev-parse", "abbrev-ref", "HEAD"]).stdout.toString().trim();
184
- const author = Bun.spawnSync(["git", "log", "-1", "format=%an"]).stdout.toString().trim();
282
+ const branch = Bun.spawnSync(["git", "rev-parse", "--abbrev-ref", "HEAD"]).stdout.toString().trim();
283
+ const author = Bun.spawnSync(["git", "log", "-1", "--format=%an"]).stdout.toString().trim();
185
284
  const hash = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"]).stdout.toString().trim();
186
285
  return { branch, author, hash };
187
286
  } catch {
@@ -190,6 +289,10 @@ export class Logger {
190
289
  }
191
290
 
192
291
  // Filtros de noise (mantidos)
292
+ // Controle de rate limiting para hotswap
293
+ private static lastHotswapTime = 0;
294
+ private static hotswapCount = 0;
295
+
193
296
  static isSystemNoise(line: string): boolean {
194
297
  const noise = [
195
298
  "Using CATALINA_", "Using JRE_HOME", "Using CLASSPATH",
@@ -198,10 +301,22 @@ export class Logger {
198
301
  "SLF4J: ", "Discovered plugins:",
199
302
  "enhanced with plugin initialization", "Hotswap ready",
200
303
  "autoHotswap.delay", "watchResources=false",
304
+ // HotswapAgent noise
305
+ "TreeWatcherNIO", "HOTSWAP AGENT", "org.hotswap.agent",
306
+ // Jersey/JAX-RS noise
307
+ "org.glassfish.jersey", "The (sub)resource method",
308
+ // Tomcat noise
309
+ "org.apache.catalina", "org.apache.jasper",
201
310
  ];
202
311
  return noise.some(n => line.includes(n));
203
312
  }
204
313
 
314
+ static isEssential(line: string): boolean {
315
+ return line.includes("SEVERE") || line.includes("ERROR") || line.includes("Exception") ||
316
+ line.includes("Caused by") || line.includes("at ") || line.includes("... ") ||
317
+ line.includes("Server startup in") || line.includes("HOTSWAP AGENT:");
318
+ }
319
+
205
320
  // SumarizaΓ§Γ£o de logs do Tomcat (simplificada)
206
321
  static summarize(line: string): string {
207
322
  if (this.isSystemNoise(line)) return "";
@@ -214,9 +329,17 @@ export class Logger {
214
329
  return `${C.success}ready ${C.gray}in ${C.white}${seconds}s${C.reset}`;
215
330
  }
216
331
 
217
- // Hotswap
332
+ // Hotswap com rate limiting (evita spam)
218
333
  if (line.includes("HOTSWAP AGENT") && line.includes("RELOAD")) {
219
- return `${C.secondary}↻ hotswap ${C.gray}detected${C.reset}`;
334
+ const now = Date.now();
335
+ if (now - this.lastHotswapTime < 2000) {
336
+ this.hotswapCount++;
337
+ return ""; // Silencia se dentro de 2s
338
+ }
339
+ this.lastHotswapTime = now;
340
+ const count = this.hotswapCount > 0 ? ` ${C.gray}(${this.hotswapCount} more)${C.reset}` : "";
341
+ this.hotswapCount = 0;
342
+ return `${C.secondary}↻ hotswap${C.reset}${count}`;
220
343
  }
221
344
 
222
345
  // Erros de compilaΓ§Γ£o
@@ -231,11 +354,293 @@ export class Logger {
231
354
  return `${C.error}βœ— ${C.gray}${line.slice(0, 80)}${C.reset}`;
232
355
  }
233
356
 
234
- // Warnings
235
- if (line.includes("WARNING")) {
357
+ // Warnings (filtra noise conhecido)
358
+ if (line.includes("WARNING") || line.includes("ADVERTÊNCIA")) {
359
+ if (this.isSystemNoise(line)) return "";
236
360
  return `${C.warning}⚠ ${C.gray}${line.slice(0, 80)}${C.reset}`;
237
361
  }
238
362
 
239
363
  return "";
240
364
  }
365
+
366
+ // ========== Tomcat Log Formatting ==========
367
+
368
+ private static tomcatNoisePatterns = [
369
+ /^Using CATALINA_/,
370
+ /^Using JRE_HOME/,
371
+ /^Using CLASSPATH/,
372
+ /^Using CATALINA_OPTS/,
373
+ /^NOTE: Picked up JDK_JAVA_OPTIONS/,
374
+ /^HOTSWAP AGENT:.*Plugin.*initialized in ClassLoader/,
375
+ /^HOTSWAP AGENT:.*Registering directory/,
376
+ /^HOTSWAP AGENT:.*WARNING.*TreeWatcherNIO.*Unable to watch/,
377
+ /^HOTSWAP AGENT:.*INFO.*TreeWatcherNIO/,
378
+ /^HOTSWAP AGENT:.*INFO.*PluginRegistry.*Discovered plugins/,
379
+ /^HOTSWAP AGENT:.*INFO.*HotswapAgent.*Loading Hotswap agent/,
380
+ /^HOTSWAP AGENT:.*INFO.*TomcatPlugin.*Tomcat plugin initialized/,
381
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*VersionLoggerListener/,
382
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*AprLifecycleListener/,
383
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Command line argument/,
384
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*CATALINA_BASE/,
385
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*CATALINA_HOME/,
386
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Server version/,
387
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Server built/,
388
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*OS Name/,
389
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*OS Version/,
390
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Architecture/,
391
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Java Home/,
392
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*JVM Version/,
393
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*JVM Vendor/,
394
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Loaded Apache Tomcat Native/,
395
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*APR capabilities/,
396
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*APR\/OpenSSL configuration/,
397
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*OpenSSL successfully initialized/,
398
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Server initialization in/,
399
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Starting service/,
400
+ /^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Starting Servlet engine/,
401
+ /^ParallelWebappClassLoader/,
402
+ /^context:/,
403
+ /^delegate:/,
404
+ /^-+> Parent Classloader/,
405
+ /^java\.net\.URLClassLoader/,
406
+ ];
407
+
408
+ static isTomcatNoise(line: string): boolean {
409
+ return this.tomcatNoisePatterns.some(p => p.test(line));
410
+ }
411
+
412
+ static formatTomcatLog(line: string): string {
413
+ const cleanLine = line.trim();
414
+ if (!cleanLine) return "";
415
+
416
+ // Silencia noise completamente
417
+ if (this.isTomcatNoise(cleanLine)) return "";
418
+
419
+ // HOTSWAP AGENT: Loading Hotswap agent X.X.X
420
+ if (cleanLine.includes("HOTSWAP AGENT") && cleanLine.includes("Loading Hotswap agent")) {
421
+ const versionMatch = cleanLine.match(/Loading Hotswap agent ([\d.]+)/);
422
+ if (versionMatch) {
423
+ return `${C.gray}β”‚${C.reset} ${C.secondary}↻${C.reset} ${C.dim}HotswapAgent v${versionMatch[1]}${C.reset}`;
424
+ }
425
+ }
426
+
427
+ // HOTSWAP AGENT: Discovered plugins
428
+ if (cleanLine.includes("HOTSWAP AGENT") && cleanLine.includes("Discovered plugins")) {
429
+ return `${C.gray}β”‚${C.reset} ${C.secondary}↻${C.reset} ${C.dim}plugins loaded${C.reset}`;
430
+ }
431
+
432
+ // Server initialization
433
+ const initMatch = cleanLine.match(/Server initialization in \[(\d+)\] milliseconds/);
434
+ if (initMatch) {
435
+ return `${C.gray}β”‚${C.reset} ${C.success}●${C.reset} ${C.dim}initialized in ${initMatch[1]}ms${C.reset}`;
436
+ }
437
+
438
+ // Starting service [Catalina]
439
+ const serviceMatch = cleanLine.match(/Starting service \[(\w+)\]/);
440
+ if (serviceMatch) {
441
+ return `${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}starting ${serviceMatch[1].toLowerCase()}${C.reset}`;
442
+ }
443
+
444
+ // Starting Servlet engine
445
+ if (cleanLine.includes("Starting Servlet engine")) {
446
+ const versionMatch = cleanLine.match(/Apache Tomcat\/([\d.]+)/);
447
+ if (versionMatch) {
448
+ return `${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}Tomcat ${versionMatch[1]}${C.reset}`;
449
+ }
450
+ }
451
+
452
+ // Deploy directory
453
+ const deployMatch = cleanLine.match(/deployDirectory.*webapps[\\/]([^'"\]]+)/);
454
+ if (deployMatch) {
455
+ return `${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}deploying ${deployMatch[1]}${C.reset}`;
456
+ }
457
+
458
+ // Server startup in ( jΓ‘ temos no summarize mas reforΓ§a aqui )
459
+ const startupMatch = cleanLine.match(/Server startup in.*?([\d,]+)\s*ms/);
460
+ if (startupMatch) {
461
+ const time = startupMatch[1].replace(",", "");
462
+ const seconds = (parseInt(time) / 1000).toFixed(1);
463
+ return `${C.gray}β”‚${C.reset} ${C.success}●${C.reset} ${C.dim}ready in ${C.white}${seconds}s${C.reset}`;
464
+ }
465
+
466
+ // Tomcat versΓ£o info (sumarizado)
467
+ const tomcatVersionMatch = cleanLine.match(/Server version number:\s+([\d.]+)/);
468
+ if (tomcatVersionMatch) {
469
+ return `${C.gray}β”‚${C.reset} ${C.dim}Tomcat ${tomcatVersionMatch[1]}${C.reset}`;
470
+ }
471
+
472
+ // JVM info sumarizada
473
+ const jvmMatch = cleanLine.match(/JVM Version:\s+([\d._]+)/);
474
+ if (jvmMatch) {
475
+ return `${C.gray}β”‚${C.reset} ${C.dim}JVM ${jvmMatch[1]}${C.reset}`;
476
+ }
477
+
478
+ // Protocol handler init
479
+ const protocolMatch = cleanLine.match(/Initializing ProtocolHandler \["(.+?)"\]/);
480
+ if (protocolMatch) {
481
+ return `${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}${protocolMatch[1]}${C.reset}`;
482
+ }
483
+
484
+ // Erros e warnings que passaram pelo filtro
485
+ if (cleanLine.includes("SEVERE") || cleanLine.includes("ERROR")) {
486
+ return `${C.gray}β”‚${C.reset} ${C.error}βœ—${C.reset} ${C.gray}${cleanLine.slice(0, 80)}${C.reset}`;
487
+ }
488
+
489
+ if (cleanLine.includes("WARNING") || cleanLine.includes("ADVERTÊNCIA")) {
490
+ return `${C.gray}β”‚${C.reset} ${C.warning}⚠${C.reset} ${C.gray}${cleanLine.slice(0, 80)}${C.reset}`;
491
+ }
492
+
493
+ // Outros logs INFO - mostra resumido
494
+ const infoMatch = cleanLine.match(/(?:INFORMAÇÕES|INFO)\s+\[.*?\]\s+(.+)/);
495
+ if (infoMatch) {
496
+ const msg = infoMatch[1].trim();
497
+ if (msg.length > 0 && !this.isTomcatNoise(msg)) {
498
+ return `${C.gray}β”‚${C.reset} ${C.dim}${msg.slice(0, 70)}${C.reset}`;
499
+ }
500
+ }
501
+
502
+ return "";
503
+ }
504
+
505
+ // ========== Build Log Formatting (Maven/Gradle) ==========
506
+
507
+ private static buildNoisePatterns = [
508
+ /^\[INFO\]\s+Scanning for projects/,
509
+ /^\[INFO\]\s+Using the MultiThreadedBuilder/,
510
+ /^\[INFO\]\s+---\s+.*\s+---$/,
511
+ /^\[INFO\]\s+T+E+\s*$/,
512
+ /^\[INFO\]\s+BUILD\s+SUCCESS/i,
513
+ /^\[INFO\]\s+BUILD\s+FAILURE/i,
514
+ /^\[INFO\]\s+Total time:/,
515
+ /^\[INFO\]\s+Finished at:/,
516
+ /^\[INFO\]\s+Final Memory:/,
517
+ /^\[INFO\]\s+http:\/\/cwiki\.apache\.org/,
518
+ /^\[INFO\]\s+-> \[Help 1\]/,
519
+ /^\[INFO\]\s+Re-run Maven using/,
520
+ /^\[INFO\]\s+To see the full stack trace/,
521
+ /^\[INFO\]\s+For more information about the errors/,
522
+ /^\[ERROR\]\s+To see the full stack trace/,
523
+ /^\[ERROR\]\s+Re-run Maven using/,
524
+ /^\[ERROR\]\s+For more information/,
525
+ /^\[ERROR\]\s+-> \[Help 1\]/,
526
+ /^\[WARNING\]\s+It is highly recommended/,
527
+ /^\[WARNING\]\s+For this reason, future Maven/,
528
+ /^\[WARNING\]\s+\[HELP, sysprop:version/,
529
+ /^\[WARNING\]\s+Some problems were encountered/,
530
+ /^\[WARNING\]\s+'dependencies\.dependency/,
531
+ /Building .*war/,
532
+ /from pom\.xml/,
533
+ /\[ war \]/,
534
+ /^\s*$/,
535
+ ];
536
+
537
+ private static gradleNoisePatterns = [
538
+ /^> Task/,
539
+ /^Download/,
540
+ /^Expiring/,
541
+ /^BUILD/,
542
+ /^\d+ actionable task/,
543
+ ];
544
+
545
+ static isBuildNoise(line: string): boolean {
546
+ return this.buildNoisePatterns.some(p => p.test(line)) ||
547
+ this.gradleNoisePatterns.some(p => p.test(line));
548
+ }
549
+
550
+ // Acumulador de erro de compilaΓ§Γ£o (para pegar mensagens multi-linha)
551
+ private static pendingError: { file: string; line: string; msg: string } | null = null;
552
+
553
+ static formatBuildLog(line: string, buildTool: 'maven' | 'gradle' = 'maven'): string {
554
+ const cleanLine = line.trim();
555
+ if (!cleanLine) return "";
556
+
557
+ // Maven: [ERROR] /path/file.java:[123,45] error message
558
+ const mavenErrorMatch = cleanLine.match(/^\[ERROR\]\s+(.+\.java):\[(\d+),\d+\]\s*(.+)/);
559
+ if (mavenErrorMatch) {
560
+ const [, file, lineNum, msg] = mavenErrorMatch;
561
+ const shortFile = file.split(/[/\\]/).pop() || file;
562
+ return `${C.gray}β”‚${C.reset} ${C.error}βœ—${C.reset} ${C.white}${shortFile}${C.gray}:${lineNum}${C.reset} ${C.error}${msg.slice(0, 60)}${C.reset}`;
563
+ }
564
+
565
+ // Maven: [ERROR] COMPILATION ERROR / BUILD FAILURE (tΓ­tulo)
566
+ if (cleanLine.match(/^\[(ERROR|INFO)\]\s+(COMPILATION ERROR|BUILD FAILURE)/)) {
567
+ return `${C.gray}β”‚${C.reset} ${C.error}βœ— COMPILATION FAILED${C.reset}`;
568
+ }
569
+
570
+ // Maven: [WARNING] 'dependencies.dependency...'
571
+ if (cleanLine.match(/^\[WARNING\]\s+'dependencies\.dependency/)) {
572
+ const match = cleanLine.match(/'dependencies\.dependency\.[\w:]+'\s+(.+)/);
573
+ if (match) {
574
+ return `${C.gray}β”‚${C.reset} ${C.warning}⚠${C.reset} ${C.gray}${match[1].slice(0, 60)}${C.reset}`;
575
+ }
576
+ }
577
+
578
+ // Maven: [WARNING] The POM for ... is invalid
579
+ const invalidPomMatch = cleanLine.match(/^\[WARNING\]\s+The POM for (.+?) is invalid/);
580
+ if (invalidPomMatch) {
581
+ return `${C.gray}β”‚${C.reset} ${C.warning}⚠${C.reset} ${C.gray}Invalid POM: ${invalidPomMatch[1].slice(0, 50)}${C.reset}`;
582
+ }
583
+
584
+ // Maven: [INFO] Compiling N source files
585
+ const compilingMatch = cleanLine.match(/^\[INFO\]\s+Compiling\s+(\d+)\s+source/);
586
+ if (compilingMatch) {
587
+ return `${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}compiling ${C.white}${compilingMatch[1]}${C.reset} ${C.dim}files${C.reset}`;
588
+ }
589
+
590
+ // Maven: [INFO] /path/file.java: Some input files use or override a deprecated API
591
+ const deprecatedMatch = cleanLine.match(/^\[INFO\]\s+(.+\.java):\s+Some input files use (.+)/);
592
+ if (deprecatedMatch) {
593
+ const shortFile = deprecatedMatch[1].split(/[/\\]/).pop() || deprecatedMatch[1];
594
+ const type = deprecatedMatch[2].includes('removal') ? 'deprecated (removal)' : 'deprecated';
595
+ return `${C.gray}β”‚${C.reset} ${C.warning}⚠${C.reset} ${C.gray}${shortFile} uses ${type}${C.reset}`;
596
+ }
597
+
598
+ // Maven: [INFO] Changes detected - recompiling
599
+ if (cleanLine.match(/^\[INFO\]\s+Changes detected/)) {
600
+ return `${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}changes detected, recompiling${C.reset}`;
601
+ }
602
+
603
+ // Maven: [INFO] Copying N resources
604
+ const resourcesMatch = cleanLine.match(/^\[INFO\]\s+Copying\s+(\d+)\s+resources?/);
605
+ if (resourcesMatch) {
606
+ return `${C.gray}β”‚${C.reset} ${C.dim}copying ${C.white}${resourcesMatch[1]}${C.reset} ${C.dim}resources${C.reset}`;
607
+ }
608
+
609
+ // Maven: [INFO] Deleting ...
610
+ const deletingMatch = cleanLine.match(/^\[INFO\]\s+Deleting\s+(.+)/);
611
+ if (deletingMatch) {
612
+ return `${C.gray}β”‚${C.reset} ${C.dim}cleaning target directory${C.reset}`;
613
+ }
614
+
615
+ // Maven: [INFO] skip non existing resourceDirectory
616
+ if (cleanLine.match(/^\[INFO\]\s+skip non existing/)) {
617
+ return ""; // Silencia
618
+ }
619
+
620
+ // Gradle: > Task :name
621
+ const gradleTaskMatch = cleanLine.match(/^> Task :(.+)/);
622
+ if (gradleTaskMatch) {
623
+ return `${C.gray}β”‚${C.reset} ${C.primary}β–Έ${C.reset} ${C.dim}${gradleTaskMatch[1]}${C.reset}`;
624
+ }
625
+
626
+ // Gradle errors
627
+ const gradleErrorMatch = cleanLine.match(/^(.+\.java):(\d+):\s*(error|warning):\s*(.+)/);
628
+ if (gradleErrorMatch) {
629
+ const [, file, lineNum, level, msg] = gradleErrorMatch;
630
+ const shortFile = file.split(/[/\\]/).pop() || file;
631
+ const icon = level === 'error' ? C.error + 'βœ—' + C.reset : C.warning + '⚠' + C.reset;
632
+ return `${C.gray}β”‚${C.reset} ${icon} ${C.white}${shortFile}${C.gray}:${lineNum}${C.reset} ${C.error}${msg.slice(0, 60)}${C.reset}`;
633
+ }
634
+
635
+ // Se nΓ£o for nenhum padrΓ£o conhecido e nΓ£o for noise, retorna formatado como info
636
+ if (!this.isBuildNoise(cleanLine)) {
637
+ // Remove prefixos [INFO], [WARNING], [ERROR] genΓ©ricos
638
+ const clean = cleanLine.replace(/^\[(INFO|WARNING|ERROR)\]\s*/, '');
639
+ if (clean.length > 0 && !this.isBuildNoise(clean)) {
640
+ return `${C.gray}β”‚${C.reset} ${C.dim}${clean.slice(0, 70)}${C.reset}`;
641
+ }
642
+ }
643
+
644
+ return "";
645
+ }
241
646
  }