Skip to content
4 changes: 2 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ SHARED utils.rs Helpers N/A ✓
tee.rs Full output recovery N/A ✓
```

**Total: 50 modules** (32 command modules + 18 infrastructure modules)
**Total: 51 modules** (33 command modules + 18 infrastructure modules)

### Module Count Breakdown

Expand Down Expand Up @@ -1483,4 +1483,4 @@ When implementing a new command, consider:

**Last Updated**: 2026-02-22
**Architecture Version**: 2.2
**rtk Version**: 0.22.2
**rtk Version**: 0.23.0
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This is a fork with critical fixes for git argument parsing and modern JavaScrip

**Verify correct installation:**
```bash
rtk --version # Should show "rtk 0.22.2" (or newer)
rtk --version # Should show "rtk 0.23.0" (or newer)
rtk gain # Should show token savings stats (NOT "command not found")
```

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ rtk filters and compresses command outputs before they reach your LLM context, s

**How to verify you have the correct rtk:**
```bash
rtk --version # Should show "rtk 0.22.2"
rtk --version # Should show "rtk 0.23.0"
rtk gain # Should show token savings stats
```

Expand Down
12 changes: 6 additions & 6 deletions scripts/test-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ assert_ok "rtk cargo build" rtk cargo build
assert_ok "rtk cargo clippy" rtk cargo clippy
# cargo test exits non-zero due to pre-existing failures; check output ignoring exit code
output_cargo_test=$(rtk cargo test 2>&1 || true)
if echo "$output_cargo_test" | grep -q "FAILURES\|test result:"; then
if echo "$output_cargo_test" | grep -q "FAILURES\|test result:\|passed"; then
PASS=$((PASS + 1))
printf " ${GREEN}PASS${NC} %s\n" "rtk cargo test"
else
Expand Down Expand Up @@ -379,19 +379,19 @@ section "Python (conditional)"
if command -v pytest &>/dev/null; then
assert_help "rtk pytest" rtk pytest --help
else
skip "pytest not installed"
skip_test "rtk pytest" "pytest not installed"
fi

if command -v ruff &>/dev/null; then
assert_help "rtk ruff" rtk ruff --help
else
skip "ruff not installed"
skip_test "rtk ruff" "ruff not installed"
fi

if command -v pip &>/dev/null; then
assert_help "rtk pip" rtk pip --help
else
skip "pip not installed"
skip_test "rtk pip" "pip not installed"
fi

# ── 28. Go (conditional) ────────────────────────────
Expand All @@ -404,13 +404,13 @@ if command -v go &>/dev/null; then
assert_help "rtk go build" rtk go build -h
assert_help "rtk go vet" rtk go vet -h
else
skip "go not installed"
skip_test "rtk go" "go not installed"
fi

if command -v golangci-lint &>/dev/null; then
assert_help "rtk golangci-lint" rtk golangci-lint --help
else
skip "golangci-lint not installed"
skip_test "rtk golangci-lint" "golangci-lint not installed"
fi

# ── 29. Global flags ────────────────────────────────
Expand Down
4 changes: 3 additions & 1 deletion src/discover/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,9 @@ const IGNORED_PREFIXES: &[&str] = &[
"case ",
];

const IGNORED_EXACT: &[&str] = &["cd", "echo", "true", "false", "wait", "pwd", "bash", "sh", "fi", "done"];
const IGNORED_EXACT: &[&str] = &[
"cd", "echo", "true", "false", "wait", "pwd", "bash", "sh", "fi", "done",
];

lazy_static! {
static ref REGEX_SET: RegexSet = RegexSet::new(PATTERNS).expect("invalid regex patterns");
Expand Down
61 changes: 61 additions & 0 deletions src/gain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ pub fn run(
monthly: bool,
all: bool,
format: &str,
failures: bool,
_verbose: u8,
) -> Result<()> {
let tracker = Tracker::new().context("Failed to initialize tracking database")?;
let project_scope = resolve_project_scope(project)?; // added: resolve project path

if failures {
return show_failures(&tracker);
}

// Handle export formats
match format {
"json" => {
Expand Down Expand Up @@ -584,3 +589,59 @@ fn export_csv(

Ok(())
}

fn show_failures(tracker: &Tracker) -> Result<()> {
let summary = tracker
.get_parse_failure_summary()
.context("Failed to load parse failure data")?;

if summary.total == 0 {
println!("No parse failures recorded.");
println!("This means all commands parsed successfully (or fallback hasn't triggered yet).");
return Ok(());
}

println!("{}", styled("RTK Parse Failures", true));
println!("{}", "═".repeat(60));
println!();

print_kpi("Total failures", summary.total.to_string());
print_kpi("Recovery rate", format!("{:.1}%", summary.recovery_rate));
println!();

if !summary.top_commands.is_empty() {
println!("{}", styled("Top Commands (by frequency)", true));
println!("{}", "─".repeat(60));
for (cmd, count) in &summary.top_commands {
let cmd_display = if cmd.len() > 50 {
format!("{}...", &cmd[..47])
} else {
cmd.clone()
};
println!(" {:>4}x {}", count, cmd_display);
}
println!();
}

if !summary.recent.is_empty() {
println!("{}", styled("Recent Failures (last 10)", true));
println!("{}", "─".repeat(60));
for rec in &summary.recent {
let ts_short = if rec.timestamp.len() >= 16 {
&rec.timestamp[..16]
} else {
&rec.timestamp
};
let status = if rec.fallback_succeeded { "ok" } else { "FAIL" };
let cmd_display = if rec.raw_command.len() > 40 {
format!("{}...", &rec.raw_command[..37])
} else {
rec.raw_command.clone()
};
println!(" {} [{}] {}", ts_short, status, cmd_display);
}
println!();
}

Ok(())
}
3 changes: 1 addition & 2 deletions src/go_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,7 @@ fn filter_go_test_json(output: &str) -> String {
// Handle build-output/build-fail events (use ImportPath, no Package)
match event.action.as_str() {
"build-output" => {
if let (Some(import_path), Some(output_text)) =
(&event.import_path, &event.output)
if let (Some(import_path), Some(output_text)) = (&event.import_path, &event.output)
{
let text = output_text.trim_end().to_string();
if !text.is_empty() {
Expand Down
Loading
Loading