Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 88 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,29 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![Crates.io Downloads](https://img.shields.io/crates/d/syncable-cli)

## 🎯 **260+ Technologies Supported**
**The most comprehensive project analyzer supporting 5 major languages and their complete ecosystems:**
- ☕ **Java/JVM**: 98 technologies (13 Spring components + enterprise stack)
- 🐍 **Python**: 76 technologies (Django, FastAPI, ML/Data Science)
- 🟨 **JavaScript/TypeScript**: 46 technologies (React, Next.js, Node.js)
- 🐹 **Go**: 21 technologies (cloud-native & microservices)
- 🦀 **Rust**: 20 technologies (high-performance web & systems)

## 🌟 Help Other Developers Discover This Tool

**If this tool saves you time, please consider giving it a ⭐ on GitHub!**

Stars help other developers find Syncable CLI, and the more builders who discover it early, the better we can make it for everyone. Every star helps us reach developers who could benefit from automated infrastructure analysis and generation.

[⭐ **Star on GitHub**](https://github.com/syncable-dev/syncable-cli)


## ✨ Features

### 🔍 Comprehensive Project Analysis
- **Language Detection**: Automatically detects JavaScript/TypeScript, Python, Rust, Go, Java/Kotlin
- **Framework Recognition**: Identifies 70+ frameworks including Express, React, Django, FastAPI, Spring Boot
- **Dependency Analysis**: Parses all package managers and extracts version constraints
- **Language Detection**: Automatically detects JavaScript/TypeScript, Python, Rust, Go, Java/Kotlin with precise version detection
- **Framework Recognition**: Identifies **260+ technologies** across all major ecosystems including complete Spring, Django, React, and Express families
- **Dependency Analysis**: Parses all package managers (npm/yarn/pnpm, pip/poetry, cargo, go mod, maven/gradle) and extracts version constraints
- **Vulnerability Scanning**: Integrates with security databases for each language ecosystem
- **Security Analysis**: Basic secret detection and environment variable security checks
- **Context Extraction**: Discovers entry points, ports, environment variables, and build scripts
Expand Down Expand Up @@ -277,28 +294,74 @@ max_file_size = 2097152 # 2MB
format = "json" # or "yaml", "toml"
```

## 🧪 Supported Technologies

### Languages & Runtimes
- JavaScript/TypeScript (Node.js)
- Python (3.7+)
- Rust
- Go
- Java/Kotlin

### Frameworks (70+ supported)
- **JavaScript**: Express, Next.js, React, Vue, Angular, Nest.js
- **Python**: Django, Flask, FastAPI, Pyramid
- **Rust**: Actix-web, Rocket, Axum, Warp
- **Go**: Gin, Echo, Fiber, Chi
- **Java**: Spring Boot, Micronaut, Quarkus

### Package Managers
- npm, yarn, pnpm
- pip, poetry, pipenv
- cargo
- go mod
- maven, gradle
## 🧪 Comprehensive Technology Support (260+ Technologies)

### 📊 Coverage by Language
- **☕ Java/JVM**: **98 technologies** - The most comprehensive JVM ecosystem coverage
- **🐍 Python**: **76 technologies** - Complete Python web, data, and ML stack
- **🟨 JavaScript/TypeScript**: **46 technologies** - Full-stack web development ecosystem
- **🐹 Go**: **21 technologies** - Modern cloud-native and microservices tools
- **🦀 Rust**: **20 technologies** - High-performance systems and web frameworks

### 🌟 Major Ecosystem Coverage

#### ☕ **Java/JVM Ecosystem** (98 technologies)
**Spring Family** (13 technologies):
- Spring Boot, Spring Framework, Spring Security, Spring Data
- Spring Cloud (Gateway, Config, Netflix), Spring WebFlux, Spring MVC
- Spring Batch, Spring Integration, Spring AOP, and more

**Enterprise & Microservices**: Quarkus, Micronaut, Dropwizard, Jakarta EE
**Database & ORM**: Hibernate, MyBatis, JPA, JDBI, MongoDB Driver, Redis Jedis
**Message Brokers**: Apache Kafka, RabbitMQ, ActiveMQ, Apache Pulsar
**Search & Big Data**: Elasticsearch, Apache Solr, Apache Spark, Apache Flink
**Security**: Apache Shiro, Keycloak, Bouncy Castle, JWT, OAuth2
**Build Tools**: Maven, Gradle, Ant
**Testing**: JUnit, TestNG, Mockito, Selenium, Cucumber, Testcontainers
**Web Servers**: Tomcat, Jetty, Undertow, Netty

#### 🐍 **Python Ecosystem** (76 technologies)
**Web Frameworks**: Django, Flask, FastAPI, Pyramid, CherryPy, Tornado, Falcon
**Django Family**: Django REST Framework, Django ORM, Django-allauth
**Data & ML**: NumPy, Pandas, Scikit-learn, TensorFlow, PyTorch, Keras
**Database & ORM**: SQLAlchemy, Alembic, psycopg2, PyMongo, Redis-py
**Async & Messaging**: Celery, asyncio, aiohttp, Dramatiq
**Scientific**: Matplotlib, Seaborn, Jupyter, SciPy
**WSGI/ASGI Servers**: Gunicorn, Uvicorn, Hypercorn, Daphne, Waitress
**Testing**: pytest, unittest, nose2, behave, Robot Framework

#### 🟨 **JavaScript/TypeScript Ecosystem** (46 technologies)
**Meta-Frameworks**: Next.js, Nuxt.js, SvelteKit, Astro, SolidStart, Tanstack Start
**Frontend**: React, Vue.js, Angular, Svelte, SolidJS
**Mobile**: React Native, Expo
**Backend**: Express.js, Nest.js, Fastify, Hono, Elysia
**Database/ORM**: Prisma, Drizzle ORM, TypeORM, Mongoose, Sequelize
**Build Tools**: Vite, Webpack, Rollup, Parcel
**Runtimes**: Node.js, Bun, Deno, Cloudflare Workers, Vercel Edge
**Testing**: Jest, Vitest, Cypress, Playwright

#### 🐹 **Go Ecosystem** (21 technologies)
**Web Frameworks**: Gin, Echo, Fiber, Chi, Gorilla Mux, Beego
**Microservices**: gRPC, go-kit, go-micro
**Database**: GORM, sqlx, pgx
**Cloud Native**: Kubernetes client, Docker, Consul
**Testing**: Testify, Ginkgo, GoConvey

#### 🦀 **Rust Ecosystem** (20 technologies)
**Web Frameworks**: Actix-web, Axum, Rocket, Warp, Tide
**Async Runtimes**: Tokio, async-std
**Database/ORM**: SeaORM, Diesel, SQLx
**Serialization**: Serde
**Testing**: Built-in test framework, criterion (benchmarking)

### 📦 **Package Manager Support**
- **JavaScript**: npm, yarn, pnpm, bun
- **Python**: pip, poetry, pipenv, conda, pdm
- **Java**: Maven, Gradle
- **Rust**: Cargo
- **Go**: go mod
- **PHP**: Composer
- **Ruby**: Bundler

## 🤝 Contributing

Expand Down
59 changes: 40 additions & 19 deletions src/analyzer/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,40 +234,40 @@ impl BoxDrawer {

if total_needed <= available_space {
// Everything fits - right-align the value
let padding_needed = available_space - label_width - value_width;
let padding_needed = available_space.saturating_sub(label_width).saturating_sub(value_width);
format!("{}{}{}", formatted_label, " ".repeat(padding_needed), formatted_value)
} else {
// Need to truncate value
let max_value_width = available_space.saturating_sub(label_width + min_space_between);
let truncated_value = truncate_to_width(&formatted_value, max_value_width);
let truncated_value_width = visual_width(&truncated_value);
let padding_needed = available_space - label_width - truncated_value_width;
let padding_needed = available_space.saturating_sub(label_width).saturating_sub(truncated_value_width);
format!("{}{}{}", formatted_label, " ".repeat(padding_needed), truncated_value)
}
} else if !line.value.is_empty() {
// Value only - left-align it (for descriptions, etc.)
let value_width = visual_width(&formatted_value);
if value_width <= content_width {
let padding_needed = content_width - value_width;
let padding_needed = content_width.saturating_sub(value_width);
format!("{}{}", formatted_value, " ".repeat(padding_needed))
} else {
// Truncate and ensure it fills exactly content_width
let truncated = truncate_to_width(&formatted_value, content_width);
let actual_width = visual_width(&truncated);
let padding_needed = content_width - actual_width;
let padding_needed = content_width.saturating_sub(actual_width);
format!("{}{}", truncated, " ".repeat(padding_needed))
}
} else if !line.label.is_empty() {
// Label only - left-align it
let label_width = visual_width(&formatted_label);
if label_width <= content_width {
let padding_needed = content_width - label_width;
let padding_needed = content_width.saturating_sub(label_width);
format!("{}{}", formatted_label, " ".repeat(padding_needed))
} else {
// Truncate and ensure it fills exactly content_width
let truncated = truncate_to_width(&formatted_label, content_width);
let actual_width = visual_width(&truncated);
let padding_needed = content_width - actual_width;
let padding_needed = content_width.saturating_sub(actual_width);
format!("{}{}", truncated, " ".repeat(padding_needed))
}
} else {
Expand All @@ -285,7 +285,7 @@ impl BoxDrawer {
content
} else {
// Add padding to reach exact width for non-table content
let padding_needed = content_width - actual_content_width;
let padding_needed = content_width.saturating_sub(actual_content_width);
format!("{}{}", content, " ".repeat(padding_needed))
}
} else {
Expand Down Expand Up @@ -624,10 +624,10 @@ fn display_single_project_matrix(analysis: &MonorepoAnalysis) {
box_drawer.add_line("Name:", &project.name.yellow(), true);
box_drawer.add_line("Type:", &format_project_category(&project.project_category).green(), true);

// Languages with confidence
// Languages
if !project.analysis.languages.is_empty() {
let lang_info = project.analysis.languages.iter()
.map(|l| format!("{} ({:.0}%)", l.name, l.confidence * 100.0))
.map(|l| l.name.clone())
.collect::<Vec<_>>()
.join(", ");
box_drawer.add_line("Languages:", &lang_info.blue(), true);
Expand Down Expand Up @@ -669,10 +669,7 @@ fn add_technologies_to_drawer(technologies: &[DetectedTechnology], box_drawer: &

// Display primary technology first
if let Some(primary) = technologies.iter().find(|t| t.is_primary) {
let primary_info = format!("{} {}",
primary.name.bright_yellow().bold(),
format!("({:.0}%)", primary.confidence * 100.0).dimmed()
);
let primary_info = primary.name.bright_yellow().bold().to_string();
box_drawer.add_line("Primary Stack:", &primary_info, true);
}

Expand All @@ -687,7 +684,7 @@ fn add_technologies_to_drawer(technologies: &[DetectedTechnology], box_drawer: &
for (category, label) in &categories {
if let Some(techs) = by_category.get(category) {
let tech_names = techs.iter()
.map(|t| format!("{} ({:.0}%)", t.name, t.confidence * 100.0))
.map(|t| t.name.clone())
.collect::<Vec<_>>()
.join(", ");

Expand All @@ -698,16 +695,40 @@ fn add_technologies_to_drawer(technologies: &[DetectedTechnology], box_drawer: &
}
}

// Handle Library category separately since it's parameterized
// Handle Library category separately since it's parameterized - use vertical layout for many items
let mut all_libraries: Vec<&DetectedTechnology> = Vec::new();
for (cat, techs) in &by_category {
if matches!(cat, TechnologyCategory::Library(_)) {
let tech_names = techs.iter()
.map(|t| format!("{} ({:.0}%)", t.name, t.confidence * 100.0))
all_libraries.extend(techs.iter().copied());
}
}

if !all_libraries.is_empty() {
// Sort libraries by confidence for better display
all_libraries.sort_by(|a, b| b.confidence.partial_cmp(&a.confidence).unwrap_or(std::cmp::Ordering::Equal));

if all_libraries.len() <= 3 {
// For few libraries, keep horizontal layout
let tech_names = all_libraries.iter()
.map(|t| t.name.clone())
.collect::<Vec<_>>()
.join(", ");
box_drawer.add_line("Libraries:", &tech_names.magenta(), true);
} else {
// For many libraries, use vertical layout with multiple rows
box_drawer.add_line("Libraries:", "", true);

if !tech_names.is_empty() {
box_drawer.add_line("Libraries:", &tech_names.magenta(), true);
// Group libraries into rows of 3-4 items each
let items_per_row = 3;
for chunk in all_libraries.chunks(items_per_row) {
let row_items = chunk.iter()
.map(|t| t.name.clone())
.collect::<Vec<_>>()
.join(", ");

// Add indented row
let indented_row = format!(" {}", row_items);
box_drawer.add_value_only(&indented_row.magenta());
}
}
}
Expand Down
Loading