skilllite_agent/
planning_rules.rs1use std::path::Path;
12
13use super::types::PlanningRule;
14use skilllite_evolution::seed;
15
16pub fn load_rules(workspace: Option<&Path>, chat_root: Option<&Path>) -> Vec<PlanningRule> {
28 let mut rules = if let Some(root) = chat_root {
30 seed::load_rules(root)
31 } else {
32 seed::load_rules(Path::new("/nonexistent"))
33 };
34
35 if let Some(ws) = workspace {
37 let path = ws.join(".skilllite").join("planning_rules.json");
38 if path.exists() {
39 if let Ok(content) = skilllite_fs::read_file(&path) {
40 if let Ok(ws_rules) = serde_json::from_str::<Vec<PlanningRule>>(&content) {
41 let ws_count = ws_rules.len();
42 merge_workspace_rules(&mut rules, ws_rules);
43 tracing::debug!(
44 "Merged {} workspace rules from {}",
45 ws_count,
46 path.display()
47 );
48 }
49 }
50 }
51 }
52
53 rules
54}
55
56fn merge_workspace_rules(base: &mut Vec<PlanningRule>, workspace: Vec<PlanningRule>) {
62 for ws_rule in workspace {
63 if let Some(pos) = base.iter().position(|r| r.id == ws_rule.id) {
64 if base[pos].mutable {
65 base[pos] = ws_rule;
66 }
67 } else {
69 base.push(ws_rule);
70 }
71 }
72}
73
74pub fn load_full_examples(chat_root: Option<&Path>) -> String {
76 if let Some(root) = chat_root {
77 return seed::load_examples(root);
78 }
79 include_str!("seed/examples.seed.md").to_string()
80}
81
82pub fn compact_examples_section(user_message: &str) -> String {
85 let msg_lower = user_message.to_lowercase();
86 let mut lines = vec![
87 "Example 1 - Simple (no tools): \"Write a poem\", \"Translate X\", \"Explain this code\" → []".to_string(),
88 "Example 2 - Tools: \"Calculate 123*456\" → [{\"id\":1,\"description\":\"Use calculator\",\"tool_hint\":\"calculator\",\"completed\":false}]".to_string(),
89 ];
90 let is_city_or_place = user_message.contains("城市")
91 || user_message.contains("地方")
92 || user_message.contains("对比")
93 || user_message.contains("优劣势")
94 || user_message.contains("全方位")
95 || user_message.contains("两地")
96 || msg_lower.contains("city")
97 || msg_lower.contains("place");
98 let candidates: Vec<(&str, &str, &str)> = vec![
99 (
100 "介绍",
101 "景点",
102 "介绍+地点/景点/路线: agent-browser or http-request for fresh info. NOT [].",
103 ),
104 (
105 "城市",
106 "全方位",
107 "城市/地方/全方位分析: http-request for fresh data. NOT chat_history.",
108 ),
109 (
110 "对比",
111 "优劣势",
112 "对比/优劣势: http-request for fresh data. NOT chat_history.",
113 ),
114 (
115 "分析",
116 "稳定性",
117 "分析稳定性/项目: chat_history (ONLY when analyzing chat/project, NOT places)",
118 ),
119 ("历史", "记录", "历史记录: chat_history + analysis."),
120 (
121 "输出到",
122 "保存到",
123 "输出到output: write_output, file_write.",
124 ),
125 (
126 "继续",
127 "",
128 "继续: use context to infer task, often http-request.",
129 ),
130 ("天气", "气象", "天气: weather skill."),
131 ("官网", "网站", "官网/网站: file_write + preview, 2 tasks."),
132 (
133 "refactor",
134 "panic",
135 "编码refactor: file_read定位→file_edit修改→command测试.",
136 ),
137 ("整理", "项目", "模糊请求: file_list探索→analysis总结/确认."),
138 ];
139 let mut added = 0;
140 for (k1, k2, text) in candidates {
141 if added >= 3 {
142 break;
143 }
144 let matches = user_message.contains(k1)
145 || msg_lower.contains(&k1.to_lowercase())
146 || (!k2.is_empty()
147 && (user_message.contains(k2) || msg_lower.contains(&k2.to_lowercase())));
148 let skip = matches && k1 == "分析" && is_city_or_place;
149 if matches && !skip {
150 lines.push(format!("Example - {}: {}", k1, text));
151 added += 1;
152 }
153 }
154 lines.join("\n")
155}