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
70 changes: 68 additions & 2 deletions apps/decodex/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ use crate::{
archive_hygiene::{self, ArchiveHygieneRequest},
maintenance::{self, MaintenanceMode, MaintenancePruneRequest, MaintenanceScope},
manual::{self, ManualCommitRequest, ManualLandRequest},
orchestrator::{self, DiagnoseRequest, IssueDispatchMode, RunOnceRequest, ServeRequest},
orchestrator::{
self, DiagnoseRequest, EvidenceRequest, IssueDispatchMode, RunOnceRequest, ServeRequest,
},
prelude::eyre,
recovery::{self, ReviewHandoffDiagnoseRequest, ReviewHandoffRebindRequest},
runtime,
Expand Down Expand Up @@ -56,6 +58,7 @@ impl Cli {
Command::Project(args) => args.run(),
Command::Status(args) => args.run(),
Command::Diagnose(args) => args.run(),
Command::Evidence(args) => args.run(),
Command::Recover(args) => args.run(),
Command::ArchiveLinear(args) => args.run(),
Command::Maintenance(args) => args.run(),
Expand Down Expand Up @@ -452,6 +455,38 @@ impl DiagnoseCommand {
}
}

#[derive(Debug, Args)]
struct EvidenceCommand {
#[command(flatten)]
project_config: ProjectConfigArgs,
/// Issue identifier or local issue id to inspect.
issue: String,
/// Restrict readback to one run id. Defaults to the latest local run for the issue.
#[arg(long, value_name = "RUN_ID")]
run_id: Option<String>,
/// Restrict readback to one attempt number. Defaults to the selected run attempt.
#[arg(long, value_name = "NUMBER")]
attempt: Option<i64>,
/// Emit structured JSON instead of human-readable text.
#[arg(long)]
json: bool,
/// Include full structured payload values instead of compact payload summaries only.
#[arg(long)]
include_payload: bool,
}
impl EvidenceCommand {
fn run(&self) -> crate::prelude::Result<()> {
orchestrator::print_private_evidence(EvidenceRequest {
config_path: self.project_config.as_path(),
issue: &self.issue,
run_id: self.run_id.as_deref(),
attempt_number: self.attempt,
json: self.json,
include_payload: self.include_payload,
})
}
}

#[derive(Debug, Args)]
struct RecoverCommand {
#[command(flatten)]
Expand Down Expand Up @@ -684,6 +719,8 @@ enum Command {
Status(StatusCommand),
/// Write and print the agent-readable local evidence index.
Diagnose(DiagnoseCommand),
/// Inspect local-only private execution evidence for one issue or run.
Evidence(EvidenceCommand),
/// Diagnose or explicitly repair supported retained-lane recovery cases.
Recover(RecoverCommand),
/// Dry-run or archive old terminal Linear issues by repo label.
Expand Down Expand Up @@ -811,7 +848,7 @@ mod tests {

use crate::cli::{
AccountCommand, AccountSubcommand, AccountUseCommand, AttemptCommand, Cli, Command,
CommitCommand, DiagnoseCommand, LandCommand, ProbeCommand, ProjectCommand,
CommitCommand, DiagnoseCommand, EvidenceCommand, LandCommand, ProbeCommand, ProjectCommand,
ProjectConfigArgs, ProjectSubcommand, RecoverCommand, RecoverSubcommand,
ReviewHandoffDiagnoseCommand, ReviewHandoffRebindCommand, ReviewHandoffRecoveryCommand,
ReviewHandoffRecoverySubcommand, RunCommand, ServeCommand, StatusCommand,
Expand Down Expand Up @@ -1128,6 +1165,35 @@ mod tests {
));
}

#[test]
fn parses_evidence_with_issue_run_attempt_json_payload_and_project_config() {
let cli = Cli::parse_from([
"decodex",
"evidence",
"--config",
"./project.toml",
"PUB-101",
"--run-id",
"run-1",
"--attempt",
"2",
"--json",
"--include-payload",
]);

assert!(matches!(
cli.command,
Command::Evidence(EvidenceCommand {
project_config: ProjectConfigArgs { config: Some(config) },
issue,
run_id: Some(_),
attempt: Some(2),
json: true,
include_payload: true,
}) if config == Path::new("./project.toml") && issue == "PUB-101"
));
}

#[test]
fn parses_review_handoff_diagnose_with_issue_and_json() {
let cli = Cli::parse_from([
Expand Down
Loading