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
3 changes: 3 additions & 0 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ pub fn login() {
// Check if already authenticated
if is_already_signed_in(&profile_config) {
println!("{}", "You are already signed in.".green());
if !crate::util::is_interactive() {
return;
}
print!("Do you want to log in again? [y/N] ");
use std::io::Write;
std::io::stdout().flush().unwrap();
Expand Down
9 changes: 9 additions & 0 deletions src/connections_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,15 @@ fn walk_auth(schema: &Value) -> Map<String, Value> {
// ── Entry point ───────────────────────────────────────────────────────────────

pub fn run(workspace_id: &str) {
if !crate::util::is_interactive() {
eprintln!(
"error: 'connections new' is interactive and stdin is not a TTY. \
Use 'hotdata connections create list' to discover types and their config schemas, \
then 'hotdata connections create --name <n> --type <t> --config '{{…}}''."
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: (not blocking) The placeholders <n> and <t> are easy to misread — <name> and <type> would match the actual flag names and be clearer to anyone copy-pasting. Also the nested single quotes in --config '{{…}}' produce a shell-broken example as displayed (terminating then immediately reopening); double-quoting the JSON would be friendlier, e.g.:

hotdata connections create --name <name> --type <type> --config '{"...": "..."}'

);
std::process::exit(1);
}

let api = ApiClient::new(Some(workspace_id));

// Phase 1: Select connection type
Expand Down
7 changes: 7 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ struct Cli {
#[arg(long, global = true, hide = true)]
debug: bool,

/// Disable interactive prompts; commands that need input will error instead
#[arg(long = "no-input", global = true)]
no_input: bool,

#[command(subcommand)]
command: Option<Commands>,
}
Expand Down Expand Up @@ -134,6 +138,9 @@ fn main() {
if cli.debug {
util::set_debug(true);
}
if cli.no_input {
util::set_no_input(true);
}

let skip_skill_auto_update =
cli.command.is_none() || matches!(&cli.command, Some(Commands::Skills { .. }));
Expand Down
21 changes: 21 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@ pub fn spinner(msg: &str) -> indicatif::ProgressBar {
pb
}

static NO_INPUT: AtomicBool = AtomicBool::new(false);

pub fn set_no_input(enabled: bool) {
NO_INPUT.store(enabled, Ordering::Relaxed);
}

/// Returns true if interactive prompts are usable. Returns false when:
/// - the global `--no-input` flag was passed,
/// - the `CI` env var is set (most CI runners set this),
/// - stdin is not a TTY (piped, redirected, or invoked by an agent harness).
pub fn is_interactive() -> bool {
if NO_INPUT.load(Ordering::Relaxed) {
return false;
}
if std::env::var_os("CI").is_some() {
return false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: (not blocking) The CI env check has no escape hatch — a developer with CI=1 exported locally (some sandboxes/test harnesses set it) can't run any interactive command, and --no-input only opts further into non-interactive. Not blocking since the workaround is CI= hotdata ..., but worth a thought if you'd rather gate this strictly on the TTY check + the explicit flag.

}
use std::io::IsTerminal;
std::io::stdin().is_terminal()
}

static DEBUG: AtomicBool = AtomicBool::new(false);

pub fn set_debug(enabled: bool) {
Expand Down
8 changes: 8 additions & 0 deletions src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ pub fn set(workspace_id: Option<&str>) {
eprintln!("error: no workspaces available.");
std::process::exit(1);
}
if !crate::util::is_interactive() {
eprintln!(
"error: stdin is not a TTY; cannot prompt for selection. \
Run 'hotdata workspaces list' to see available IDs, \
then 'hotdata workspaces set <workspace_id>'."
);
std::process::exit(1);
}
Comment on lines +46 to +53
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: (not blocking) The interactivity check could move above the api.get("/workspaces") call (gated on workspace_id.is_none()) so non-interactive callers fail without burning a network round-trip. Just a small ergonomic win.

let options: Vec<String> = workspaces
.iter()
.map(|w| format!("{} ({})", w.name, w.public_id))
Expand Down
Loading