// Tests for prompt_builder.mbt — Task 5.10 + 5.11
// Cross-target via moonbitlang/x/fs + moonbitlang/core/env.
///| Monotonic counter for unique tmp dirs across same-ms calls.
let _pb_tmp_counter : Array[Int] = [0]
fn _mkdtemp_pb() -> String {
let tmp = (@env.get_env_var("TMPDIR")).unwrap_or("/tmp")
_pb_tmp_counter[0] = _pb_tmp_counter[0] + 1
let suffix = @env.now().to_string() + "-" + _pb_tmp_counter[0].to_string()
let dir = tmp + "/mnemo-pb-" + suffix
let _ = try? @fs.create_dir(dir)
dir
}
fn _rmrf_pb(dir : String) -> Unit {
let _ = try? @fs.remove_dir(dir)
}
fn _write_file_pb(path : String, content : String) -> Unit {
let _ = try? @fs.write_string_to_file(path, content)
}
///| Recursive mkdir via @fs.create_dir (which is single-level).
fn _mkdir_p_pb(path : String) -> Unit {
if @fs.path_exists(path) {
return
}
let view = path.view()
let mut last = -1
let mut i = 0
while i < view.length() {
if view.get_char(i) is Some('/') {
last = i
}
i = i + 1
}
if last > 0 {
_mkdir_p_pb(view[0:last].to_string())
}
let _ = try? @fs.create_dir(path)
}
// ---------------------------------------------------------------------------
// Task 5.10: truncation + context file loader tests
// ---------------------------------------------------------------------------
test "pb_truncate: short content unchanged" {
let short = "hello world"
assert_eq(pb_truncate_content(short), short)
}
test "pb_truncate: long content head+tail+marker" {
let long = String::make(25000, 'A')
let out = pb_truncate_content(long)
assert_eq(out.has_prefix("AAAA"), true)
assert_eq(out.has_suffix("AAAA"), true)
assert_eq(out.contains("chars truncated"), true)
}
test "pb_find_context_file: absent returns empty" {
let dir = _mkdtemp_pb()
let result = pb_find_context_file(dir)
assert_eq(result, "")
_rmrf_pb(dir)
}
test "pb_find_context_file: CLAUDE.md in cwd" {
let dir = _mkdtemp_pb()
_write_file_pb(dir + "/CLAUDE.md", "# rules")
let result = pb_find_context_file(dir)
assert_eq(result.contains("rules"), true)
_rmrf_pb(dir)
}
// ---------------------------------------------------------------------------
// Task 5.11: SOUL loader + build_system_prompt tests
// ---------------------------------------------------------------------------
test "pb_load_soul_md: absent returns empty" {
let dir = _mkdtemp_pb()
let result = pb_load_soul_md(dir)
assert_eq(result, "")
_rmrf_pb(dir)
}
test "pb_load_soul_md: present returns content" {
let dir = _mkdtemp_pb()
_write_file_pb(dir + "/SOUL.md", "Be thorough.")
let result = pb_load_soul_md(dir)
assert_eq(result.contains("Be thorough"), true)
_rmrf_pb(dir)
}
test "build_system_prompt: empty config has identity" {
let hh = _mkdtemp_pb()
let cwd = _mkdtemp_pb()
let prompt = build_system_prompt({
hermes_home: hh,
cwd,
platform: "",
model: "",
available_tools: [],
disabled_skills: [],
})
assert_eq(prompt.contains("Ermes Agent"), true)
assert_eq(prompt.contains("persistent memory"), true)
_rmrf_pb(hh)
_rmrf_pb(cwd)
}
test "build_system_prompt: SOUL.md included when present" {
let hh = _mkdtemp_pb()
let cwd = _mkdtemp_pb()
_write_file_pb(hh + "/SOUL.md", "Be very precise.")
let prompt = build_system_prompt({
hermes_home: hh,
cwd,
platform: "",
model: "",
available_tools: [],
disabled_skills: [],
})
assert_eq(prompt.contains("SOUL.md"), true)
assert_eq(prompt.contains("Be very precise"), true)
_rmrf_pb(hh)
_rmrf_pb(cwd)
}
test "build_system_prompt: context file included" {
let hh = _mkdtemp_pb()
let cwd = _mkdtemp_pb()
_write_file_pb(cwd + "/CLAUDE.md", "# rules\nUse ESM.")
let prompt = build_system_prompt({
hermes_home: hh,
cwd,
platform: "",
model: "",
available_tools: [],
disabled_skills: [],
})
assert_eq(prompt.contains("Project Context"), true)
assert_eq(prompt.contains("Use ESM"), true)
_rmrf_pb(hh)
_rmrf_pb(cwd)
}
test "build_system_prompt: assembly order identity before context" {
let hh = _mkdtemp_pb()
let cwd = _mkdtemp_pb()
_write_file_pb(cwd + "/CLAUDE.md", "# project rules")
let prompt = build_system_prompt({
hermes_home: hh,
cwd,
platform: "",
model: "",
available_tools: [],
disabled_skills: [],
})
// Identity must appear before Project Context in the output
let idx_id = pb_index_of(prompt, "Ermes Agent")
let idx_ctx = pb_index_of(prompt, "Project Context")
assert_eq(idx_id < idx_ctx, true)
_rmrf_pb(hh)
_rmrf_pb(cwd)
}
// ---------------------------------------------------------------------------
// Model-specific guidance tests
// ---------------------------------------------------------------------------
test "build_system_prompt: OpenAI guidance for gpt-4o" {
let hh = _mkdtemp_pb()
let cwd = _mkdtemp_pb()
let prompt = build_system_prompt({
hermes_home: hh,
cwd,
platform: "",
model: "gpt-4o",
available_tools: [],
disabled_skills: [],
})
assert_eq(prompt.contains("Execution discipline"), true)
assert_eq(prompt.contains("tool_persistence"), true)
_rmrf_pb(hh)
_rmrf_pb(cwd)
}
test "build_system_prompt: Google guidance for gemini model" {
let hh = _mkdtemp_pb()
let cwd = _mkdtemp_pb()
let prompt = build_system_prompt({
hermes_home: hh,
cwd,
platform: "",
model: "gemini-2.0-flash",
available_tools: [],
disabled_skills: [],
})
assert_eq(prompt.contains("Google model operational directives"), true)
assert_eq(prompt.contains("Absolute paths"), true)
_rmrf_pb(hh)
_rmrf_pb(cwd)
}
test "build_system_prompt: no model-specific guidance for claude" {
let hh = _mkdtemp_pb()
let cwd = _mkdtemp_pb()
let prompt = build_system_prompt({
hermes_home: hh,
cwd,
platform: "",
model: "claude-3-5-sonnet",
available_tools: [],
disabled_skills: [],
})
assert_eq(prompt.contains("Execution discipline"), false)
assert_eq(prompt.contains("Google model operational directives"), false)
_rmrf_pb(hh)
_rmrf_pb(cwd)
}