Skip to main content

matrixcode_core/prompt/
mod.rs

1//! Prompt Runtime System
2//!
3//! A dynamic prompt management system with:
4//! - Section-based composition (static/dynamic)
5//! - Cache management with boundary markers
6//! - Runtime context injection
7//! - Pre-processing hooks for Skills/Workflows triggering
8//! - Prompt export for observability
9//! - SessionStart hooks for dynamic injection
10//!
11//! # Migration Status
12//!
13//! This module provides both:
14//! 1. New dynamic system (PromptOrchestrator, PromptBuilder)
15//! 2. Legacy compatibility functions (build_system_prompt, etc.)
16//!
17//! The legacy functions are gradually being migrated to the new system.
18
19pub mod cache;
20pub mod constants;
21pub mod context;
22pub mod dump;
23pub mod hooks;
24pub mod orchestrator;
25pub mod preprocess;
26pub mod section;
27
28// Re-export main types
29pub 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    // Predefined sections
47    red_flags_section, skill_priority_section, skill_rules_section, tool_guidelines_section,
48};
49
50// Re-export constants (includes SECTION_*, MEMORY_*, MSG_*)
51pub use constants::*;
52
53// =============================================================================
54// Legacy Compatibility Layer
55// =============================================================================
56
57/// Legacy profile enum - alias to new PromptProfile
58pub use orchestrator::PromptProfile as LegacyPromptProfile;
59
60/// Legacy OverviewContext for project overview generation
61/// Used by overview.rs to generate MATRIX.md content
62#[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)>, // (filename, content)
68    pub readme: Option<String>,
69    pub source_files: Vec<(String, String)>, // (filename, content)
70}
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
85/// Build overview prompt from context (legacy compatibility)
86/// This function generates the AI prompt for MATRIX.md generation
87pub fn build_overview_prompt(context: &OverviewContext) -> String {
88    let mut prompt = String::new();
89
90    // Header
91    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    // Add project info
101    prompt.push_str(&format!("项目名称: {}\n", context.project_name));
102    prompt.push_str(&format!("项目类型: {}\n\n", context.project_type));
103
104    // Add directory structure
105    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    // Add config files
111    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    // Add README
122    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    // Add key source files
129    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
148// Helper for joining iterators
149fn join_lines(items: &[String]) -> String {
150    items.join("\n")
151}
152
153/// Build system prompt using the new orchestrator system
154pub 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
170/// Build system prompt with workflow support and LSP injection
171///
172/// # Arguments
173/// - `lsp_servers`: Optional LSP server info list for dynamic injection
174/// - `lsp_registry`: Optional LSP registry for tool prompt generation
175pub 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
194/// Build system prompt with workflow support and full LSP integration
195///
196/// # Arguments
197/// - `lsp_servers`: Optional LSP server info list for dynamic injection
198/// - `lsp_registry`: Optional LSP registry for tool prompt generation
199pub 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    // Determine if CodeGraph is available
209    let has_codegraph = project_path
210        .map(|p| crate::tools::codegraph::should_inject_codegraph_tools(p))
211        .unwrap_or(false);
212
213    // Use orchestrator to build prompt
214    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    // Add static sections from constants
223    for (name, content) in constants::get_static_sections(has_codegraph) {
224        builder = builder.add_static(name, content);
225    }
226
227    // Add memory sections
228    for (name, content) in constants::get_memory_sections() {
229        builder = builder.add_static(name, content);
230    }
231
232    // Build orchestrator
233    let mut orchestrator = builder.build();
234
235    // Assemble base prompt
236    let assembled = orchestrator.assemble();
237    let mut parts = vec![assembled.prompt];
238
239    // Add tools prompt (dynamic based on project_path and LSP registry)
240    let tools_prompt = crate::tools::generate_tools_prompt_with_path_and_lsp(project_path, lsp_registry);
241    parts.push(tools_prompt);
242
243    // Add Skills section if available
244    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    // Add Workflows section if project_path available
266    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    // Add project overview if provided
295    if let Some(overview) = project_overview {
296        parts.push(format!("[PROJECT CONTEXT]\n{}", overview));
297    }
298
299    // Add memory summary if provided
300    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    // Add LSP servers section if active (dynamic injection)
308    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}