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//!
10//! # Migration Status
11//!
12//! This module provides both:
13//! 1. New dynamic system (PromptOrchestrator, PromptBuilder)
14//! 2. Legacy compatibility functions (build_system_prompt, etc.)
15//!
16//! The legacy functions are gradually being migrated to the new system.
17
18pub mod cache;
19pub mod constants;
20pub mod context;
21pub mod dump;
22pub mod orchestrator;
23pub mod preprocess;
24pub mod section;
25
26// Re-export main types
27pub 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
41// Re-export constants (includes SECTION_*, MEMORY_*, MSG_*)
42pub use constants::*;
43
44// =============================================================================
45// Legacy Compatibility Layer
46// =============================================================================
47
48/// Legacy profile enum - alias to new PromptProfile
49pub use orchestrator::PromptProfile as LegacyPromptProfile;
50
51/// Legacy OverviewContext for project overview generation
52/// Used by overview.rs to generate MATRIX.md content
53#[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)>, // (filename, content)
59    pub readme: Option<String>,
60    pub source_files: Vec<(String, String)>, // (filename, content)
61}
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
76/// Build overview prompt from context (legacy compatibility)
77/// This function generates the AI prompt for MATRIX.md generation
78pub fn build_overview_prompt(context: &OverviewContext) -> String {
79    let mut prompt = String::new();
80
81    // Header
82    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    // Add project info
92    prompt.push_str(&format!("项目名称: {}\n", context.project_name));
93    prompt.push_str(&format!("项目类型: {}\n\n", context.project_type));
94
95    // Add directory structure
96    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    // Add config files
102    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    // Add README
113    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    // Add key source files
120    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
139// Helper for joining iterators
140fn join_lines(items: &[String]) -> String {
141    items.join("\n")
142}
143
144/// Build system prompt using the new orchestrator system
145pub 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
161/// Build system prompt with workflow support and LSP injection
162///
163/// # Arguments
164/// - `lsp_servers`: Optional LSP server info list for dynamic injection
165/// - `lsp_registry`: Optional LSP registry for tool prompt generation
166pub 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
185/// Build system prompt with workflow support and full LSP integration
186///
187/// # Arguments
188/// - `lsp_servers`: Optional LSP server info list for dynamic injection
189/// - `lsp_registry`: Optional LSP registry for tool prompt generation
190pub 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    // Determine if CodeGraph is available
200    let has_codegraph = project_path
201        .map(|p| crate::tools::codegraph::should_inject_codegraph_tools(p))
202        .unwrap_or(false);
203
204    // Use orchestrator to build prompt
205    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    // Add static sections from constants
214    for (name, content) in constants::get_static_sections(has_codegraph) {
215        builder = builder.add_static(name, content);
216    }
217
218    // Add memory sections
219    for (name, content) in constants::get_memory_sections() {
220        builder = builder.add_static(name, content);
221    }
222
223    // Build orchestrator
224    let mut orchestrator = builder.build();
225
226    // Assemble base prompt
227    let assembled = orchestrator.assemble();
228    let mut parts = vec![assembled.prompt];
229
230    // Add tools prompt (dynamic based on project_path and LSP registry)
231    let tools_prompt = crate::tools::generate_tools_prompt_with_path_and_lsp(project_path, lsp_registry);
232    parts.push(tools_prompt);
233
234    // Add Skills section if available
235    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    // Add Workflows section if project_path available
257    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    // Add project overview if provided
286    if let Some(overview) = project_overview {
287        parts.push(format!("[PROJECT CONTEXT]\n{}", overview));
288    }
289
290    // Add memory summary if provided
291    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    // Add LSP servers section if active (dynamic injection)
299    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}