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