use super::*;
pub fn run_image(args: &ImageArgs) -> Result<()> {
let extracting = args.out.is_some();
let selection = parse_id_selection(&args.id)?;
let session_files = crate::path::resolve_targets_with_session_list(
&args.paths,
args.sessions_from.as_deref(),
args.want_subagents().into(),
crate::path::Caller::Other,
)?;
use rayon::prelude::*;
let per_file: Vec<(Vec<ImageRef>, usize)> = session_files
.par_iter()
.map(|p| images_in_file(p, extracting))
.collect::<Result<Vec<_>>>()?;
let mut images: Vec<ImageRef> = Vec::new();
let mut skipped_lines = 0usize;
for (refs, skipped) in per_file {
images.extend(refs);
skipped_lines += skipped;
}
images.sort_by(|a, b| {
match (a.ts_utc.as_deref(), b.ts_utc.as_deref()) {
(Some(x), Some(y)) => x.cmp(y),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => std::cmp::Ordering::Equal,
}
.then((a.line_no, a.img_index).cmp(&(b.line_no, b.img_index)))
});
let time_window =
crate::time_window::TimeWindow::from_args(args.since.as_deref(), args.until.as_deref())?;
images.retain(|i| time_window.contains(i.ts_utc.as_deref()));
if let Some(prefix) = args.uuid.as_deref() {
images.retain(|i| {
i.record_uuid
.as_deref()
.is_some_and(|u| u.starts_with(prefix))
});
}
let mut distinct_transcripts = count_distinct_transcripts(&images);
if let Some(spec_str) = args.turn_range.as_deref() {
let spec = crate::text::parse_range_spec(spec_str, "--turn", false)?;
if distinct_transcripts > 1 {
bail!(
"--turn is per-transcript (turn indices are), but the scope resolves to \
{distinct_transcripts} transcripts — pin it with `@<uuid> --no-subagents`"
);
}
if let Some(path) = pinned_path(&session_files, &images) {
let turn_of = transcript_line_info(path)?.turn_of;
let turn_count = turn_of.values().copied().max().map_or(0, |m| m + 1);
let (lo, hi) = spec.resolve(turn_count, false);
images.retain(|i| {
turn_of
.get(&i.line_no)
.is_some_and(|t| *t >= lo && *t <= hi)
});
} else {
images.clear();
}
distinct_transcripts = count_distinct_transcripts(&images);
}
let selected: Vec<&ImageRef> = if selection.is_empty() {
dedup_latest(&images)
} else {
if distinct_transcripts > 1 {
bail!(
"--id is per-transcript (line numbers and `#N` are), but the scope resolves to \
{distinct_transcripts} transcripts — pin it with `@<uuid> --no-subagents`"
);
}
resolve_selection(&selection, &images, &session_files)?
};
if let Some(out_dir) = args.out.as_deref() {
extract(&selected, out_dir, args, skipped_lines)
} else {
match args.format {
OutputFormat::Json => render_json(&selected, distinct_transcripts, skipped_lines),
OutputFormat::Text => render_text(&selected, distinct_transcripts, skipped_lines),
}
Ok(())
}
}