use std::{io::IsTerminal, sync::OnceLock};
pub mod cli_args;
pub mod commands;
pub mod help;
pub mod render;
pub mod style;
pub mod tips;
pub mod transaction_sentinel;
#[cfg(feature = "client")]
pub use cli_args::PresenceCommands;
pub use cli_args::{
ActorCommands, AdoptArgs, AgentCommands, CheckpointArgs, Cli, CloneArgs, CollapseArgs,
Commands, CommitArgs, CompletionSubject, ContextCommands, DaemonCommands, DiagnoseArgs,
DiffArgs, DoctorArgs, DoctorCommands, DoctorDocsArgs, DoctorSchemasArgs, ExpandArgs,
HookCommands, HookInstallSource, InitArgs, IntegrationCommands, IntegrationInstallArgs,
IntegrationRelayArgs, IntegrationTargetArgs, LogArgs, MaintenanceCommands, MergeArgs,
OplogCommands, OutputMode, PullArgs, PurgeApplyArgs, PurgeCommands, PurgeListArgs, PushArgs,
ReadyArgs, RedactApplyArgs, RedactCommands, RedactListArgs, RedactShowArgs, RedactTrustAddArgs,
RedactTrustCommands, RedactTrustListArgs, RedactTrustRemoveArgs, RemoteCommands, ResolveArgs,
RetroArgs, RevertArgs, RunArgs, SessionCommands, SessionEndArgs, SessionListArgs,
SessionSegmentArgs, SessionShowArgs, SessionStartArgs, ShellCommands, ShellKind, SnapshotArgs,
StashCommands, SwitchArgs, ThreadAbsorbArgs, ThreadCleanupArgs, ThreadCommands, ThreadDropArgs,
ThreadListArgs, ThreadMarkerCommands, ThreadMoveArgs, ThreadNameArgs, ThreadPromoteArgs,
ThreadRenameArgs, ThreadResolveArgs, ThreadShowArgs, ThreadStartArgs, TimelineArgs,
TimelineCommands, TimelineForkArgs, TimelineRecoverArgs, TimelineResetArgs, TimelineTargetArgs,
TryArgs, UndoArgs, VisibilityCommands, VisibilityListArgs, VisibilityPromoteArgs,
VisibilitySetArgs, VisibilityShowArgs, VisibilityTierArg, WatchArgs, WorkspaceModeArg,
};
#[cfg(feature = "client")]
pub use cli_args::{AuthCommands, SupportCommands};
#[cfg(feature = "git-overlay")]
pub use cli_args::{BridgeCommands, GitCommands};
#[cfg(feature = "semantic")]
pub use cli_args::{HotEventKindArg, HotSpotKeyArg, SemanticCommands};
use repo::{Config, OutputFormat};
use crate::config::UserConfig;
pub fn is_tty() -> bool {
std::io::stdout().is_terminal()
}
pub fn user_config_or_exit() -> &'static UserConfig {
static USER_CONFIG: OnceLock<UserConfig> = OnceLock::new();
USER_CONFIG.get_or_init(|| {
UserConfig::load_default().unwrap_or_default()
})
}
pub fn load_user_config_or_exit() -> UserConfig {
user_config_or_exit().clone()
}
pub fn should_output_json(cli: &Cli, config: Option<&Config>) -> bool {
let user_config = user_config_or_exit();
let mut format = config
.and_then(|cfg| cfg.output.format)
.unwrap_or(user_config.output.format);
if let Some(output) = cli.output {
format = match output {
OutputMode::Json | OutputMode::JsonCompact => OutputFormat::Json,
OutputMode::Text => OutputFormat::Text,
};
}
matches!(format, OutputFormat::Json)
}
pub fn output_is_compact(cli: &Cli) -> bool {
matches!(cli.output, Some(OutputMode::JsonCompact))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JsonOutputMode {
Text,
Json,
Jsonl,
}
pub fn json_output_mode_for_kind(
cli: &Cli,
config: Option<&Config>,
json_kind: &str,
) -> JsonOutputMode {
match json_kind {
"jsonl" => {
if matches!(cli.output, Some(OutputMode::Json | OutputMode::JsonCompact)) {
JsonOutputMode::Jsonl
} else {
JsonOutputMode::Text
}
}
"json" | "json_or_jsonl" => {
if should_output_json(cli, config) {
JsonOutputMode::Json
} else {
JsonOutputMode::Text
}
}
"none" => JsonOutputMode::Text,
_ => JsonOutputMode::Text,
}
}
impl weft_client_shim::CliContext for Cli {
fn repo_path(&self) -> Option<&std::path::Path> {
self.repo.as_deref()
}
fn operation_id_wire(&self) -> String {
crate::operation_id::wire(self)
}
fn should_output_json(&self, repo_config: Option<&Config>) -> bool {
should_output_json(self, repo_config)
}
}
pub fn worktree_status_options(config: Option<&Config>) -> repo::WorktreeStatusOptions {
user_config_or_exit().worktree_status_options(config)
}
#[cfg(test)]
mod tests {
use clap::Parser;
use super::*;
#[test]
fn jsonl_commands_require_explicit_json_output() {
let auto = Cli::try_parse_from(["heddle", "watch"]).expect("watch should parse");
assert_eq!(
json_output_mode_for_kind(&auto, None, "jsonl"),
JsonOutputMode::Text
);
let json = Cli::try_parse_from(["heddle", "--output", "json", "watch"])
.expect("watch --output json should parse");
assert_eq!(
json_output_mode_for_kind(&json, None, "jsonl"),
JsonOutputMode::Jsonl
);
let text = Cli::try_parse_from(["heddle", "--output", "text", "watch"])
.expect("watch --output text should parse");
assert_eq!(
json_output_mode_for_kind(&text, None, "jsonl"),
JsonOutputMode::Text
);
}
}