fix(cli): JSON parity for /memory and /providers in resume mode

Two gaps closed:

1. /memory (resume): json field was None, emitting prose regardless of
   --output-format json. Now emits:
     {kind:memory, cwd, instruction_files:N, files:[{path,lines,preview}...]}

2. /providers (resume): had a spec entry but no parse arm, producing the
   circular 'Unknown slash command: /providers — Did you mean /providers'.
   Added 'providers' as an alias for 'doctor' in the parse match so
   /providers dispatches to the same structured diagnostic output.

3. /doctor (resume): also wired json_value() so --output-format json
   returns the structured doctor report instead of None.

Continues ROADMAP #26 resumed-command JSON parity track.
159 CLI tests pass, fmt clean.
This commit is contained in:
YeonGyu-Kim
2026-04-09 23:35:25 +09:00
parent ed42f8f298
commit 7587f2c1eb
2 changed files with 32 additions and 7 deletions

View File

@@ -1320,7 +1320,7 @@ pub fn validate_slash_command_input(
"skills" | "skill" => SlashCommand::Skills { "skills" | "skill" => SlashCommand::Skills {
args: parse_skills_args(remainder.as_deref())?, args: parse_skills_args(remainder.as_deref())?,
}, },
"doctor" => { "doctor" | "providers" => {
validate_no_args(command, &args)?; validate_no_args(command, &args)?;
SlashCommand::Doctor SlashCommand::Doctor
} }

View File

@@ -2773,7 +2773,7 @@ fn run_resume_command(
SlashCommand::Memory => Ok(ResumeCommandOutcome { SlashCommand::Memory => Ok(ResumeCommandOutcome {
session: session.clone(), session: session.clone(),
message: Some(render_memory_report()?), message: Some(render_memory_report()?),
json: None, json: Some(render_memory_json()?),
}), }),
SlashCommand::Init => { SlashCommand::Init => {
let message = init_claude_md()?; let message = init_claude_md()?;
@@ -2829,11 +2829,14 @@ fn run_resume_command(
json: Some(handle_skills_slash_command_json(args.as_deref(), &cwd)?), json: Some(handle_skills_slash_command_json(args.as_deref(), &cwd)?),
}) })
} }
SlashCommand::Doctor => Ok(ResumeCommandOutcome { SlashCommand::Doctor => {
session: session.clone(), let report = render_doctor_report()?;
message: Some(render_doctor_report()?.render()), Ok(ResumeCommandOutcome {
json: None, session: session.clone(),
}), message: Some(report.render()),
json: Some(report.json_value()),
})
}
SlashCommand::Stats => { SlashCommand::Stats => {
let usage = UsageTracker::from_session(session).cumulative_usage(); let usage = UsageTracker::from_session(session).cumulative_usage();
Ok(ResumeCommandOutcome { Ok(ResumeCommandOutcome {
@@ -5421,6 +5424,28 @@ fn render_memory_report() -> Result<String, Box<dyn std::error::Error>> {
)) ))
} }
fn render_memory_json() -> Result<serde_json::Value, Box<dyn std::error::Error>> {
let cwd = env::current_dir()?;
let project_context = ProjectContext::discover(&cwd, DEFAULT_DATE)?;
let files: Vec<_> = project_context
.instruction_files
.iter()
.map(|f| {
json!({
"path": f.path.display().to_string(),
"lines": f.content.lines().count(),
"preview": f.content.lines().next().unwrap_or("").trim(),
})
})
.collect();
Ok(json!({
"kind": "memory",
"cwd": cwd.display().to_string(),
"instruction_files": files.len(),
"files": files,
}))
}
fn init_claude_md() -> Result<String, Box<dyn std::error::Error>> { fn init_claude_md() -> Result<String, Box<dyn std::error::Error>> {
let cwd = env::current_dir()?; let cwd = env::current_dir()?;
Ok(initialize_repo(&cwd)?.render()) Ok(initialize_repo(&cwd)?.render())