From 1c63fd04a5ce0c976b1ef07ccd21c00feac631a4 Mon Sep 17 00:00:00 2001 From: Greyforge Admin Date: Tue, 19 May 2026 23:23:09 -0400 Subject: [PATCH] Import YAML session exports --- src/cortex-cli/src/import_cmd.rs | 88 ++++++++++++++++------------- src/cortex-cli/tests/import_yaml.rs | 41 ++++++++++++++ 2 files changed, 90 insertions(+), 39 deletions(-) create mode 100644 src/cortex-cli/tests/import_yaml.rs diff --git a/src/cortex-cli/src/import_cmd.rs b/src/cortex-cli/src/import_cmd.rs index 696d93ae8..1dbc56df7 100644 --- a/src/cortex-cli/src/import_cmd.rs +++ b/src/cortex-cli/src/import_cmd.rs @@ -1,6 +1,6 @@ //! Session import command for Cortex CLI. //! -//! Imports a session from a portable JSON format (exported or shared). +//! Imports a session from a portable JSON or YAML format (exported or shared). use anyhow::{Context, Result, bail}; use clap::Parser; @@ -21,10 +21,10 @@ use crate::export_cmd::{ExportMessage, SessionExport}; /// Maximum depth for processing messages to prevent stack overflow from deeply nested structures. const MAX_PROCESSING_DEPTH: usize = 10000; -/// Import a session from JSON format. +/// Import a session from JSON or YAML format. #[derive(Debug, Parser)] pub struct ImportCommand { - /// Path to the JSON file to import, URL to fetch, or "-" for stdin + /// Path to the JSON/YAML file to import, URL to fetch, or "-" for stdin #[arg(value_name = "FILE_OR_URL")] pub source: String, @@ -50,7 +50,7 @@ impl ImportCommand { .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?; // Read the export data - let (json_content, is_from_url) = if self.source == "-" { + let (export_content, is_from_url) = if self.source == "-" { // Read from stdin use std::io::Read; let mut content = String::new(); @@ -70,41 +70,7 @@ impl ImportCommand { }; // Parse the export with helpful error messages - let export: SessionExport = serde_json::from_str(&json_content).map_err(|e| { - // Create a helpful error message with content preview - let preview_len = json_content.len().min(200); - let content_preview = &json_content[..preview_len]; - let truncated = if json_content.len() > 200 { - "..." - } else { - "" - }; - - let source_type = if is_from_url { "URL" } else { "file" }; - - // Detect common non-JSON content types - let hint = if content_preview.trim_start().starts_with(" Result { + match serde_json::from_str(content) { + Ok(export) => return Ok(export), + Err(json_err) => { + let yaml_err = match serde_yaml::from_str(content) { + Ok(export) => return Ok(export), + Err(err) => err, + }; + + let content_preview: String = content.chars().take(200).collect(); + let truncated = if content.chars().count() > 200 { + "..." + } else { + "" + }; + + let source_type = if is_from_url { "URL" } else { "file" }; + + // Detect common non-export content types + let hint = if content_preview.trim_start().starts_with(" Result { // Use curl for fetching diff --git a/src/cortex-cli/tests/import_yaml.rs b/src/cortex-cli/tests/import_yaml.rs new file mode 100644 index 000000000..649737be3 --- /dev/null +++ b/src/cortex-cli/tests/import_yaml.rs @@ -0,0 +1,41 @@ +use std::process::Command; + +use tempfile::tempdir; + +#[test] +fn import_accepts_yaml_session_export() { + let home_dir = tempdir().unwrap(); + let export_file = home_dir.path().join("session.yaml"); + std::fs::write( + &export_file, + r#"version: 1 +session: + id: "550e8400-e29b-41d4-a716-446655440000" + title: "YAML import" + created_at: "2024-01-01T00:00:00Z" + cwd: "/tmp" + model: "test-model" +messages: + - role: "user" + content: "hello from yaml" +"#, + ) + .unwrap(); + + let output = Command::new(env!("CARGO_BIN_EXE_Cortex")) + .args(["import", export_file.to_str().unwrap()]) + .env("HOME", home_dir.path()) + .env_remove("CORTEX_HOME") + .output() + .unwrap(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + let combined = format!("{stdout}{stderr}"); + assert!( + output.status.success(), + "import failed\nstdout:\n{stdout}\nstderr:\n{stderr}" + ); + assert!(combined.contains("Imported session as:")); + assert!(combined.contains("Messages: 1")); +}