remem-ai 0.6.50

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use super::super::host::HostKind;
use super::super::invocation::ContextInvocation;
use super::super::render::{context_stdout_for_invocation, empty_context_output};
use super::super::types::ContextRequest;

#[test]
fn empty_context_uses_ansi_when_color_enabled() {
    let output = empty_context_output(&ContextRequest {
        cwd: "/tmp/remem".to_string(),
        project: "/tmp/remem".to_string(),
        session_id: None,
        hook_source: Some("compact".to_string()),
        current_branch: Some("main".to_string()),
        host: HostKind::CodexCli,
        use_colors: true,
    });

    assert!(output.starts_with("\x1b[1;36mremem context\x1b[0m"));
    assert!(output.contains("\x1b[1;36mremem context\x1b[0m"));
    assert!(output.contains("├─ \x1b[1mproject\x1b[0m: /tmp/remem"));
}

#[test]
fn codex_colored_header_aligns_rows_under_hook_context_value() {
    let output = empty_context_output(&ContextRequest {
        cwd: "/tmp/remem".to_string(),
        project: "/tmp/remem".to_string(),
        session_id: None,
        hook_source: None,
        current_branch: Some("main".to_string()),
        host: HostKind::CodexCli,
        use_colors: true,
    });
    let plain = super::super::style::strip_ansi(&output);
    let mut lines = plain.lines();

    assert_eq!(lines.next(), Some("remem context"));
    let project_line = lines.next().unwrap_or_default();
    assert!(project_line.ends_with("├─ project: /tmp/remem"));
    let row_indent = project_line.chars().take_while(|ch| *ch == ' ').count();
    assert_eq!(row_indent, "hook context: ".chars().count());
}

#[test]
fn codex_session_start_hook_stdout_uses_structured_additional_context() {
    let invocation = ContextInvocation {
        cwd: "/tmp/remem".to_string(),
        project: "/tmp/remem".to_string(),
        session_id: Some("sess-hook-json".to_string()),
        transcript_path: Some("/tmp/remem/session.jsonl".to_string()),
        source: Some("startup".to_string()),
        host: HostKind::CodexCli,
        use_colors: false,
        debug: false,
        force: false,
        gate_mode: None,
    };
    let context = "remem context\nUse `search`/`get_observations` for details.\n";

    let stdout = context_stdout_for_invocation(context, &invocation).unwrap();
    let parsed: serde_json::Value = serde_json::from_str(stdout.trim()).unwrap();

    assert_eq!(
        parsed["hookSpecificOutput"]["hookEventName"],
        "SessionStart"
    );
    assert_eq!(parsed["hookSpecificOutput"]["additionalContext"], context);
    assert!(!stdout.contains("first assistant response"));
    assert!(!stdout.contains("Remem context:"));
}

#[test]
fn codex_session_start_hook_stdout_strips_ansi_before_model_context() {
    let invocation = ContextInvocation {
        cwd: "/tmp/remem".to_string(),
        project: "/tmp/remem".to_string(),
        session_id: Some("sess-hook-color".to_string()),
        transcript_path: None,
        source: Some("compact".to_string()),
        host: HostKind::CodexCli,
        use_colors: true,
        debug: false,
        force: false,
        gate_mode: None,
    };
    let context = "\x1b[1;36mremem context\x1b[0m\nbody\n";

    let stdout = context_stdout_for_invocation(context, &invocation).unwrap();
    let parsed: serde_json::Value = serde_json::from_str(stdout.trim()).unwrap();

    assert_eq!(
        parsed["hookSpecificOutput"]["additionalContext"],
        "remem context\nbody\n"
    );
}

#[test]
fn codex_direct_context_stdout_stays_plain_text() {
    let invocation = ContextInvocation {
        cwd: "/tmp/remem".to_string(),
        project: "/tmp/remem".to_string(),
        session_id: None,
        transcript_path: None,
        source: None,
        host: HostKind::CodexCli,
        use_colors: false,
        debug: false,
        force: true,
        gate_mode: Some("off".to_string()),
    };

    let stdout = context_stdout_for_invocation("remem context\n", &invocation).unwrap();

    assert_eq!(stdout, "remem context\n");
}

#[test]
fn codex_suppressed_context_stdout_stays_silent() {
    let invocation = ContextInvocation {
        cwd: "/tmp/remem".to_string(),
        project: "/tmp/remem".to_string(),
        session_id: Some("sess-suppressed".to_string()),
        transcript_path: None,
        source: Some("compact".to_string()),
        host: HostKind::CodexCli,
        use_colors: false,
        debug: false,
        force: false,
        gate_mode: None,
    };

    let stdout = context_stdout_for_invocation("", &invocation).unwrap();

    assert_eq!(stdout, "");
}