Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* **hook:** respect Claude Code deny/ask permission rules on rewrite — hook now checks settings.json before rewriting commands, preventing bypass of user-configured deny/ask permissions
* **git:** replace symbol prefixes (`* branch`, `+ Staged:`, `~ Modified:`, `? Untracked:`) with plain lowercase labels (`branch:`, `staged:`, `modified:`, `untracked:`) in git status output
* **wget:** fix `-O -` flag parsed as URL when preceded by other flags (e.g. `--no-check-certificate -O -`)
* **ruby:** use `rails test` instead of `rake test` when positional file args are passed — `rake test` ignores positional files and only supports `TEST=path`

### Features
Expand Down
8 changes: 6 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1735,8 +1735,12 @@ fn main() -> Result<()> {
}

Commands::Wget { url, output, args } => {
if output.as_deref() == Some("-") {
wget_cmd::run_stdout(&url, &args, cli.verbose)?;
// Detect -O - in trailing args (Clap's trailing_var_arg may absorb
// -O - when other unknown flags precede it, e.g. --no-check-certificate -O -)
let stdout_in_args = wget_cmd::has_stdout_output_flag(&args);
if output.as_deref() == Some("-") || stdout_in_args {
let filtered: Vec<String> = wget_cmd::strip_output_flag(&args);
wget_cmd::run_stdout(&url, &filtered, cli.verbose)?;
} else {
// Pass -O <file> through to wget via args
let mut all_args = Vec::new();
Expand Down
146 changes: 146 additions & 0 deletions src/wget_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,56 @@ use crate::tracking;
use crate::utils::resolved_command;
use anyhow::{Context, Result};

/// Check whether trailing args contain `-O -` / `-O-` / `--output-document -`
/// (stdout redirect that Clap's trailing_var_arg may have absorbed).
pub fn has_stdout_output_flag(args: &[String]) -> bool {
let mut i = 0;
while i < args.len() {
let a = args[i].as_str();
// -O- (combined short form)
if a == "-O-" {
return true;
}
// -O - or -O <value> — only stdout when value is "-"
if a == "-O" || a == "--output-document" {
if args.get(i + 1).map(|v| v.as_str()) == Some("-") {
return true;
}
i += 2;
continue;
}
// --output-document=- (long form with =)
if a == "--output-document=-" {
return true;
}
i += 1;
}
false
}

/// Remove the `-O -` / `-O-` / `--output-document -` tokens from args,
/// returning the remaining arguments to pass through to wget.
pub fn strip_output_flag(args: &[String]) -> Vec<String> {
let mut result = Vec::with_capacity(args.len());
let mut i = 0;
while i < args.len() {
let a = args[i].as_str();
if a == "-O-" || a == "--output-document=-" {
i += 1;
continue;
}
if a == "-O" || a == "--output-document" {
if args.get(i + 1).map(|v| v.as_str()) == Some("-") {
i += 2;
continue;
}
}
result.push(args[i].clone());
i += 1;
}
result
}

/// Compact wget - strips progress bars, shows only result
pub fn run(url: &str, args: &[String], verbose: u8) -> Result<()> {
let timer = tracking::TimedExecution::start();
Expand Down Expand Up @@ -261,3 +311,99 @@ fn truncate_line(line: &str, max: usize) -> String {
format!("{}...", t)
}
}

#[cfg(test)]
mod tests {
use super::*;

fn s(v: &[&str]) -> Vec<String> {
v.iter().map(|s| s.to_string()).collect()
}

// --- has_stdout_output_flag ---

#[test]
fn detect_dash_o_dash_separated() {
assert!(has_stdout_output_flag(&s(&["-O", "-"])));
}

#[test]
fn detect_dash_o_dash_combined() {
assert!(has_stdout_output_flag(&s(&["-O-"])));
}

#[test]
fn detect_long_form_separated() {
assert!(has_stdout_output_flag(&s(&["--output-document", "-"])));
}

#[test]
fn detect_long_form_equals() {
assert!(has_stdout_output_flag(&s(&["--output-document=-"])));
}

#[test]
fn detect_stdout_after_other_flags() {
assert!(has_stdout_output_flag(&s(&[
"--no-check-certificate",
"-q",
"-O",
"-"
])));
}

#[test]
fn no_detect_output_to_file() {
assert!(!has_stdout_output_flag(&s(&["-O", "file.html"])));
}

#[test]
fn no_detect_no_output_flag() {
assert!(!has_stdout_output_flag(&s(&["-q", "--no-check-certificate"])));
}

#[test]
fn no_detect_empty_args() {
assert!(!has_stdout_output_flag(&s(&[])));
}

#[test]
fn no_detect_dash_o_at_end_without_value() {
assert!(!has_stdout_output_flag(&s(&["-O"])));
}

// --- strip_output_flag ---

#[test]
fn strip_dash_o_dash_separated() {
assert_eq!(strip_output_flag(&s(&["-q", "-O", "-"])), s(&["-q"]));
}

#[test]
fn strip_dash_o_dash_combined() {
assert_eq!(
strip_output_flag(&s(&["--no-check-certificate", "-O-"])),
s(&["--no-check-certificate"])
);
}

#[test]
fn strip_long_form_equals() {
assert_eq!(
strip_output_flag(&s(&["-q", "--output-document=-"])),
s(&["-q"])
);
}

#[test]
fn strip_preserves_output_to_file() {
let args = s(&["-O", "file.html", "-q"]);
assert_eq!(strip_output_flag(&args), args);
}

#[test]
fn strip_preserves_no_output_flag() {
let args = s(&["-q", "--no-check-certificate"]);
assert_eq!(strip_output_flag(&args), args);
}
}