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
74 changes: 58 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "2"

[workspace.package]
version = "0.1.198"
version = "0.1.199"
edition = "2024"
rust-version = "1.88"
license = "Apache-2.0"
Expand Down
3 changes: 3 additions & 0 deletions crates/cli-sub-agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,8 @@ sha2.workspace = true
tokuin.workspace = true
xurl-core.workspace = true

[dev-dependencies]
serial_test = "3.4"

[features]
codex-pty-fork = ["csa-executor/codex-pty-fork"]
32 changes: 32 additions & 0 deletions crates/cli-sub-agent/src/cli_review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,22 @@ pub struct ReviewArgs {
/// Read supplementary prompt/context from a file (bypasses shell quoting issues)
#[arg(long, value_name = "PATH")]
pub prompt_file: Option<PathBuf>,

/// [DEPRECATED] Daemon mode is now the default. This flag is a no-op.
#[arg(long, hide = true)]
pub daemon: bool,

/// Run in foreground blocking mode instead of the default daemon mode.
#[arg(long)]
pub no_daemon: bool,

/// Internal flag: this process IS the daemon child. Skip re-spawning.
#[arg(long, hide = true)]
pub daemon_child: bool,

/// Internal: pre-assigned session ID from daemon parent
#[arg(long, hide = true)]
pub session_id: Option<String>,
}

impl ReviewArgs {
Expand Down Expand Up @@ -342,4 +358,20 @@ pub struct DebateArgs {
/// Read the debate question from a file (bypasses shell quoting issues)
#[arg(long, value_name = "PATH", conflicts_with_all = ["question", "topic"])]
pub prompt_file: Option<PathBuf>,

/// [DEPRECATED] Daemon mode is now the default. This flag is a no-op.
#[arg(long, hide = true)]
pub daemon: bool,

/// Run in foreground blocking mode instead of the default daemon mode.
#[arg(long)]
pub no_daemon: bool,

/// Internal flag: this process IS the daemon child. Skip re-spawning.
#[arg(long, hide = true)]
pub daemon_child: bool,

/// Internal: pre-assigned session ID from daemon parent
#[arg(long, hide = true)]
pub session_id: Option<String>,
}
37 changes: 21 additions & 16 deletions crates/cli-sub-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,22 +412,13 @@ async fn run() -> Result<()> {
daemon_child,
session_id,
} => {
// Daemon spawn: daemon mode is the default; --no-daemon opts out.
if !no_daemon && !daemon_child {
if let Some(ref _id) = session_id {
anyhow::bail!("--session-id is an internal flag and must not be used directly");
}
// spawn_and_exit() calls process::exit(0) on success — never returns.
run_cmd_daemon::spawn_and_exit(cd.as_deref())?;
}

// Daemon child: propagate pre-assigned session ID via env so the
// pipeline's create_session reuses it (same directory as spool files).
if let Some(ref sid) = session_id {
// SAFETY: This runs in the daemon child before tokio spawns worker
// threads (we are still in the synchronous dispatch path of main).
unsafe { std::env::set_var("CSA_DAEMON_SESSION_ID", sid) };
}
run_cmd_daemon::check_daemon_flags(
"run",
no_daemon,
daemon_child,
&session_id,
cd.as_deref(),
)?;

// Daemon child path: continue with normal run logic.
// --stream-stdout forces streaming; --no-stream-stdout forces buffering;
Expand Down Expand Up @@ -535,6 +526,13 @@ async fn run() -> Result<()> {
memory_cmd::handle_memory_command(command).await?;
}
Commands::Review(args) => {
run_cmd_daemon::check_daemon_flags(
"review",
args.no_daemon,
args.daemon_child,
&args.session_id,
args.cd.as_deref(),
)?;
let exit_code = review_cmd::handle_review(args, current_depth).await?;
crate::pipeline::prompt_guard::emit_sa_mode_caller_guard(
sa_mode_active,
Expand All @@ -546,6 +544,13 @@ async fn run() -> Result<()> {
std::process::exit(exit_code);
}
Commands::Debate(args) => {
run_cmd_daemon::check_daemon_flags(
"debate",
args.no_daemon,
args.daemon_child,
&args.session_id,
Comment on lines +547 to +551
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve stdin prompt support for daemonized debate

Daemonizing debate here changes default behavior for calls that omit question and rely on stdin (for example printf ... | csa debate ...): the parent now exits after spawning, and the child is launched with stdin=/dev/null, so resolve_prompt_with_file reads an empty stream and fails with an empty-stdin prompt error. This is a regression from the previous foreground default and breaks existing piped-debate workflows unless users discover --no-daemon.

Useful? React with 👍 / 👎.

args.cd.as_deref(),
)?;
let exit_code = debate_cmd::handle_debate(args, current_depth, output_format).await?;
crate::pipeline::prompt_guard::emit_sa_mode_caller_guard(
sa_mode_active,
Expand Down
Loading
Loading