Skip to main content

spool/output/
prompt.rs

1use crate::domain::{ContextBundle, TargetTool};
2
3pub fn render(bundle: &ContextBundle, max_chars: usize) -> String {
4    let mut output = String::new();
5    output.push_str(match bundle.input.target {
6        TargetTool::Claude => "以下是给 Claude 使用的精简上下文。\n\n",
7        TargetTool::Codex => "以下是给 Codex 使用的精简上下文。\n\n",
8        TargetTool::Opencode => "以下是给 OpenCode 使用的精简上下文。\n\n",
9    });
10
11    if let Some(project) = &bundle.route.project {
12        output.push_str(&format!("项目:{} ({})\n", project.name, project.id));
13        output.push_str(&format!("项目命中原因:{}\n\n", project.reason));
14    }
15
16    if !bundle.route.modules.is_empty() {
17        output.push_str("模块:\n");
18        for module in &bundle.route.modules {
19            output.push_str(&format!("- {}\n", module.id));
20        }
21        output.push('\n');
22    }
23
24    if !bundle.route.scenes.is_empty() {
25        output.push_str("场景:\n");
26        for scene in &bundle.route.scenes {
27            output.push_str(&format!("- {}\n", scene.id));
28        }
29        output.push('\n');
30    }
31
32    if !bundle.route.lifecycle_candidates.is_empty() {
33        output.push_str("记忆(accepted / canonical):\n");
34        for candidate in &bundle.route.lifecycle_candidates {
35            let summary = truncate_chars(&candidate.summary, 120);
36            output.push_str(&format!(
37                "- [{}] [{}] {} — {}\n",
38                candidate.score, candidate.memory_type, candidate.title, summary
39            ));
40        }
41        output.push('\n');
42    }
43
44    output.push_str("候选笔记:\n");
45    for candidate in &bundle.route.candidates {
46        output.push_str(&format!(
47            "- [{}] {}\n  {}\n",
48            candidate.score, candidate.relative_path, candidate.excerpt
49        ));
50    }
51
52    if output.chars().count() > max_chars {
53        output.chars().take(max_chars).collect()
54    } else {
55        output
56    }
57}
58
59fn truncate_chars(value: &str, max_chars: usize) -> String {
60    let chars: Vec<char> = value.chars().collect();
61    if chars.len() <= max_chars {
62        return value.to_string();
63    }
64    let mut out: String = chars.iter().take(max_chars).collect();
65    out.push('…');
66    out
67}