mirror of
https://github.com/rtk-ai/rtk.git
synced 2026-09-19 07:33:17 +08:00
4792008fc1
* fix: remove decorative emojis from CLI output (#511) Replace decorative emojis with plain text to reduce token waste. Keep functional symbols (⚠️ ✓ ❌ ✅ ℹ️) that convey meaning in fewer tokens. * fix: remove remaining decorative emojis from find_cmd and formatter Missed in initial emoji cleanup pass: 📁 in find_cmd.rs and parser/formatter.rs * fix: remove all decorative emojis from CLI output (#511) Replace emojis with plain text tokens across all production files for better LLM compatibility. Test fixtures and external tool detection patterns (e.g. Black's "All done!") are preserved. * fix: remove last decorative emoji from next_cmd.rs Remove ⚡ from Next.js Build header, missed in previous passes. * fix: remove remaining emojis from gh_cmd.rs and init.rs Replace production emojis: - gh_cmd.rs: 🟣→[merged], ⚪→[unknown]/[pending], ⭐→removed, 🔱→removed - init.rs: ⚪→[--] for "not found" status indicators * fix: remove all checkmark emojis from CLI output Replace ✓ (U+2713) with plain text across 19 files: - "ok ✓" → "ok" (git add/commit/push/pull) - "✓ cargo test: ..." → "cargo test: ..." (all tool summaries) - Preserved ✓ in input detection patterns and test fixtures LLMs cannot interpret emoji semantics; plain text is clearer. --------- Signed-off-by: Patrick Szymkowiak <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> Signed-off-by: Patrick Szymkowiak <patrick@rtk-ai.app>
269 lines
8.8 KiB
Rust
269 lines
8.8 KiB
Rust
use crate::tracking;
|
|
use anyhow::Result;
|
|
use regex::Regex;
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
/// Summarize project dependencies
|
|
pub fn run(path: &Path, verbose: u8) -> Result<()> {
|
|
let timer = tracking::TimedExecution::start();
|
|
|
|
let dir = if path.is_file() {
|
|
path.parent().unwrap_or(Path::new("."))
|
|
} else {
|
|
path
|
|
};
|
|
|
|
if verbose > 0 {
|
|
eprintln!("Scanning dependencies in: {}", dir.display());
|
|
}
|
|
|
|
let mut found = false;
|
|
let mut rtk = String::new();
|
|
let mut raw = String::new();
|
|
|
|
let cargo_path = dir.join("Cargo.toml");
|
|
if cargo_path.exists() {
|
|
found = true;
|
|
raw.push_str(&fs::read_to_string(&cargo_path).unwrap_or_default());
|
|
rtk.push_str("Rust (Cargo.toml):\n");
|
|
rtk.push_str(&summarize_cargo_str(&cargo_path)?);
|
|
}
|
|
|
|
let package_path = dir.join("package.json");
|
|
if package_path.exists() {
|
|
found = true;
|
|
raw.push_str(&fs::read_to_string(&package_path).unwrap_or_default());
|
|
rtk.push_str("Node.js (package.json):\n");
|
|
rtk.push_str(&summarize_package_json_str(&package_path)?);
|
|
}
|
|
|
|
let requirements_path = dir.join("requirements.txt");
|
|
if requirements_path.exists() {
|
|
found = true;
|
|
raw.push_str(&fs::read_to_string(&requirements_path).unwrap_or_default());
|
|
rtk.push_str("Python (requirements.txt):\n");
|
|
rtk.push_str(&summarize_requirements_str(&requirements_path)?);
|
|
}
|
|
|
|
let pyproject_path = dir.join("pyproject.toml");
|
|
if pyproject_path.exists() {
|
|
found = true;
|
|
raw.push_str(&fs::read_to_string(&pyproject_path).unwrap_or_default());
|
|
rtk.push_str("Python (pyproject.toml):\n");
|
|
rtk.push_str(&summarize_pyproject_str(&pyproject_path)?);
|
|
}
|
|
|
|
let gomod_path = dir.join("go.mod");
|
|
if gomod_path.exists() {
|
|
found = true;
|
|
raw.push_str(&fs::read_to_string(&gomod_path).unwrap_or_default());
|
|
rtk.push_str("Go (go.mod):\n");
|
|
rtk.push_str(&summarize_gomod_str(&gomod_path)?);
|
|
}
|
|
|
|
if !found {
|
|
rtk.push_str(&format!("No dependency files found in {}", dir.display()));
|
|
}
|
|
|
|
print!("{}", rtk);
|
|
timer.track("cat */deps", "rtk deps", &raw, &rtk);
|
|
Ok(())
|
|
}
|
|
|
|
fn summarize_cargo_str(path: &Path) -> Result<String> {
|
|
let content = fs::read_to_string(path)?;
|
|
let dep_re =
|
|
Regex::new(r#"^([a-zA-Z0-9_-]+)\s*=\s*(?:"([^"]+)"|.*version\s*=\s*"([^"]+)")"#).unwrap();
|
|
let section_re = Regex::new(r"^\[([^\]]+)\]").unwrap();
|
|
let mut current_section = String::new();
|
|
let mut deps = Vec::new();
|
|
let mut dev_deps = Vec::new();
|
|
let mut out = String::new();
|
|
|
|
for line in content.lines() {
|
|
if let Some(caps) = section_re.captures(line) {
|
|
current_section = caps
|
|
.get(1)
|
|
.map(|m| m.as_str().to_string())
|
|
.unwrap_or_default();
|
|
} else if let Some(caps) = dep_re.captures(line) {
|
|
let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
|
|
let version = caps
|
|
.get(2)
|
|
.or(caps.get(3))
|
|
.map(|m| m.as_str())
|
|
.unwrap_or("*");
|
|
let dep = format!("{} ({})", name, version);
|
|
match current_section.as_str() {
|
|
"dependencies" => deps.push(dep),
|
|
"dev-dependencies" => dev_deps.push(dep),
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !deps.is_empty() {
|
|
out.push_str(&format!(" Dependencies ({}):\n", deps.len()));
|
|
for d in deps.iter().take(10) {
|
|
out.push_str(&format!(" {}\n", d));
|
|
}
|
|
if deps.len() > 10 {
|
|
out.push_str(&format!(" ... +{} more\n", deps.len() - 10));
|
|
}
|
|
}
|
|
if !dev_deps.is_empty() {
|
|
out.push_str(&format!(" Dev ({}):\n", dev_deps.len()));
|
|
for d in dev_deps.iter().take(5) {
|
|
out.push_str(&format!(" {}\n", d));
|
|
}
|
|
if dev_deps.len() > 5 {
|
|
out.push_str(&format!(" ... +{} more\n", dev_deps.len() - 5));
|
|
}
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
fn summarize_package_json_str(path: &Path) -> Result<String> {
|
|
let content = fs::read_to_string(path)?;
|
|
let json: serde_json::Value = serde_json::from_str(&content)?;
|
|
let mut out = String::new();
|
|
|
|
if let Some(name) = json.get("name").and_then(|v| v.as_str()) {
|
|
let version = json.get("version").and_then(|v| v.as_str()).unwrap_or("?");
|
|
out.push_str(&format!(" {} @ {}\n", name, version));
|
|
}
|
|
if let Some(deps) = json.get("dependencies").and_then(|v| v.as_object()) {
|
|
out.push_str(&format!(" Dependencies ({}):\n", deps.len()));
|
|
for (i, (name, version)) in deps.iter().enumerate() {
|
|
if i >= 10 {
|
|
out.push_str(&format!(" ... +{} more\n", deps.len() - 10));
|
|
break;
|
|
}
|
|
out.push_str(&format!(
|
|
" {} ({})\n",
|
|
name,
|
|
version.as_str().unwrap_or("*")
|
|
));
|
|
}
|
|
}
|
|
if let Some(dev_deps) = json.get("devDependencies").and_then(|v| v.as_object()) {
|
|
out.push_str(&format!(" Dev Dependencies ({}):\n", dev_deps.len()));
|
|
for (i, (name, _)) in dev_deps.iter().enumerate() {
|
|
if i >= 5 {
|
|
out.push_str(&format!(" ... +{} more\n", dev_deps.len() - 5));
|
|
break;
|
|
}
|
|
out.push_str(&format!(" {}\n", name));
|
|
}
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
fn summarize_requirements_str(path: &Path) -> Result<String> {
|
|
let content = fs::read_to_string(path)?;
|
|
let dep_re = Regex::new(r"^([a-zA-Z0-9_-]+)([=<>!~]+.*)?$").unwrap();
|
|
let mut deps = Vec::new();
|
|
let mut out = String::new();
|
|
|
|
for line in content.lines() {
|
|
let line = line.trim();
|
|
if line.is_empty() || line.starts_with('#') {
|
|
continue;
|
|
}
|
|
if let Some(caps) = dep_re.captures(line) {
|
|
let name = caps.get(1).map(|m| m.as_str()).unwrap_or("");
|
|
let version = caps.get(2).map(|m| m.as_str()).unwrap_or("");
|
|
deps.push(format!("{}{}", name, version));
|
|
}
|
|
}
|
|
|
|
out.push_str(&format!(" Packages ({}):\n", deps.len()));
|
|
for d in deps.iter().take(15) {
|
|
out.push_str(&format!(" {}\n", d));
|
|
}
|
|
if deps.len() > 15 {
|
|
out.push_str(&format!(" ... +{} more\n", deps.len() - 15));
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
fn summarize_pyproject_str(path: &Path) -> Result<String> {
|
|
let content = fs::read_to_string(path)?;
|
|
let mut in_deps = false;
|
|
let mut deps = Vec::new();
|
|
let mut out = String::new();
|
|
|
|
for line in content.lines() {
|
|
if line.contains("dependencies") && line.contains("[") {
|
|
in_deps = true;
|
|
continue;
|
|
}
|
|
if in_deps {
|
|
if line.trim() == "]" {
|
|
break;
|
|
}
|
|
let line = line
|
|
.trim()
|
|
.trim_matches(|c| c == '"' || c == '\'' || c == ',');
|
|
if !line.is_empty() {
|
|
deps.push(line.to_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
if !deps.is_empty() {
|
|
out.push_str(&format!(" Dependencies ({}):\n", deps.len()));
|
|
for d in deps.iter().take(10) {
|
|
out.push_str(&format!(" {}\n", d));
|
|
}
|
|
if deps.len() > 10 {
|
|
out.push_str(&format!(" ... +{} more\n", deps.len() - 10));
|
|
}
|
|
}
|
|
Ok(out)
|
|
}
|
|
|
|
fn summarize_gomod_str(path: &Path) -> Result<String> {
|
|
let content = fs::read_to_string(path)?;
|
|
let mut module_name = String::new();
|
|
let mut go_version = String::new();
|
|
let mut deps = Vec::new();
|
|
let mut in_require = false;
|
|
let mut out = String::new();
|
|
|
|
for line in content.lines() {
|
|
let line = line.trim();
|
|
if line.starts_with("module ") {
|
|
module_name = line.trim_start_matches("module ").to_string();
|
|
} else if line.starts_with("go ") {
|
|
go_version = line.trim_start_matches("go ").to_string();
|
|
} else if line == "require (" {
|
|
in_require = true;
|
|
} else if line == ")" {
|
|
in_require = false;
|
|
} else if in_require && !line.starts_with("//") {
|
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
|
if parts.len() >= 2 {
|
|
deps.push(format!("{} {}", parts[0], parts[1]));
|
|
}
|
|
} else if line.starts_with("require ") && !line.contains("(") {
|
|
deps.push(line.trim_start_matches("require ").to_string());
|
|
}
|
|
}
|
|
|
|
if !module_name.is_empty() {
|
|
out.push_str(&format!(" {} (go {})\n", module_name, go_version));
|
|
}
|
|
if !deps.is_empty() {
|
|
out.push_str(&format!(" Dependencies ({}):\n", deps.len()));
|
|
for d in deps.iter().take(10) {
|
|
out.push_str(&format!(" {}\n", d));
|
|
}
|
|
if deps.len() > 10 {
|
|
out.push_str(&format!(" ... +{} more\n", deps.len() - 10));
|
|
}
|
|
}
|
|
Ok(out)
|
|
}
|