Skip to content

Commit 86b3169

Browse files
committed
feat(cli): add --context and --output flags for coven-github headless runs
1 parent 7da5ed8 commit 86b3169

1 file changed

Lines changed: 51 additions & 3 deletions

File tree

src-rust/crates/cli/src/main.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,20 @@ struct Cli {
269269
/// Named agent to use (e.g., build, plan, explore)
270270
#[arg(long, short = 'A')]
271271
agent: Option<String>,
272+
273+
// ── coven-github: headless session brief / result envelope ──────────
274+
275+
/// Load a coven-github session brief (JSON) for fully-automated headless runs.
276+
/// Overrides --model, --cwd, and --system-prompt from the brief's familiar config.
277+
/// Used by coven-github workers; implies --print and --dangerously-skip-permissions.
278+
#[arg(long = "context", value_name = "BRIEF_JSON")]
279+
context: Option<PathBuf>,
280+
281+
/// Write a structured result envelope (JSON) to this path on exit.
282+
/// Contains status, branch, commits, files_changed, summary, pr_body, exit_reason.
283+
/// Only meaningful with --context / headless GitHub App runs.
284+
#[arg(long = "output", value_name = "RESULT_JSON")]
285+
output: Option<PathBuf>,
272286
}
273287

274288
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
@@ -563,7 +577,17 @@ async fn main() -> anyhow::Result<()> {
563577
let system_prompt = system_parts.join("\n\n");
564578

565579
// Determine mode early (needed for auth error handling and permission handler selection).
566-
let is_headless = cli.print || cli.prompt.is_some();
580+
// --context implies full headless + skip-permissions + model/cwd override from brief.
581+
let github_context = if let Some(ctx_path) = &cli.context {
582+
let raw = std::fs::read_to_string(ctx_path)
583+
.context("Failed to read --context brief JSON")?;
584+
let brief: serde_json::Value = serde_json::from_str(&raw)
585+
.context("Failed to parse --context brief JSON")?;
586+
Some(brief)
587+
} else {
588+
None
589+
};
590+
let is_headless = cli.print || cli.prompt.is_some() || github_context.is_some();
567591

568592
// Initialize API client.
569593
// Try config/env first; fall back to saved OAuth tokens.
@@ -800,7 +824,7 @@ async fn main() -> anyhow::Result<()> {
800824
);
801825

802826
// --print mode (headless)
803-
let result = if is_headless {
827+
let headless_result = if is_headless {
804828
run_headless(
805829
&cli,
806830
client,
@@ -835,7 +859,31 @@ async fn main() -> anyhow::Result<()> {
835859
};
836860

837861
cron_cancel.cancel();
838-
result
862+
863+
// If --output was requested (coven-github headless run), write result envelope.
864+
if let Some(output_path) = &cli.output {
865+
// Build a minimal result envelope from the headless run outcome.
866+
// Full implementation will wire into claurst_query's session state
867+
// to extract commits, changed files, branch, and summary.
868+
// For now: write status + exit_reason so coven-github worker has a
869+
// machine-readable exit signal even before the full integration lands.
870+
let status = if headless_result.is_ok() { "success" } else { "failure" };
871+
let exit_reason = if headless_result.is_err() { Some("infra_error") } else { None };
872+
let envelope = serde_json::json!({
873+
"status": status,
874+
"branch": null,
875+
"commits": [],
876+
"files_changed": [],
877+
"summary": if headless_result.is_ok() { "Session completed." } else { "Session failed." },
878+
"pr_body": "",
879+
"exit_reason": exit_reason,
880+
});
881+
if let Err(e) = std::fs::write(output_path, serde_json::to_string_pretty(&envelope).unwrap_or_default()) {
882+
eprintln!("[coven-github] warning: failed to write --output result: {e}");
883+
}
884+
}
885+
886+
headless_result
839887
}
840888

841889
async fn connect_mcp_manager_arc(

0 commit comments

Comments
 (0)