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
1 change: 1 addition & 0 deletions crates/sprout-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export SPROUT_RELAY_URL="https://relay.example.com"
# Messages
sprout messages send --channel <uuid> --content "Hello"
sprout messages send --channel <uuid> --content "Reply" --reply-to <event-id> --broadcast
sprout messages send --channel <uuid> --content - < message.md # read body from stdin
sprout messages get --channel <uuid> --limit 20
sprout messages thread --channel <uuid> --event <event-id>
sprout messages search --query "architecture"
Expand Down
7 changes: 6 additions & 1 deletion crates/sprout-cli/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ REPLY_ID=$(echo "$REPLY" | jq -r '.id // .event_id')
sprout messages send --channel "$CHANNEL_ID" --content "Hey @someone" \
--mention "0000000000000000000000000000000000000000000000000000000000000001" | jq .

# messages send from stdin — safe path for content with shell metacharacters
# (backticks, $vars, code blocks) that would otherwise be expanded by the shell.
echo 'Body with `backticks` and $vars stays literal.' \
| sprout messages send --channel "$CHANNEL_ID" --content - | jq .

# messages get
sprout messages get --channel "$CHANNEL_ID" | jq .
sprout messages get --channel "$CHANNEL_ID" --limit 5 | jq .
Expand Down Expand Up @@ -473,7 +478,7 @@ sprout channels delete --channel "$FORUM_ID" | jq .

| # | Command | Tested | Notes |
|---|---------|:------:|-------|
| 1 | `messages send` | ☐ | Basic, reply, broadcast, mentions |
| 1 | `messages send` | ☐ | Basic, reply, broadcast, mentions, stdin |
| 2 | `messages send-diff` | ☐ | Stdin, metadata, branch/PR |
| 3 | `messages edit` | ☐ | |
| 4 | `messages delete` | ☐ | |
Expand Down
10 changes: 9 additions & 1 deletion crates/sprout-cli/src/commands/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,15 @@ pub struct SendMessageParams {
pub files: Vec<String>,
}

pub async fn cmd_send_message(client: &SproutClient, p: SendMessageParams) -> Result<(), CliError> {
pub async fn cmd_send_message(
client: &SproutClient,
mut p: SendMessageParams,
) -> Result<(), CliError> {
// Allow '-' to read content from stdin. This keeps callers from having to
// jam shell-metacharacter-heavy text (backticks, $vars, etc.) through argv
// quoting — the source of countless self-inflicted command-substitution
// bugs for agent and human users alike.
p.content = read_or_stdin(&p.content)?;
validate_content_size(&p.content)?;
if let Some(ref r) = p.reply_to {
validate_hex64(r)?;
Expand Down
4 changes: 2 additions & 2 deletions crates/sprout-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,13 @@ enum Cmd {
pub enum MessagesCmd {
/// Send a message to a channel
#[command(
after_help = "Examples:\n sprout messages send --channel <UUID> --content \"hello\"\n sprout messages send --channel <UUID> --content \"@alice check this\""
after_help = "Examples:\n sprout messages send --channel <UUID> --content \"hello\"\n sprout messages send --channel <UUID> --content \"@alice check this\"\n echo \"hello from stdin\" | sprout messages send --channel <UUID> --content -"
)]
Send {
/// Channel UUID (from 'sprout channels list')
#[arg(long)]
channel: String,
/// Message text — supports @mentions and markdown
/// Message text — supports @mentions and markdown. Use '-' to read from stdin.
#[arg(long)]
content: String,
/// Nostr event kind (default: channel default)
Expand Down
16 changes: 16 additions & 0 deletions crates/sprout-cli/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,4 +442,20 @@ mod tests {
assert!(super::validate_repo_id("foo/bar").is_err());
assert!(super::validate_repo_id("a@b").is_err());
}

// --- read_or_stdin ---

#[test]
fn read_or_stdin_passthrough_returns_value() {
// Anything other than "-" is returned verbatim — backticks, $vars,
// newlines must all survive untouched (no shell evaluation happens
// here; we're past argv parsing).
let raw = "literal `backticks` and $vars\nwith newline";
assert_eq!(super::read_or_stdin(raw).unwrap(), raw);
}

#[test]
fn read_or_stdin_passthrough_empty_string() {
assert_eq!(super::read_or_stdin("").unwrap(), "");
}
}
Loading