Skip to main content

apm_core/wrapper/builtin/
debug.rs

1use super::write_and_spawn_script;
2use crate::wrapper::{Wrapper, WrapperContext};
3
4pub(crate) const DEBUG_SCRIPT: &str = r#"#!/bin/sh
5env | grep '^APM_' >&2
6printf '\n=== SYSTEM PROMPT ===\n' >&2
7cat "$APM_SYSTEM_PROMPT_FILE" >&2
8printf '\n=== USER MESSAGE ===\n' >&2
9cat "$APM_USER_MESSAGE_FILE" >&2
10printf '{"type":"tool_use","id":"debug-1","name":"noop","input":{}}\n'
11rm -f "$0"
12"#;
13
14pub struct DebugWrapper;
15
16impl Wrapper for DebugWrapper {
17    fn spawn(&self, ctx: &WrapperContext) -> anyhow::Result<std::process::Child> {
18        write_and_spawn_script("debug", DEBUG_SCRIPT, ctx)
19    }
20}
21
22#[cfg(test)]
23mod tests {
24    use super::DEBUG_SCRIPT;
25
26    #[test]
27    fn script_dumps_apm_env_to_stderr() {
28        assert!(
29            DEBUG_SCRIPT.contains("env | grep '^APM_' >&2"),
30            "script must redirect APM_ env vars to stderr"
31        );
32    }
33
34    #[test]
35    fn script_emits_one_canonical_event() {
36        let canonical_lines: Vec<&str> = DEBUG_SCRIPT
37            .lines()
38            .filter(|l| l.starts_with("printf '{") && !l.contains(">&2"))
39            .collect();
40        assert_eq!(canonical_lines.len(), 1, "expected exactly one stdout JSONL line: {canonical_lines:?}");
41        assert!(canonical_lines[0].contains("\"type\":\"tool_use\""), "canonical event must have type=tool_use");
42    }
43
44    #[test]
45    fn script_outputs_system_prompt_and_user_message_to_stderr() {
46        assert!(DEBUG_SCRIPT.contains("cat \"$APM_SYSTEM_PROMPT_FILE\" >&2"));
47        assert!(DEBUG_SCRIPT.contains("cat \"$APM_USER_MESSAGE_FILE\" >&2"));
48    }
49
50    #[test]
51    fn script_self_cleans_up() {
52        assert!(DEBUG_SCRIPT.contains("rm -f \"$0\""), "script must remove itself after running");
53    }
54}