1pub mod cache;
19pub mod constants;
20pub mod context;
21pub mod dump;
22pub mod orchestrator;
23pub mod preprocess;
24pub mod section;
25
26pub use cache::{
28 CacheKey, CacheStats, CachedEntry, SectionCache, clear_global_cache, estimate_tokens,
29 global_cache,
30};
31pub use context::{ContextInjector, ProjectType, SystemContext, UserContext};
32pub use dump::{
33 DumpEntry, DumpFileAnalysis, PromptAnalysis, PromptDumper, analyze_dump_file, read_dump_file,
34};
35pub use orchestrator::{
36 AssembledPrompt, CACHE_BOUNDARY, PromptBuilder, PromptOrchestrator, PromptProfile,
37};
38pub use preprocess::{PreProcessHook, ProcessResult, SkillPattern, WorkflowTrigger, preprocess, preprocess_with_skills};
39pub use section::{PromptSection, SectionBuilder, SectionContent};
40
41pub use constants::*;
43
44pub use orchestrator::PromptProfile as LegacyPromptProfile;
50
51#[derive(Debug, Clone)]
54pub struct OverviewContext {
55 pub project_name: String,
56 pub project_type: String,
57 pub directory_structure: String,
58 pub config_files: Vec<(String, String)>, pub readme: Option<String>,
60 pub source_files: Vec<(String, String)>, }
62
63impl Default for OverviewContext {
64 fn default() -> Self {
65 Self {
66 project_name: String::new(),
67 project_type: String::new(),
68 directory_structure: String::new(),
69 config_files: Vec::new(),
70 readme: None,
71 source_files: Vec::new(),
72 }
73 }
74}
75
76pub fn build_overview_prompt(context: &OverviewContext) -> String {
79 let mut prompt = String::new();
80
81 prompt
83 .push_str("你是一个项目分析专家,请根据提供的项目信息生成项目概览文档(MATRIX.md)。\n\n");
84 prompt.push_str("要求:\n");
85 prompt.push_str("- 使用 Markdown 格式\n");
86 prompt.push_str("- 简洁明了,突出重点\n");
87 prompt.push_str("- 包含项目定位、技术栈、架构要点\n");
88 prompt.push_str("- 便于 AI 助手快速理解项目\n");
89 prompt.push_str("\n---\n\n");
90
91 prompt.push_str(&format!("项目名称: {}\n", context.project_name));
93 prompt.push_str(&format!("项目类型: {}\n\n", context.project_type));
94
95 prompt.push_str("## 目录结构\n\n");
97 prompt.push_str("```\n");
98 prompt.push_str(&context.directory_structure);
99 prompt.push_str("```\n\n");
100
101 if !context.config_files.is_empty() {
103 prompt.push_str("## 配置文件\n\n");
104 for (filename, content) in &context.config_files {
105 prompt.push_str(&format!("### {}\n\n", filename));
106 prompt.push_str("```\n");
107 prompt.push_str(content);
108 prompt.push_str("\n```\n\n");
109 }
110 }
111
112 if let Some(readme) = &context.readme {
114 prompt.push_str("## README.md (开头部分)\n\n");
115 prompt.push_str(readme);
116 prompt.push_str("\n\n");
117 }
118
119 if !context.source_files.is_empty() {
121 prompt.push_str("## 关键源文件\n\n");
122 for (filename, content) in &context.source_files {
123 prompt.push_str(&format!("### {}\n\n", filename));
124 prompt.push_str("```\n");
125 prompt.push_str(content);
126 prompt.push_str("\n```\n\n");
127 }
128 }
129
130 prompt.push_str("---\n\n");
131 prompt.push_str("请根据以上信息生成 MATRIX.md 文档内容:\n");
132
133 prompt
134}
135
136use crate::skills::Skill;
137use std::path::PathBuf;
138
139fn join_lines(items: &[String]) -> String {
141 items.join("\n")
142}
143
144pub fn build_system_prompt(
146 profile: &PromptProfile,
147 skills: &[Skill],
148 project_overview: Option<&str>,
149 memory_summary: Option<&str>,
150) -> String {
151 build_system_prompt_with_workflows(
152 profile,
153 skills,
154 project_overview,
155 memory_summary,
156 None,
157 None,
158 )
159}
160
161pub fn build_system_prompt_with_workflows(
167 profile: &PromptProfile,
168 skills: &[Skill],
169 project_overview: Option<&str>,
170 memory_summary: Option<&str>,
171 project_path: Option<&PathBuf>,
172 lsp_servers: Option<&[crate::lsp::LspServerInfo]>,
173) -> String {
174 build_system_prompt_with_workflows_and_lsp(
175 profile,
176 skills,
177 project_overview,
178 memory_summary,
179 project_path,
180 lsp_servers,
181 None,
182 )
183}
184
185pub fn build_system_prompt_with_workflows_and_lsp(
191 profile: &PromptProfile,
192 skills: &[Skill],
193 project_overview: Option<&str>,
194 memory_summary: Option<&str>,
195 project_path: Option<&PathBuf>,
196 lsp_servers: Option<&[crate::lsp::LspServerInfo]>,
197 lsp_registry: Option<std::sync::Arc<crate::lsp::LspClientRegistry>>,
198) -> String {
199 let has_codegraph = project_path
201 .map(|p| crate::tools::codegraph::should_inject_codegraph_tools(p))
202 .unwrap_or(false);
203
204 let mut builder = PromptBuilder::new(
206 project_path
207 .cloned()
208 .unwrap_or_else(|| std::env::current_dir().unwrap()),
209 )
210 .profile(*profile)
211 .no_context();
212
213 for (name, content) in constants::get_static_sections(has_codegraph) {
215 builder = builder.add_static(name, content);
216 }
217
218 for (name, content) in constants::get_memory_sections() {
220 builder = builder.add_static(name, content);
221 }
222
223 let mut orchestrator = builder.build();
225
226 let assembled = orchestrator.assemble();
228 let mut parts = vec![assembled.prompt];
229
230 let tools_prompt = crate::tools::generate_tools_prompt_with_path_and_lsp(project_path, lsp_registry);
232 parts.push(tools_prompt);
233
234 if !skills.is_empty() {
236 let skills_lines: Vec<String> = skills
237 .iter()
238 .map(|s| {
239 if let Some(trigger) = &s.trigger {
240 format!(
241 " - {}: {}\n 触发场景: {}",
242 s.name, s.description, trigger
243 )
244 } else {
245 format!(" - {}: {}", s.name, s.description)
246 }
247 })
248 .collect();
249 let skills_section = format!(
250 "[AVAILABLE SKILLS]\n可用技能(使用 skill 工具调用):\n\n{}",
251 join_lines(&skills_lines)
252 );
253 parts.push(skills_section);
254 }
255
256 if let Some(path) = project_path {
258 let registry = crate::workflow::WorkflowRegistry::new(Some(path));
259 if !registry.is_empty() {
260 let workflows_lines: Vec<String> = registry
261 .list()
262 .iter()
263 .map(|w| {
264 let desc = w.description.as_deref().unwrap_or(&w.name);
265 if w.required_inputs.is_empty() {
266 format!(" - {}: {}", w.id, desc)
267 } else {
268 format!(
269 " - {}: {} (需要输入: {})",
270 w.id,
271 desc,
272 w.required_inputs.join(", ")
273 )
274 }
275 })
276 .collect();
277 let workflows_section = format!(
278 "[AVAILABLE WORKFLOWS]\n可执行的自动化流程(使用 workflow_run 工具调用):\n\n{}",
279 join_lines(&workflows_lines)
280 );
281 parts.push(workflows_section);
282 }
283 }
284
285 if let Some(overview) = project_overview {
287 parts.push(format!("[PROJECT CONTEXT]\n{}", overview));
288 }
289
290 if let Some(memory) = memory_summary {
292 parts.push(format!(
293 "{}\n{}\n\n{}",
294 MEMORY_SUMMARY_HEADER, MEMORY_USAGE_INSTRUCTIONS, memory
295 ));
296 }
297
298 if let Some(servers) = lsp_servers {
300 if let Some(lsp_section) = crate::prompt::constants::get_lsp_section(servers) {
301 parts.push(lsp_section);
302 }
303 }
304
305 parts.join("\n\n")
306}