@hasna/terminal 2.3.0 → 2.3.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/dist/cli.js +64 -16
- package/package.json +1 -1
- package/src/ai.ts +8 -0
- package/src/cli.tsx +57 -18
- package/src/output-processor.ts +6 -1
- package/src/output-store.ts +58 -12
- package/src/tool-profiles.ts +139 -0
- package/temp/rtk/.claude/agents/code-reviewer.md +0 -221
- package/temp/rtk/.claude/agents/debugger.md +0 -519
- package/temp/rtk/.claude/agents/rtk-testing-specialist.md +0 -461
- package/temp/rtk/.claude/agents/rust-rtk.md +0 -511
- package/temp/rtk/.claude/agents/technical-writer.md +0 -355
- package/temp/rtk/.claude/commands/diagnose.md +0 -352
- package/temp/rtk/.claude/commands/test-routing.md +0 -362
- package/temp/rtk/.claude/hooks/bash/pre-commit-format.sh +0 -16
- package/temp/rtk/.claude/hooks/rtk-rewrite.sh +0 -70
- package/temp/rtk/.claude/hooks/rtk-suggest.sh +0 -152
- package/temp/rtk/.claude/rules/cli-testing.md +0 -526
- package/temp/rtk/.claude/skills/issue-triage/SKILL.md +0 -348
- package/temp/rtk/.claude/skills/issue-triage/templates/issue-comment.md +0 -134
- package/temp/rtk/.claude/skills/performance.md +0 -435
- package/temp/rtk/.claude/skills/pr-triage/SKILL.md +0 -315
- package/temp/rtk/.claude/skills/pr-triage/templates/review-comment.md +0 -71
- package/temp/rtk/.claude/skills/repo-recap.md +0 -206
- package/temp/rtk/.claude/skills/rtk-tdd/SKILL.md +0 -78
- package/temp/rtk/.claude/skills/rtk-tdd/references/testing-patterns.md +0 -124
- package/temp/rtk/.claude/skills/security-guardian.md +0 -503
- package/temp/rtk/.claude/skills/ship.md +0 -404
- package/temp/rtk/.github/workflows/benchmark.yml +0 -34
- package/temp/rtk/.github/workflows/dco-check.yaml +0 -12
- package/temp/rtk/.github/workflows/release-please.yml +0 -51
- package/temp/rtk/.github/workflows/release.yml +0 -343
- package/temp/rtk/.github/workflows/security-check.yml +0 -135
- package/temp/rtk/.github/workflows/validate-docs.yml +0 -78
- package/temp/rtk/.release-please-manifest.json +0 -3
- package/temp/rtk/ARCHITECTURE.md +0 -1491
- package/temp/rtk/CHANGELOG.md +0 -640
- package/temp/rtk/CLAUDE.md +0 -605
- package/temp/rtk/CONTRIBUTING.md +0 -199
- package/temp/rtk/Cargo.lock +0 -1668
- package/temp/rtk/Cargo.toml +0 -64
- package/temp/rtk/Formula/rtk.rb +0 -43
- package/temp/rtk/INSTALL.md +0 -390
- package/temp/rtk/LICENSE +0 -21
- package/temp/rtk/README.md +0 -386
- package/temp/rtk/README_es.md +0 -159
- package/temp/rtk/README_fr.md +0 -197
- package/temp/rtk/README_ja.md +0 -159
- package/temp/rtk/README_ko.md +0 -159
- package/temp/rtk/README_zh.md +0 -167
- package/temp/rtk/ROADMAP.md +0 -15
- package/temp/rtk/SECURITY.md +0 -217
- package/temp/rtk/TEST_EXEC_TIME.md +0 -102
- package/temp/rtk/build.rs +0 -57
- package/temp/rtk/docs/AUDIT_GUIDE.md +0 -432
- package/temp/rtk/docs/FEATURES.md +0 -1410
- package/temp/rtk/docs/TROUBLESHOOTING.md +0 -309
- package/temp/rtk/docs/filter-workflow.md +0 -102
- package/temp/rtk/docs/images/gain-dashboard.jpg +0 -0
- package/temp/rtk/docs/tracking.md +0 -583
- package/temp/rtk/hooks/opencode-rtk.ts +0 -39
- package/temp/rtk/hooks/rtk-awareness.md +0 -29
- package/temp/rtk/hooks/rtk-rewrite.sh +0 -61
- package/temp/rtk/hooks/test-rtk-rewrite.sh +0 -442
- package/temp/rtk/install.sh +0 -124
- package/temp/rtk/release-please-config.json +0 -10
- package/temp/rtk/scripts/benchmark.sh +0 -592
- package/temp/rtk/scripts/check-installation.sh +0 -162
- package/temp/rtk/scripts/install-local.sh +0 -37
- package/temp/rtk/scripts/rtk-economics.sh +0 -137
- package/temp/rtk/scripts/test-all.sh +0 -561
- package/temp/rtk/scripts/test-aristote.sh +0 -227
- package/temp/rtk/scripts/test-tracking.sh +0 -79
- package/temp/rtk/scripts/update-readme-metrics.sh +0 -32
- package/temp/rtk/scripts/validate-docs.sh +0 -73
- package/temp/rtk/src/aws_cmd.rs +0 -880
- package/temp/rtk/src/binlog.rs +0 -1645
- package/temp/rtk/src/cargo_cmd.rs +0 -1727
- package/temp/rtk/src/cc_economics.rs +0 -1157
- package/temp/rtk/src/ccusage.rs +0 -340
- package/temp/rtk/src/config.rs +0 -187
- package/temp/rtk/src/container.rs +0 -855
- package/temp/rtk/src/curl_cmd.rs +0 -134
- package/temp/rtk/src/deps.rs +0 -268
- package/temp/rtk/src/diff_cmd.rs +0 -367
- package/temp/rtk/src/discover/mod.rs +0 -274
- package/temp/rtk/src/discover/provider.rs +0 -388
- package/temp/rtk/src/discover/registry.rs +0 -2022
- package/temp/rtk/src/discover/report.rs +0 -202
- package/temp/rtk/src/discover/rules.rs +0 -667
- package/temp/rtk/src/display_helpers.rs +0 -402
- package/temp/rtk/src/dotnet_cmd.rs +0 -1771
- package/temp/rtk/src/dotnet_format_report.rs +0 -133
- package/temp/rtk/src/dotnet_trx.rs +0 -593
- package/temp/rtk/src/env_cmd.rs +0 -204
- package/temp/rtk/src/filter.rs +0 -462
- package/temp/rtk/src/filters/README.md +0 -52
- package/temp/rtk/src/filters/ansible-playbook.toml +0 -34
- package/temp/rtk/src/filters/basedpyright.toml +0 -47
- package/temp/rtk/src/filters/biome.toml +0 -45
- package/temp/rtk/src/filters/brew-install.toml +0 -37
- package/temp/rtk/src/filters/composer-install.toml +0 -40
- package/temp/rtk/src/filters/df.toml +0 -16
- package/temp/rtk/src/filters/dotnet-build.toml +0 -64
- package/temp/rtk/src/filters/du.toml +0 -16
- package/temp/rtk/src/filters/fail2ban-client.toml +0 -15
- package/temp/rtk/src/filters/gcc.toml +0 -49
- package/temp/rtk/src/filters/gcloud.toml +0 -22
- package/temp/rtk/src/filters/hadolint.toml +0 -24
- package/temp/rtk/src/filters/helm.toml +0 -29
- package/temp/rtk/src/filters/iptables.toml +0 -27
- package/temp/rtk/src/filters/jj.toml +0 -28
- package/temp/rtk/src/filters/jq.toml +0 -24
- package/temp/rtk/src/filters/make.toml +0 -41
- package/temp/rtk/src/filters/markdownlint.toml +0 -24
- package/temp/rtk/src/filters/mix-compile.toml +0 -27
- package/temp/rtk/src/filters/mix-format.toml +0 -15
- package/temp/rtk/src/filters/mvn-build.toml +0 -44
- package/temp/rtk/src/filters/oxlint.toml +0 -43
- package/temp/rtk/src/filters/ping.toml +0 -63
- package/temp/rtk/src/filters/pio-run.toml +0 -40
- package/temp/rtk/src/filters/poetry-install.toml +0 -50
- package/temp/rtk/src/filters/pre-commit.toml +0 -35
- package/temp/rtk/src/filters/ps.toml +0 -16
- package/temp/rtk/src/filters/quarto-render.toml +0 -41
- package/temp/rtk/src/filters/rsync.toml +0 -48
- package/temp/rtk/src/filters/shellcheck.toml +0 -27
- package/temp/rtk/src/filters/shopify-theme.toml +0 -29
- package/temp/rtk/src/filters/skopeo.toml +0 -45
- package/temp/rtk/src/filters/sops.toml +0 -16
- package/temp/rtk/src/filters/ssh.toml +0 -44
- package/temp/rtk/src/filters/stat.toml +0 -34
- package/temp/rtk/src/filters/swift-build.toml +0 -41
- package/temp/rtk/src/filters/systemctl-status.toml +0 -33
- package/temp/rtk/src/filters/terraform-plan.toml +0 -35
- package/temp/rtk/src/filters/tofu-fmt.toml +0 -16
- package/temp/rtk/src/filters/tofu-init.toml +0 -38
- package/temp/rtk/src/filters/tofu-plan.toml +0 -35
- package/temp/rtk/src/filters/tofu-validate.toml +0 -17
- package/temp/rtk/src/filters/trunk-build.toml +0 -39
- package/temp/rtk/src/filters/ty.toml +0 -50
- package/temp/rtk/src/filters/uv-sync.toml +0 -37
- package/temp/rtk/src/filters/xcodebuild.toml +0 -99
- package/temp/rtk/src/filters/yamllint.toml +0 -25
- package/temp/rtk/src/find_cmd.rs +0 -598
- package/temp/rtk/src/format_cmd.rs +0 -386
- package/temp/rtk/src/gain.rs +0 -723
- package/temp/rtk/src/gh_cmd.rs +0 -1651
- package/temp/rtk/src/git.rs +0 -2012
- package/temp/rtk/src/go_cmd.rs +0 -592
- package/temp/rtk/src/golangci_cmd.rs +0 -254
- package/temp/rtk/src/grep_cmd.rs +0 -288
- package/temp/rtk/src/gt_cmd.rs +0 -810
- package/temp/rtk/src/hook_audit_cmd.rs +0 -283
- package/temp/rtk/src/hook_check.rs +0 -171
- package/temp/rtk/src/init.rs +0 -1859
- package/temp/rtk/src/integrity.rs +0 -537
- package/temp/rtk/src/json_cmd.rs +0 -231
- package/temp/rtk/src/learn/detector.rs +0 -628
- package/temp/rtk/src/learn/mod.rs +0 -119
- package/temp/rtk/src/learn/report.rs +0 -184
- package/temp/rtk/src/lint_cmd.rs +0 -694
- package/temp/rtk/src/local_llm.rs +0 -316
- package/temp/rtk/src/log_cmd.rs +0 -248
- package/temp/rtk/src/ls.rs +0 -324
- package/temp/rtk/src/main.rs +0 -2482
- package/temp/rtk/src/mypy_cmd.rs +0 -389
- package/temp/rtk/src/next_cmd.rs +0 -241
- package/temp/rtk/src/npm_cmd.rs +0 -236
- package/temp/rtk/src/parser/README.md +0 -267
- package/temp/rtk/src/parser/error.rs +0 -46
- package/temp/rtk/src/parser/formatter.rs +0 -336
- package/temp/rtk/src/parser/mod.rs +0 -311
- package/temp/rtk/src/parser/types.rs +0 -119
- package/temp/rtk/src/pip_cmd.rs +0 -302
- package/temp/rtk/src/playwright_cmd.rs +0 -479
- package/temp/rtk/src/pnpm_cmd.rs +0 -573
- package/temp/rtk/src/prettier_cmd.rs +0 -221
- package/temp/rtk/src/prisma_cmd.rs +0 -482
- package/temp/rtk/src/psql_cmd.rs +0 -382
- package/temp/rtk/src/pytest_cmd.rs +0 -384
- package/temp/rtk/src/read.rs +0 -217
- package/temp/rtk/src/rewrite_cmd.rs +0 -50
- package/temp/rtk/src/ruff_cmd.rs +0 -402
- package/temp/rtk/src/runner.rs +0 -271
- package/temp/rtk/src/summary.rs +0 -297
- package/temp/rtk/src/tee.rs +0 -405
- package/temp/rtk/src/telemetry.rs +0 -248
- package/temp/rtk/src/toml_filter.rs +0 -1655
- package/temp/rtk/src/tracking.rs +0 -1416
- package/temp/rtk/src/tree.rs +0 -209
- package/temp/rtk/src/tsc_cmd.rs +0 -259
- package/temp/rtk/src/utils.rs +0 -432
- package/temp/rtk/src/verify_cmd.rs +0 -47
- package/temp/rtk/src/vitest_cmd.rs +0 -385
- package/temp/rtk/src/wc_cmd.rs +0 -401
- package/temp/rtk/src/wget_cmd.rs +0 -260
- package/temp/rtk/tests/fixtures/dotnet/build_failed.txt +0 -11
- package/temp/rtk/tests/fixtures/dotnet/format_changes.json +0 -31
- package/temp/rtk/tests/fixtures/dotnet/format_empty.json +0 -1
- package/temp/rtk/tests/fixtures/dotnet/format_success.json +0 -12
- package/temp/rtk/tests/fixtures/dotnet/test_failed.txt +0 -18
package/temp/rtk/src/ruff_cmd.rs
DELETED
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
use crate::tracking;
|
|
2
|
-
use crate::utils::truncate;
|
|
3
|
-
use anyhow::{Context, Result};
|
|
4
|
-
use serde::Deserialize;
|
|
5
|
-
use std::collections::HashMap;
|
|
6
|
-
use std::process::Command;
|
|
7
|
-
|
|
8
|
-
#[derive(Debug, Deserialize)]
|
|
9
|
-
struct RuffLocation {
|
|
10
|
-
row: usize,
|
|
11
|
-
column: usize,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
#[derive(Debug, Deserialize)]
|
|
15
|
-
struct RuffFix {
|
|
16
|
-
#[allow(dead_code)]
|
|
17
|
-
applicability: Option<String>,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
#[derive(Debug, Deserialize)]
|
|
21
|
-
struct RuffDiagnostic {
|
|
22
|
-
code: String,
|
|
23
|
-
message: String,
|
|
24
|
-
location: RuffLocation,
|
|
25
|
-
#[allow(dead_code)]
|
|
26
|
-
end_location: Option<RuffLocation>,
|
|
27
|
-
filename: String,
|
|
28
|
-
fix: Option<RuffFix>,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
pub fn run(args: &[String], verbose: u8) -> Result<()> {
|
|
32
|
-
let timer = tracking::TimedExecution::start();
|
|
33
|
-
|
|
34
|
-
// Detect subcommand: check, format, or version
|
|
35
|
-
let is_check = args.is_empty()
|
|
36
|
-
|| args[0] == "check"
|
|
37
|
-
|| (!args[0].starts_with('-') && args[0] != "format" && args[0] != "version");
|
|
38
|
-
|
|
39
|
-
let is_format = args.iter().any(|a| a == "format");
|
|
40
|
-
|
|
41
|
-
let mut cmd = Command::new("ruff");
|
|
42
|
-
|
|
43
|
-
if is_check {
|
|
44
|
-
// Force JSON output for check command
|
|
45
|
-
if !args.contains(&"--output-format".to_string()) {
|
|
46
|
-
cmd.arg("check").arg("--output-format=json");
|
|
47
|
-
} else {
|
|
48
|
-
cmd.arg("check");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Add user arguments (skip "check" if it was the first arg)
|
|
52
|
-
let start_idx = if !args.is_empty() && args[0] == "check" {
|
|
53
|
-
1
|
|
54
|
-
} else {
|
|
55
|
-
0
|
|
56
|
-
};
|
|
57
|
-
for arg in &args[start_idx..] {
|
|
58
|
-
cmd.arg(arg);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Default to current directory if no path specified
|
|
62
|
-
if args
|
|
63
|
-
.iter()
|
|
64
|
-
.skip(start_idx)
|
|
65
|
-
.all(|a| a.starts_with('-') || a.contains('='))
|
|
66
|
-
{
|
|
67
|
-
cmd.arg(".");
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
// Format or other commands - pass through
|
|
71
|
-
for arg in args {
|
|
72
|
-
cmd.arg(arg);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if verbose > 0 {
|
|
77
|
-
eprintln!("Running: ruff {}", args.join(" "));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let output = cmd
|
|
81
|
-
.output()
|
|
82
|
-
.context("Failed to run ruff. Is it installed? Try: pip install ruff")?;
|
|
83
|
-
|
|
84
|
-
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
85
|
-
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
86
|
-
let raw = format!("{}\n{}", stdout, stderr);
|
|
87
|
-
|
|
88
|
-
let filtered = if is_check && !stdout.trim().is_empty() {
|
|
89
|
-
filter_ruff_check_json(&stdout)
|
|
90
|
-
} else if is_format {
|
|
91
|
-
filter_ruff_format(&raw)
|
|
92
|
-
} else {
|
|
93
|
-
// Fallback for other commands (version, etc.)
|
|
94
|
-
raw.trim().to_string()
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
println!("{}", filtered);
|
|
98
|
-
|
|
99
|
-
timer.track(
|
|
100
|
-
&format!("ruff {}", args.join(" ")),
|
|
101
|
-
&format!("rtk ruff {}", args.join(" ")),
|
|
102
|
-
&raw,
|
|
103
|
-
&filtered,
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
// Preserve exit code for CI/CD
|
|
107
|
-
if !output.status.success() {
|
|
108
|
-
std::process::exit(output.status.code().unwrap_or(1));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
Ok(())
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/// Filter ruff check JSON output - group by rule and file
|
|
115
|
-
pub fn filter_ruff_check_json(output: &str) -> String {
|
|
116
|
-
let diagnostics: Result<Vec<RuffDiagnostic>, _> = serde_json::from_str(output);
|
|
117
|
-
|
|
118
|
-
let diagnostics = match diagnostics {
|
|
119
|
-
Ok(d) => d,
|
|
120
|
-
Err(e) => {
|
|
121
|
-
// Fallback if JSON parsing fails
|
|
122
|
-
return format!(
|
|
123
|
-
"Ruff check (JSON parse failed: {})\n{}",
|
|
124
|
-
e,
|
|
125
|
-
truncate(output, 500)
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
if diagnostics.is_empty() {
|
|
131
|
-
return "✓ Ruff: No issues found".to_string();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
let total_issues = diagnostics.len();
|
|
135
|
-
let fixable_count = diagnostics.iter().filter(|d| d.fix.is_some()).count();
|
|
136
|
-
|
|
137
|
-
// Count unique files
|
|
138
|
-
let unique_files: std::collections::HashSet<_> =
|
|
139
|
-
diagnostics.iter().map(|d| &d.filename).collect();
|
|
140
|
-
let total_files = unique_files.len();
|
|
141
|
-
|
|
142
|
-
// Group by rule code
|
|
143
|
-
let mut by_rule: HashMap<String, usize> = HashMap::new();
|
|
144
|
-
for diag in &diagnostics {
|
|
145
|
-
*by_rule.entry(diag.code.clone()).or_insert(0) += 1;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Group by file
|
|
149
|
-
let mut by_file: HashMap<&str, usize> = HashMap::new();
|
|
150
|
-
for diag in &diagnostics {
|
|
151
|
-
*by_file.entry(&diag.filename).or_insert(0) += 1;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
let mut file_counts: Vec<_> = by_file.iter().collect();
|
|
155
|
-
file_counts.sort_by(|a, b| b.1.cmp(a.1));
|
|
156
|
-
|
|
157
|
-
// Build output
|
|
158
|
-
let mut result = String::new();
|
|
159
|
-
result.push_str(&format!(
|
|
160
|
-
"Ruff: {} issues in {} files",
|
|
161
|
-
total_issues, total_files
|
|
162
|
-
));
|
|
163
|
-
|
|
164
|
-
if fixable_count > 0 {
|
|
165
|
-
result.push_str(&format!(" ({} fixable)", fixable_count));
|
|
166
|
-
}
|
|
167
|
-
result.push('\n');
|
|
168
|
-
result.push_str("═══════════════════════════════════════\n");
|
|
169
|
-
|
|
170
|
-
// Show top rules
|
|
171
|
-
let mut rule_counts: Vec<_> = by_rule.iter().collect();
|
|
172
|
-
rule_counts.sort_by(|a, b| b.1.cmp(a.1));
|
|
173
|
-
|
|
174
|
-
if !rule_counts.is_empty() {
|
|
175
|
-
result.push_str("Top rules:\n");
|
|
176
|
-
for (rule, count) in rule_counts.iter().take(10) {
|
|
177
|
-
result.push_str(&format!(" {} ({}x)\n", rule, count));
|
|
178
|
-
}
|
|
179
|
-
result.push('\n');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Show top files
|
|
183
|
-
result.push_str("Top files:\n");
|
|
184
|
-
for (file, count) in file_counts.iter().take(10) {
|
|
185
|
-
let short_path = compact_path(file);
|
|
186
|
-
result.push_str(&format!(" {} ({} issues)\n", short_path, count));
|
|
187
|
-
|
|
188
|
-
// Show top 3 rules in this file
|
|
189
|
-
let mut file_rules: HashMap<String, usize> = HashMap::new();
|
|
190
|
-
for diag in diagnostics.iter().filter(|d| &d.filename == *file) {
|
|
191
|
-
*file_rules.entry(diag.code.clone()).or_insert(0) += 1;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
let mut file_rule_counts: Vec<_> = file_rules.iter().collect();
|
|
195
|
-
file_rule_counts.sort_by(|a, b| b.1.cmp(a.1));
|
|
196
|
-
|
|
197
|
-
for (rule, count) in file_rule_counts.iter().take(3) {
|
|
198
|
-
result.push_str(&format!(" {} ({})\n", rule, count));
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if file_counts.len() > 10 {
|
|
203
|
-
result.push_str(&format!("\n... +{} more files\n", file_counts.len() - 10));
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if fixable_count > 0 {
|
|
207
|
-
result.push_str(&format!(
|
|
208
|
-
"\n💡 Run `ruff check --fix` to auto-fix {} issues\n",
|
|
209
|
-
fixable_count
|
|
210
|
-
));
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
result.trim().to_string()
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/// Filter ruff format output - show files that need formatting
|
|
217
|
-
pub fn filter_ruff_format(output: &str) -> String {
|
|
218
|
-
let mut files_to_format: Vec<String> = Vec::new();
|
|
219
|
-
let mut files_checked = 0;
|
|
220
|
-
|
|
221
|
-
for line in output.lines() {
|
|
222
|
-
let trimmed = line.trim();
|
|
223
|
-
let lower = trimmed.to_lowercase();
|
|
224
|
-
|
|
225
|
-
// Count "would reformat" lines (check mode) - case insensitive
|
|
226
|
-
if lower.contains("would reformat:") {
|
|
227
|
-
// Extract filename from "Would reformat: path/to/file.py"
|
|
228
|
-
if let Some(filename) = trimmed.split(':').nth(1) {
|
|
229
|
-
files_to_format.push(filename.trim().to_string());
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Count total checked files - look for patterns like "3 files left unchanged"
|
|
234
|
-
if lower.contains("left unchanged") {
|
|
235
|
-
// Find "X file(s) left unchanged" pattern specifically
|
|
236
|
-
// Split by comma to handle "2 files would be reformatted, 3 files left unchanged"
|
|
237
|
-
let parts: Vec<&str> = trimmed.split(',').collect();
|
|
238
|
-
for part in parts {
|
|
239
|
-
let part_lower = part.to_lowercase();
|
|
240
|
-
if part_lower.contains("left unchanged") {
|
|
241
|
-
let words: Vec<&str> = part.trim().split_whitespace().collect();
|
|
242
|
-
// Look for number before "file" or "files"
|
|
243
|
-
for (i, word) in words.iter().enumerate() {
|
|
244
|
-
if (word == &"file" || word == &"files") && i > 0 {
|
|
245
|
-
if let Ok(count) = words[i - 1].parse::<usize>() {
|
|
246
|
-
files_checked = count;
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
let output_lower = output.to_lowercase();
|
|
258
|
-
|
|
259
|
-
// Check if all files are formatted
|
|
260
|
-
if files_to_format.is_empty() && output_lower.contains("left unchanged") {
|
|
261
|
-
return "✓ Ruff format: All files formatted correctly".to_string();
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
let mut result = String::new();
|
|
265
|
-
|
|
266
|
-
if output_lower.contains("would reformat") {
|
|
267
|
-
// Check mode: show files that need formatting
|
|
268
|
-
if files_to_format.is_empty() {
|
|
269
|
-
result.push_str("✓ Ruff format: All files formatted correctly\n");
|
|
270
|
-
} else {
|
|
271
|
-
result.push_str(&format!(
|
|
272
|
-
"Ruff format: {} files need formatting\n",
|
|
273
|
-
files_to_format.len()
|
|
274
|
-
));
|
|
275
|
-
result.push_str("═══════════════════════════════════════\n");
|
|
276
|
-
|
|
277
|
-
for (i, file) in files_to_format.iter().take(10).enumerate() {
|
|
278
|
-
result.push_str(&format!("{}. {}\n", i + 1, compact_path(file)));
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if files_to_format.len() > 10 {
|
|
282
|
-
result.push_str(&format!(
|
|
283
|
-
"\n... +{} more files\n",
|
|
284
|
-
files_to_format.len() - 10
|
|
285
|
-
));
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if files_checked > 0 {
|
|
289
|
-
result.push_str(&format!("\n✓ {} files already formatted\n", files_checked));
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
result.push_str("\n💡 Run `ruff format` to format these files\n");
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
// Write mode or other output - show summary
|
|
296
|
-
result.push_str(output.trim());
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
result.trim().to_string()
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/// Compact file path (remove common prefixes)
|
|
303
|
-
fn compact_path(path: &str) -> String {
|
|
304
|
-
let path = path.replace('\\', "/");
|
|
305
|
-
|
|
306
|
-
if let Some(pos) = path.rfind("/src/") {
|
|
307
|
-
format!("src/{}", &path[pos + 5..])
|
|
308
|
-
} else if let Some(pos) = path.rfind("/lib/") {
|
|
309
|
-
format!("lib/{}", &path[pos + 5..])
|
|
310
|
-
} else if let Some(pos) = path.rfind("/tests/") {
|
|
311
|
-
format!("tests/{}", &path[pos + 7..])
|
|
312
|
-
} else if let Some(pos) = path.rfind('/') {
|
|
313
|
-
path[pos + 1..].to_string()
|
|
314
|
-
} else {
|
|
315
|
-
path
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
#[cfg(test)]
|
|
320
|
-
mod tests {
|
|
321
|
-
use super::*;
|
|
322
|
-
|
|
323
|
-
#[test]
|
|
324
|
-
fn test_filter_ruff_check_no_issues() {
|
|
325
|
-
let output = "[]";
|
|
326
|
-
let result = filter_ruff_check_json(output);
|
|
327
|
-
assert!(result.contains("✓ Ruff"));
|
|
328
|
-
assert!(result.contains("No issues found"));
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
#[test]
|
|
332
|
-
fn test_filter_ruff_check_with_issues() {
|
|
333
|
-
let output = r#"[
|
|
334
|
-
{
|
|
335
|
-
"code": "F401",
|
|
336
|
-
"message": "`os` imported but unused",
|
|
337
|
-
"location": {"row": 1, "column": 8},
|
|
338
|
-
"end_location": {"row": 1, "column": 10},
|
|
339
|
-
"filename": "src/main.py",
|
|
340
|
-
"fix": {"applicability": "safe"}
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
"code": "F401",
|
|
344
|
-
"message": "`sys` imported but unused",
|
|
345
|
-
"location": {"row": 2, "column": 8},
|
|
346
|
-
"end_location": {"row": 2, "column": 11},
|
|
347
|
-
"filename": "src/main.py",
|
|
348
|
-
"fix": null
|
|
349
|
-
},
|
|
350
|
-
{
|
|
351
|
-
"code": "E501",
|
|
352
|
-
"message": "Line too long (100 > 88 characters)",
|
|
353
|
-
"location": {"row": 10, "column": 89},
|
|
354
|
-
"end_location": {"row": 10, "column": 100},
|
|
355
|
-
"filename": "src/utils.py",
|
|
356
|
-
"fix": null
|
|
357
|
-
}
|
|
358
|
-
]"#;
|
|
359
|
-
let result = filter_ruff_check_json(output);
|
|
360
|
-
assert!(result.contains("3 issues"));
|
|
361
|
-
assert!(result.contains("2 files"));
|
|
362
|
-
assert!(result.contains("1 fixable"));
|
|
363
|
-
assert!(result.contains("F401"));
|
|
364
|
-
assert!(result.contains("E501"));
|
|
365
|
-
assert!(result.contains("main.py"));
|
|
366
|
-
assert!(result.contains("utils.py"));
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
#[test]
|
|
370
|
-
fn test_filter_ruff_format_all_formatted() {
|
|
371
|
-
let output = "5 files left unchanged";
|
|
372
|
-
let result = filter_ruff_format(output);
|
|
373
|
-
assert!(result.contains("✓ Ruff format"));
|
|
374
|
-
assert!(result.contains("All files formatted correctly"));
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
#[test]
|
|
378
|
-
fn test_filter_ruff_format_needs_formatting() {
|
|
379
|
-
let output = r#"Would reformat: src/main.py
|
|
380
|
-
Would reformat: tests/test_utils.py
|
|
381
|
-
2 files would be reformatted, 3 files left unchanged"#;
|
|
382
|
-
let result = filter_ruff_format(output);
|
|
383
|
-
assert!(result.contains("2 files need formatting"));
|
|
384
|
-
assert!(result.contains("main.py"));
|
|
385
|
-
assert!(result.contains("test_utils.py"));
|
|
386
|
-
assert!(result.contains("3 files already formatted"));
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
#[test]
|
|
390
|
-
fn test_compact_path() {
|
|
391
|
-
assert_eq!(
|
|
392
|
-
compact_path("/Users/foo/project/src/main.py"),
|
|
393
|
-
"src/main.py"
|
|
394
|
-
);
|
|
395
|
-
assert_eq!(compact_path("/home/user/app/lib/utils.py"), "lib/utils.py");
|
|
396
|
-
assert_eq!(
|
|
397
|
-
compact_path("C:\\Users\\foo\\project\\tests\\test.py"),
|
|
398
|
-
"tests/test.py"
|
|
399
|
-
);
|
|
400
|
-
assert_eq!(compact_path("relative/file.py"), "file.py");
|
|
401
|
-
}
|
|
402
|
-
}
|
package/temp/rtk/src/runner.rs
DELETED
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
use crate::tracking;
|
|
2
|
-
use anyhow::{Context, Result};
|
|
3
|
-
use regex::Regex;
|
|
4
|
-
use std::process::{Command, Stdio};
|
|
5
|
-
|
|
6
|
-
/// Run a command and filter output to show only errors/warnings
|
|
7
|
-
pub fn run_err(command: &str, verbose: u8) -> Result<()> {
|
|
8
|
-
let timer = tracking::TimedExecution::start();
|
|
9
|
-
|
|
10
|
-
if verbose > 0 {
|
|
11
|
-
eprintln!("Running: {}", command);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
let output = if cfg!(target_os = "windows") {
|
|
15
|
-
Command::new("cmd")
|
|
16
|
-
.args(["/C", command])
|
|
17
|
-
.stdout(Stdio::piped())
|
|
18
|
-
.stderr(Stdio::piped())
|
|
19
|
-
.output()
|
|
20
|
-
} else {
|
|
21
|
-
Command::new("sh")
|
|
22
|
-
.args(["-c", command])
|
|
23
|
-
.stdout(Stdio::piped())
|
|
24
|
-
.stderr(Stdio::piped())
|
|
25
|
-
.output()
|
|
26
|
-
}
|
|
27
|
-
.context("Failed to execute command")?;
|
|
28
|
-
|
|
29
|
-
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
30
|
-
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
31
|
-
let raw = format!("{}\n{}", stdout, stderr);
|
|
32
|
-
let filtered = filter_errors(&raw);
|
|
33
|
-
let mut rtk = String::new();
|
|
34
|
-
|
|
35
|
-
if filtered.is_empty() {
|
|
36
|
-
if output.status.success() {
|
|
37
|
-
rtk.push_str("✅ Command completed successfully (no errors)");
|
|
38
|
-
} else {
|
|
39
|
-
rtk.push_str(&format!(
|
|
40
|
-
"❌ Command failed (exit code: {:?})\n",
|
|
41
|
-
output.status.code()
|
|
42
|
-
));
|
|
43
|
-
let lines: Vec<&str> = raw.lines().collect();
|
|
44
|
-
for line in lines.iter().rev().take(10).rev() {
|
|
45
|
-
rtk.push_str(&format!(" {}\n", line));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
rtk.push_str(&filtered);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
let exit_code = output
|
|
53
|
-
.status
|
|
54
|
-
.code()
|
|
55
|
-
.unwrap_or(if output.status.success() { 0 } else { 1 });
|
|
56
|
-
if let Some(hint) = crate::tee::tee_and_hint(&raw, "err", exit_code) {
|
|
57
|
-
println!("{}\n{}", rtk, hint);
|
|
58
|
-
} else {
|
|
59
|
-
println!("{}", rtk);
|
|
60
|
-
}
|
|
61
|
-
timer.track(command, "rtk run-err", &raw, &rtk);
|
|
62
|
-
Ok(())
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/// Run tests and show only failures
|
|
66
|
-
pub fn run_test(command: &str, verbose: u8) -> Result<()> {
|
|
67
|
-
let timer = tracking::TimedExecution::start();
|
|
68
|
-
|
|
69
|
-
if verbose > 0 {
|
|
70
|
-
eprintln!("Running tests: {}", command);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
let output = if cfg!(target_os = "windows") {
|
|
74
|
-
Command::new("cmd")
|
|
75
|
-
.args(["/C", command])
|
|
76
|
-
.stdout(Stdio::piped())
|
|
77
|
-
.stderr(Stdio::piped())
|
|
78
|
-
.output()
|
|
79
|
-
} else {
|
|
80
|
-
Command::new("sh")
|
|
81
|
-
.args(["-c", command])
|
|
82
|
-
.stdout(Stdio::piped())
|
|
83
|
-
.stderr(Stdio::piped())
|
|
84
|
-
.output()
|
|
85
|
-
}
|
|
86
|
-
.context("Failed to execute test command")?;
|
|
87
|
-
|
|
88
|
-
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
89
|
-
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
90
|
-
let raw = format!("{}\n{}", stdout, stderr);
|
|
91
|
-
|
|
92
|
-
let exit_code = output
|
|
93
|
-
.status
|
|
94
|
-
.code()
|
|
95
|
-
.unwrap_or(if output.status.success() { 0 } else { 1 });
|
|
96
|
-
let summary = extract_test_summary(&raw, command);
|
|
97
|
-
if let Some(hint) = crate::tee::tee_and_hint(&raw, "test", exit_code) {
|
|
98
|
-
println!("{}\n{}", summary, hint);
|
|
99
|
-
} else {
|
|
100
|
-
println!("{}", summary);
|
|
101
|
-
}
|
|
102
|
-
timer.track(command, "rtk run-test", &raw, &summary);
|
|
103
|
-
Ok(())
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
fn filter_errors(output: &str) -> String {
|
|
107
|
-
lazy_static::lazy_static! {
|
|
108
|
-
static ref ERROR_PATTERNS: Vec<Regex> = vec![
|
|
109
|
-
// Generic errors
|
|
110
|
-
Regex::new(r"(?i)^.*error[\s:\[].*$").unwrap(),
|
|
111
|
-
Regex::new(r"(?i)^.*\berr\b.*$").unwrap(),
|
|
112
|
-
Regex::new(r"(?i)^.*warning[\s:\[].*$").unwrap(),
|
|
113
|
-
Regex::new(r"(?i)^.*\bwarn\b.*$").unwrap(),
|
|
114
|
-
Regex::new(r"(?i)^.*failed.*$").unwrap(),
|
|
115
|
-
Regex::new(r"(?i)^.*failure.*$").unwrap(),
|
|
116
|
-
Regex::new(r"(?i)^.*exception.*$").unwrap(),
|
|
117
|
-
Regex::new(r"(?i)^.*panic.*$").unwrap(),
|
|
118
|
-
// Rust specific
|
|
119
|
-
Regex::new(r"^error\[E\d+\]:.*$").unwrap(),
|
|
120
|
-
Regex::new(r"^\s*--> .*:\d+:\d+$").unwrap(),
|
|
121
|
-
// Python
|
|
122
|
-
Regex::new(r"^Traceback.*$").unwrap(),
|
|
123
|
-
Regex::new(r#"^\s*File ".*", line \d+.*$"#).unwrap(),
|
|
124
|
-
// JavaScript/TypeScript
|
|
125
|
-
Regex::new(r"^\s*at .*:\d+:\d+.*$").unwrap(),
|
|
126
|
-
// Go
|
|
127
|
-
Regex::new(r"^.*\.go:\d+:.*$").unwrap(),
|
|
128
|
-
];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
let mut result = Vec::new();
|
|
132
|
-
let mut in_error_block = false;
|
|
133
|
-
let mut blank_count = 0;
|
|
134
|
-
|
|
135
|
-
for line in output.lines() {
|
|
136
|
-
let is_error_line = ERROR_PATTERNS.iter().any(|p| p.is_match(line));
|
|
137
|
-
|
|
138
|
-
if is_error_line {
|
|
139
|
-
in_error_block = true;
|
|
140
|
-
blank_count = 0;
|
|
141
|
-
result.push(line.to_string());
|
|
142
|
-
} else if in_error_block {
|
|
143
|
-
if line.trim().is_empty() {
|
|
144
|
-
blank_count += 1;
|
|
145
|
-
if blank_count >= 2 {
|
|
146
|
-
in_error_block = false;
|
|
147
|
-
} else {
|
|
148
|
-
result.push(line.to_string());
|
|
149
|
-
}
|
|
150
|
-
} else if line.starts_with(' ') || line.starts_with('\t') {
|
|
151
|
-
// Continuation of error
|
|
152
|
-
result.push(line.to_string());
|
|
153
|
-
blank_count = 0;
|
|
154
|
-
} else {
|
|
155
|
-
in_error_block = false;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
result.join("\n")
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
fn extract_test_summary(output: &str, command: &str) -> String {
|
|
164
|
-
let mut result = Vec::new();
|
|
165
|
-
let lines: Vec<&str> = output.lines().collect();
|
|
166
|
-
|
|
167
|
-
// Detect test framework
|
|
168
|
-
let is_cargo = command.contains("cargo test");
|
|
169
|
-
let is_pytest = command.contains("pytest");
|
|
170
|
-
let is_jest =
|
|
171
|
-
command.contains("jest") || command.contains("npm test") || command.contains("yarn test");
|
|
172
|
-
let is_go = command.contains("go test");
|
|
173
|
-
|
|
174
|
-
// Collect failures
|
|
175
|
-
let mut failures = Vec::new();
|
|
176
|
-
let mut in_failure = false;
|
|
177
|
-
let mut failure_lines = Vec::new();
|
|
178
|
-
|
|
179
|
-
for line in lines.iter() {
|
|
180
|
-
// Cargo test
|
|
181
|
-
if is_cargo {
|
|
182
|
-
if line.contains("test result:") {
|
|
183
|
-
result.push(line.to_string());
|
|
184
|
-
}
|
|
185
|
-
if line.contains("FAILED") && !line.contains("test result") {
|
|
186
|
-
failures.push(line.to_string());
|
|
187
|
-
}
|
|
188
|
-
if line.starts_with("failures:") {
|
|
189
|
-
in_failure = true;
|
|
190
|
-
}
|
|
191
|
-
if in_failure && line.starts_with(" ") {
|
|
192
|
-
failure_lines.push(line.to_string());
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Pytest
|
|
197
|
-
if is_pytest {
|
|
198
|
-
if line.contains(" passed") || line.contains(" failed") || line.contains(" error") {
|
|
199
|
-
result.push(line.to_string());
|
|
200
|
-
}
|
|
201
|
-
if line.contains("FAILED") {
|
|
202
|
-
failures.push(line.to_string());
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Jest
|
|
207
|
-
if is_jest {
|
|
208
|
-
if line.contains("Tests:") || line.contains("Test Suites:") {
|
|
209
|
-
result.push(line.to_string());
|
|
210
|
-
}
|
|
211
|
-
if line.contains("✕") || line.contains("FAIL") {
|
|
212
|
-
failures.push(line.to_string());
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Go test
|
|
217
|
-
if is_go {
|
|
218
|
-
if line.starts_with("ok") || line.starts_with("FAIL") || line.starts_with("---") {
|
|
219
|
-
result.push(line.to_string());
|
|
220
|
-
}
|
|
221
|
-
if line.contains("FAIL") {
|
|
222
|
-
failures.push(line.to_string());
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Build output
|
|
228
|
-
let mut output = String::new();
|
|
229
|
-
|
|
230
|
-
if !failures.is_empty() {
|
|
231
|
-
output.push_str("❌ FAILURES:\n");
|
|
232
|
-
for f in failures.iter().take(10) {
|
|
233
|
-
output.push_str(&format!(" {}\n", f));
|
|
234
|
-
}
|
|
235
|
-
if failures.len() > 10 {
|
|
236
|
-
output.push_str(&format!(" ... +{} more failures\n", failures.len() - 10));
|
|
237
|
-
}
|
|
238
|
-
output.push('\n');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if !result.is_empty() {
|
|
242
|
-
output.push_str("📊 SUMMARY:\n");
|
|
243
|
-
for r in &result {
|
|
244
|
-
output.push_str(&format!(" {}\n", r));
|
|
245
|
-
}
|
|
246
|
-
} else {
|
|
247
|
-
// Fallback: show last few lines
|
|
248
|
-
output.push_str("📊 OUTPUT (last 5 lines):\n");
|
|
249
|
-
let start = lines.len().saturating_sub(5);
|
|
250
|
-
for line in &lines[start..] {
|
|
251
|
-
if !line.trim().is_empty() {
|
|
252
|
-
output.push_str(&format!(" {}\n", line));
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
output
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
#[cfg(test)]
|
|
261
|
-
mod tests {
|
|
262
|
-
use super::*;
|
|
263
|
-
|
|
264
|
-
#[test]
|
|
265
|
-
fn test_filter_errors() {
|
|
266
|
-
let output = "info: compiling\nerror: something failed\n at line 10\ninfo: done";
|
|
267
|
-
let filtered = filter_errors(output);
|
|
268
|
-
assert!(filtered.contains("error"));
|
|
269
|
-
assert!(!filtered.contains("info"));
|
|
270
|
-
}
|
|
271
|
-
}
|