fix(output): emit array-of-objects for JSON output

sessions list/dump and agents list/dump were passing Vec<Vec<String>>
directly to render_json, producing unlabeled arrays. Now each row is
mapped to a serde_json object with named keys before serializing.

Closes claudbg-pfa5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
Elijah Voigt 2 months ago
parent b6ef138aae
commit f3258ee2b5

@ -1,11 +1,11 @@
---
# claudbg-pfa5
title: JSON output is array-of-arrays instead of array-of-objects
status: todo
status: completed
type: bug
priority: normal
created_at: 2026-03-30T04:37:05Z
updated_at: 2026-03-30T04:41:14Z
updated_at: 2026-03-30T05:05:12Z
parent: claudbg-tci9
---
@ -38,3 +38,7 @@ Output should be an array of objects with named keys:
- `src/commands/sessions.rs``list()` and `dump()` JSON output branches
- `src/output/json.rs` — generic `render_json`
## Summary of Changes
Fixed JSON output in both sessions.rs and agents.rs to produce array-of-objects instead of array-of-arrays. Each row is now mapped to a serde_json object with named keys before serializing. Keys: sessions list (id, date, project, model, messages, subagents), sessions dump (seq, timestamp, type, role, content), agents list (id, type, file, modified), agents dump (seq, timestamp, type, role, content).

@ -185,7 +185,20 @@ pub async fn list(session_id: &str, opts: &crate::cli::GlobalOpts) -> Result<()>
crate::cli::OutputFormat::Table => {
crate::output::render_table(&["Agent ID", "Type", "File", "Modified"], &rows)?
}
crate::cli::OutputFormat::Json => crate::output::render_json(&rows)?,
crate::cli::OutputFormat::Json => {
let objects: Vec<serde_json::Value> = rows
.iter()
.map(|r| {
serde_json::json!({
"id": r[0],
"type": r[1],
"file": r[2],
"modified": r[3],
})
})
.collect();
crate::output::render_json(&objects)?
}
crate::cli::OutputFormat::Xml => {
crate::output::render_xml_rows(&["agent_id", "type", "file", "modified"], &rows)?
}
@ -239,7 +252,21 @@ pub async fn dump(
crate::cli::OutputFormat::Table => {
crate::output::render_table(&["#", "Timestamp", "Type", "Role", "Content"], &rows)?
}
crate::cli::OutputFormat::Json => crate::output::render_json(&rows)?,
crate::cli::OutputFormat::Json => {
let objects: Vec<serde_json::Value> = rows
.iter()
.map(|r| {
serde_json::json!({
"seq": r[0],
"timestamp": r[1],
"type": r[2],
"role": r[3],
"content": r[4],
})
})
.collect();
crate::output::render_json(&objects)?
}
crate::cli::OutputFormat::Xml => {
crate::output::render_xml_rows(&["seq", "timestamp", "type", "role", "content"], &rows)?
}

@ -202,7 +202,22 @@ pub async fn list(opts: &crate::cli::GlobalOpts) -> Result<()> {
crate::cli::OutputFormat::Table => {
crate::output::render_table(&["ID", "Date", "Project", "Model", "Messages"], &rows)?
}
crate::cli::OutputFormat::Json => crate::output::render_json(&rows)?,
crate::cli::OutputFormat::Json => {
let objects: Vec<serde_json::Value> = rows
.iter()
.map(|r| {
serde_json::json!({
"id": r[0],
"date": r[1],
"project": r[2],
"model": r[3],
"messages": r[4],
"subagents": 0,
})
})
.collect();
crate::output::render_json(&objects)?
}
crate::cli::OutputFormat::Xml => crate::output::render_xml_rows(
&["session_id", "date", "project", "model", "messages"],
&rows,
@ -293,7 +308,21 @@ pub async fn dump(id: &str, follow: bool, opts: &crate::cli::GlobalOpts) -> Resu
crate::cli::OutputFormat::Table => {
crate::output::render_table(&["#", "Timestamp", "Type", "Role", "Content"], &rows)?
}
crate::cli::OutputFormat::Json => crate::output::render_json(&rows)?,
crate::cli::OutputFormat::Json => {
let objects: Vec<serde_json::Value> = rows
.iter()
.map(|r| {
serde_json::json!({
"seq": r[0],
"timestamp": r[1],
"type": r[2],
"role": r[3],
"content": r[4],
})
})
.collect();
crate::output::render_json(&objects)?
}
crate::cli::OutputFormat::Xml => {
crate::output::render_xml_rows(&["seq", "timestamp", "type", "role", "content"], &rows)?
}

Loading…
Cancel
Save