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 section;
19pub mod cache;
20pub mod context;
21pub mod orchestrator;
22pub mod preprocess;
23pub mod dump;
24pub mod constants;
25
26// Re-export main types
27pub use section::{PromptSection, SectionContent, SectionBuilder};
28pub use cache::{SectionCache, CacheKey, CachedEntry, CacheStats, global_cache, clear_global_cache, estimate_tokens};
29pub use context::{ContextInjector, UserContext, SystemContext, ProjectType};
30pub use orchestrator::{PromptOrchestrator, PromptProfile, PromptBuilder, AssembledPrompt, CACHE_BOUNDARY};
31pub use preprocess::{PreProcessHook, ProcessResult, SkillPattern, WorkflowTrigger, preprocess};
32pub use dump::{PromptDumper, DumpEntry, PromptAnalysis, DumpFileAnalysis, read_dump_file, analyze_dump_file};
33
34// Re-export constants (includes SECTION_*, MEMORY_*, MSG_*)
35pub use constants::*;
36
37// =============================================================================
38// Legacy Compatibility Layer
39// =============================================================================
40
41/// Legacy profile enum - alias to new PromptProfile
42pub use orchestrator::PromptProfile as LegacyPromptProfile;
43
44/// Legacy OverviewContext for project overview generation
45/// Used by overview.rs to generate MATRIX.md content
46#[derive(Debug, Clone)]
47pub struct OverviewContext {
48    pub project_name: String,
49    pub project_type: String,
50    pub directory_structure: String,
51    pub config_files: Vec<(String, String)>,  // (filename, content)
52    pub readme: Option<String>,
53    pub source_files: Vec<(String, String)>,  // (filename, content)
54}
55
56impl Default for OverviewContext {
57    fn default() -> Self {
58        Self {
59            project_name: String::new(),
60            project_type: String::new(),
61            directory_structure: String::new(),
62            config_files: Vec::new(),
63            readme: None,
64            source_files: Vec::new(),
65        }
66    }
67}
68
69/// Build overview prompt from context (legacy compatibility)
70/// This function generates the AI prompt for MATRIX.md generation
71pub fn build_overview_prompt(context: &OverviewContext) -> String {
72    let mut prompt = String::new();
73
74    // Header
75    prompt.push_str("你是一个项目分析专家,请根据提供的项目信息生成项目概览文档(MATRIX.md)。\n\n");
76    prompt.push_str("要求:\n");
77    prompt.push_str("- 使用 Markdown 格式\n");
78    prompt.push_str("- 简洁明了,突出重点\n");
79    prompt.push_str("- 包含项目定位、技术栈、架构要点\n");
80    prompt.push_str("- 便于 AI 助手快速理解项目\n");
81    prompt.push_str("\n---\n\n");
82
83    // Add project info
84    prompt.push_str(&format!("项目名称: {}\n", context.project_name));
85    prompt.push_str(&format!("项目类型: {}\n\n", context.project_type));
86
87    // Add directory structure
88    prompt.push_str("## 目录结构\n\n");
89    prompt.push_str("```\n");
90    prompt.push_str(&context.directory_structure);
91    prompt.push_str("```\n\n");
92
93    // Add config files
94    if !context.config_files.is_empty() {
95        prompt.push_str("## 配置文件\n\n");
96        for (filename, content) in &context.config_files {
97            prompt.push_str(&format!("### {}\n\n", filename));
98            prompt.push_str("```\n");
99            prompt.push_str(content);
100            prompt.push_str("\n```\n\n");
101        }
102    }
103
104    // Add README
105    if let Some(readme) = &context.readme {
106        prompt.push_str("## README.md (开头部分)\n\n");
107        prompt.push_str(readme);
108        prompt.push_str("\n\n");
109    }
110
111    // Add key source files
112    if !context.source_files.is_empty() {
113        prompt.push_str("## 关键源文件\n\n");
114        for (filename, content) in &context.source_files {
115            prompt.push_str(&format!("### {}\n\n", filename));
116            prompt.push_str("```\n");
117            prompt.push_str(content);
118            prompt.push_str("\n```\n\n");
119        }
120    }
121
122    prompt.push_str("---\n\n");
123    prompt.push_str("请根据以上信息生成 MATRIX.md 文档内容:\n");
124
125    prompt
126}
127
128use crate::skills::Skill;
129use std::path::PathBuf;
130
131// Helper for joining iterators
132fn join_lines(items: &[String]) -> String {
133    items.join("\n")
134}
135
136/// Build system prompt using the new orchestrator system
137pub fn build_system_prompt(
138    profile: &PromptProfile,
139    skills: &[Skill],
140    project_overview: Option<&str>,
141    memory_summary: Option<&str>,
142) -> String {
143    build_system_prompt_with_workflows(profile, skills, project_overview, memory_summary, None, None)
144}
145
146/// Build system prompt with workflow support and LSP injection
147/// 
148/// # Arguments
149/// - `lsp_servers`: Optional LSP server info list for dynamic injection
150pub fn build_system_prompt_with_workflows(
151    profile: &PromptProfile,
152    skills: &[Skill],
153    project_overview: Option<&str>,
154    memory_summary: Option<&str>,
155    project_path: Option<&PathBuf>,
156    lsp_servers: Option<&[crate::lsp::LspServerInfo]>,
157) -> String {
158    // Determine if CodeGraph is available
159    let has_codegraph = project_path
160        .map(|p| crate::tools::codegraph::should_inject_codegraph_tools(p))
161        .unwrap_or(false);
162
163    // Use orchestrator to build prompt
164    let mut builder = PromptBuilder::new(
165        project_path.cloned().unwrap_or_else(|| std::env::current_dir().unwrap())
166    )
167    .profile(*profile)
168    .no_context();
169
170    // Add static sections from constants
171    for (name, content) in constants::get_static_sections(has_codegraph) {
172        builder = builder.add_static(name, content);
173    }
174
175    // Add memory sections
176    for (name, content) in constants::get_memory_sections() {
177        builder = builder.add_static(name, content);
178    }
179
180    // Build orchestrator
181    let mut orchestrator = builder.build();
182
183    // Assemble base prompt
184    let assembled = orchestrator.assemble();
185    let mut parts = vec![assembled.prompt];
186
187    // Add tools prompt (dynamic based on project_path)
188    let tools_prompt = crate::tools::generate_tools_prompt_with_path(project_path);
189    parts.push(tools_prompt);
190
191    // Add Skills section if available
192    if !skills.is_empty() {
193        let skills_lines: Vec<String> = skills.iter()
194            .map(|s| {
195                if let Some(trigger) = &s.trigger {
196                    format!("  - {}: {}\n    触发场景: {}", s.name, s.description, trigger)
197                } else {
198                    format!("  - {}: {}", s.name, s.description)
199                }
200            })
201            .collect();
202        let skills_section = format!(
203            "[AVAILABLE SKILLS]\n可用技能(使用 skill 工具调用):\n\n{}",
204            join_lines(&skills_lines)
205        );
206        parts.push(skills_section);
207    }
208
209    // Add Workflows section if project_path available
210    if let Some(path) = project_path {
211        let registry = crate::workflow::WorkflowRegistry::new(Some(path));
212        if !registry.is_empty() {
213            let workflows_lines: Vec<String> = registry.list().iter()
214                .map(|w| {
215                    let desc = w.description.as_deref().unwrap_or(&w.name);
216                    if w.required_inputs.is_empty() {
217                        format!("  - {}: {}", w.id, desc)
218                    } else {
219                        format!("  - {}: {} (需要输入: {})", w.id, desc, w.required_inputs.join(", "))
220                    }
221                })
222                .collect();
223            let workflows_section = format!(
224                "[AVAILABLE WORKFLOWS]\n可执行的自动化流程(使用 workflow_run 工具调用):\n\n{}",
225                join_lines(&workflows_lines)
226            );
227            parts.push(workflows_section);
228        }
229    }
230
231    // Add project overview if provided
232    if let Some(overview) = project_overview {
233        parts.push(format!("[PROJECT CONTEXT]\n{}", overview));
234    }
235
236    // Add memory summary if provided
237    if let Some(memory) = memory_summary {
238        parts.push(format!(
239            "{}\n{}\n\n{}",
240            MEMORY_SUMMARY_HEADER,
241            MEMORY_USAGE_INSTRUCTIONS,
242            memory
243        ));
244    }
245
246    // Add LSP servers section if active (dynamic injection)
247    if let Some(servers) = lsp_servers {
248        if let Some(lsp_section) = crate::prompt::constants::get_lsp_section(servers) {
249            parts.push(lsp_section);
250        }
251    }
252
253    parts.join("\n\n")
254}