diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index aa164c9..20ecf03 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -1570,24 +1570,19 @@ fn resume_session(session_path: &Path, commands: &[String], output_format: CliOu && matches!(command, SlashCommand::Status) { let tracker = UsageTracker::from_session(&session); - let usage = tracker.cumulative_usage(); let context = status_context(Some(&resolved_path)).expect("status context"); - let value = json!({ - "kind": "status", - "messages": session.messages.len(), - "turns": tracker.turns(), - "latest_total": tracker.current_turn_usage().total_tokens(), - "cumulative_input": usage.input_tokens, - "cumulative_output": usage.output_tokens, - "cumulative_total": usage.total_tokens(), - "workspace": { - "cwd": context.cwd, - "project_root": context.project_root, - "git_branch": context.git_branch, - "git_state": context.git_summary.headline(), - "session": context.session_path.as_ref().map_or_else(|| "live-repl".to_string(), |path| path.display().to_string()), - } - }); + let value = status_json_value( + "restored-session", + StatusUsage { + message_count: session.messages.len(), + turns: tracker.turns(), + latest: tracker.current_turn_usage(), + cumulative: tracker.cumulative_usage(), + estimated_tokens: 0, + }, + default_permission_mode().as_str(), + &context, + ); println!( "{}", serde_json::to_string_pretty(&value).expect("status json") @@ -3845,54 +3840,68 @@ fn print_status_snapshot( ), CliOutputFormat::Json => println!( "{}", - serde_json::to_string_pretty(&json!({ - "kind": "status", - "model": model, - "permission_mode": permission_mode.as_str(), - "usage": { - "messages": usage.message_count, - "turns": usage.turns, - "latest_total": usage.latest.total_tokens(), - "cumulative_input": usage.cumulative.input_tokens, - "cumulative_output": usage.cumulative.output_tokens, - "cumulative_total": usage.cumulative.total_tokens(), - "estimated_tokens": usage.estimated_tokens, - }, - "workspace": { - "cwd": context.cwd, - "project_root": context.project_root, - "git_branch": context.git_branch, - "git_state": context.git_summary.headline(), - "changed_files": context.git_summary.changed_files, - "staged_files": context.git_summary.staged_files, - "unstaged_files": context.git_summary.unstaged_files, - "untracked_files": context.git_summary.untracked_files, - "session": context.session_path.as_ref().map_or_else(|| "live-repl".to_string(), |path| path.display().to_string()), - "loaded_config_files": context.loaded_config_files, - "discovered_config_files": context.discovered_config_files, - "memory_file_count": context.memory_file_count, - }, - "sandbox": { - "enabled": context.sandbox_status.enabled, - "active": context.sandbox_status.active, - "supported": context.sandbox_status.supported, - "in_container": context.sandbox_status.in_container, - "requested_namespace": context.sandbox_status.requested.namespace_restrictions, - "active_namespace": context.sandbox_status.namespace_active, - "requested_network": context.sandbox_status.requested.network_isolation, - "active_network": context.sandbox_status.network_active, - "filesystem_mode": context.sandbox_status.filesystem_mode.as_str(), - "filesystem_active": context.sandbox_status.filesystem_active, - "allowed_mounts": context.sandbox_status.allowed_mounts, - "markers": context.sandbox_status.container_markers, - "fallback_reason": context.sandbox_status.fallback_reason, - } - }))? + serde_json::to_string_pretty(&status_json_value( + model, + usage, + permission_mode.as_str(), + &context, + ))? ), } Ok(()) } +fn status_json_value( + model: &str, + usage: StatusUsage, + permission_mode: &str, + context: &StatusContext, +) -> serde_json::Value { + json!({ + "kind": "status", + "model": model, + "permission_mode": permission_mode, + "usage": { + "messages": usage.message_count, + "turns": usage.turns, + "latest_total": usage.latest.total_tokens(), + "cumulative_input": usage.cumulative.input_tokens, + "cumulative_output": usage.cumulative.output_tokens, + "cumulative_total": usage.cumulative.total_tokens(), + "estimated_tokens": usage.estimated_tokens, + }, + "workspace": { + "cwd": context.cwd, + "project_root": context.project_root, + "git_branch": context.git_branch, + "git_state": context.git_summary.headline(), + "changed_files": context.git_summary.changed_files, + "staged_files": context.git_summary.staged_files, + "unstaged_files": context.git_summary.unstaged_files, + "untracked_files": context.git_summary.untracked_files, + "session": context.session_path.as_ref().map_or_else(|| "live-repl".to_string(), |path| path.display().to_string()), + "loaded_config_files": context.loaded_config_files, + "discovered_config_files": context.discovered_config_files, + "memory_file_count": context.memory_file_count, + }, + "sandbox": { + "enabled": context.sandbox_status.enabled, + "active": context.sandbox_status.active, + "supported": context.sandbox_status.supported, + "in_container": context.sandbox_status.in_container, + "requested_namespace": context.sandbox_status.requested.namespace_restrictions, + "active_namespace": context.sandbox_status.namespace_active, + "requested_network": context.sandbox_status.requested.network_isolation, + "active_network": context.sandbox_status.network_active, + "filesystem_mode": context.sandbox_status.filesystem_mode.as_str(), + "filesystem_active": context.sandbox_status.filesystem_active, + "allowed_mounts": context.sandbox_status.allowed_mounts, + "markers": context.sandbox_status.container_markers, + "fallback_reason": context.sandbox_status.fallback_reason, + } + }) +} + fn status_context( session_path: Option<&Path>, ) -> Result> { diff --git a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs index 7b6848e..74e81f5 100644 --- a/rust/crates/rusty-claude-cli/tests/output_format_contract.rs +++ b/rust/crates/rusty-claude-cli/tests/output_format_contract.rs @@ -130,7 +130,10 @@ fn doctor_and_resume_status_emit_json_when_requested() { ], ); assert_eq!(resumed["kind"], "status"); - assert_eq!(resumed["messages"], 1); + assert_eq!(resumed["model"], "restored-session"); + assert_eq!(resumed["usage"]["messages"], 1); + assert!(resumed["workspace"]["cwd"].as_str().is_some()); + assert!(resumed["sandbox"]["filesystem_mode"].as_str().is_some()); } fn assert_json_command(current_dir: &Path, args: &[&str]) -> Value { diff --git a/rust/crates/rusty-claude-cli/tests/resume_slash_commands.rs b/rust/crates/rusty-claude-cli/tests/resume_slash_commands.rs index 1d060c2..273cfa7 100644 --- a/rust/crates/rusty-claude-cli/tests/resume_slash_commands.rs +++ b/rust/crates/rusty-claude-cli/tests/resume_slash_commands.rs @@ -261,11 +261,18 @@ fn resumed_status_command_emits_structured_json_when_requested() { let parsed: Value = serde_json::from_str(stdout.trim()).expect("resume status output should be json"); assert_eq!(parsed["kind"], "status"); - assert_eq!(parsed["messages"], 1); + assert_eq!(parsed["model"], "restored-session"); + assert_eq!(parsed["permission_mode"], "danger-full-access"); + assert_eq!(parsed["usage"]["messages"], 1); + assert!(parsed["usage"]["turns"].is_number()); + assert!(parsed["workspace"]["cwd"].as_str().is_some()); assert_eq!( parsed["workspace"]["session"], session_path.to_str().expect("utf8 path") ); + assert!(parsed["workspace"]["changed_files"].is_number()); + assert_eq!(parsed["workspace"]["loaded_config_files"].as_u64(), Some(0)); + assert!(parsed["sandbox"]["filesystem_mode"].as_str().is_some()); } fn run_claw(current_dir: &Path, args: &[&str]) -> Output {