use regex::Regex;
const PLAN_KEYWORDS: &[&str] = &[
"make a plan",
"create a plan",
"plan for",
"plan to implement",
"plan out",
"planning",
"create plan",
"make plan",
];
const READ_FILE_KEYWORDS: &[&str] = &[
"read file",
"read the file",
"show me file",
"show me the file",
"show file content",
"what's in",
"what is in",
"display file",
"view file",
"look at file",
"check file",
];
const SEARCH_KEYWORDS: &[&str] = &[
"search for",
"find",
"look for",
"grep",
"search code",
"find in files",
"search in",
"where is",
"locate",
];
const WRITE_FILE_KEYWORDS: &[&str] = &[
"create file",
"create a file",
"write file",
"write to file",
"make a file",
"make file",
"new file",
];
const EDIT_FILE_KEYWORDS: &[&str] = &[
"edit file",
"modify file",
"update file",
"change file",
"fix in file",
"update the file",
"modify the file",
];
const BASH_KEYWORDS: &[&str] = &[
"run command",
"execute command",
"run shell",
"shell command",
"terminal command",
"bash command",
];
const WEB_SEARCH_KEYWORDS: &[&str] = &[
"search online",
"search the web",
"google",
"search internet",
"find online",
"look up online",
"web search",
];
pub struct PromptAnalyzer {
plan_regex: Regex,
read_file_regex: Regex,
search_regex: Regex,
write_file_regex: Regex,
edit_file_regex: Regex,
bash_regex: Regex,
web_search_regex: Regex,
}
impl PromptAnalyzer {
pub fn new() -> Self {
Self {
plan_regex: Self::build_keyword_regex(PLAN_KEYWORDS),
read_file_regex: Self::build_keyword_regex(READ_FILE_KEYWORDS),
search_regex: Self::build_keyword_regex(SEARCH_KEYWORDS),
write_file_regex: Self::build_keyword_regex(WRITE_FILE_KEYWORDS),
edit_file_regex: Self::build_keyword_regex(EDIT_FILE_KEYWORDS),
bash_regex: Self::build_keyword_regex(BASH_KEYWORDS),
web_search_regex: Self::build_keyword_regex(WEB_SEARCH_KEYWORDS),
}
}
fn build_keyword_regex(keywords: &[&str]) -> Regex {
let pattern = keywords
.iter()
.map(|k| regex::escape(k))
.collect::<Vec<_>>()
.join("|");
Regex::new(&format!(r"(?i)\b({})\b", pattern)).expect("Failed to compile keyword regex")
}
pub fn analyze_and_transform(&self, prompt: &str) -> String {
let mut transformations = Vec::new();
let lower_prompt = prompt.to_lowercase();
if self.plan_regex.is_match(&lower_prompt) {
tracing::info!("🔍 Detected PLAN intent in prompt");
transformations.push(
"\n\n**CRITICAL**: You MUST use the `plan` tool now! \
DO NOT write text - CALL THE TOOL IMMEDIATELY:\n\
1. plan(operation='create', title='...', description='...')\n\
2. plan(operation='add_task', ...) for each task\n\
3. plan(operation='finalize')\n\
**START WITH THE FIRST TOOL CALL NOW!**",
);
}
if self.read_file_regex.is_match(&lower_prompt) {
tracing::info!("🔍 Detected READ_FILE intent in prompt");
transformations
.push("\n\n**TOOL HINT**: Use the `read_file` tool to read the contents of files.");
}
if self.search_regex.is_match(&lower_prompt) {
tracing::info!("🔍 Detected SEARCH/GREP intent in prompt");
transformations.push(
"\n\n**TOOL HINT**: Use the `grep` tool to search for patterns in files, \
or use `glob` to find files by pattern.",
);
}
if self.write_file_regex.is_match(&lower_prompt) {
tracing::info!("🔍 Detected WRITE_FILE intent in prompt");
transformations
.push("\n\n**TOOL HINT**: Use the `write_file` tool to create new files.");
}
if self.edit_file_regex.is_match(&lower_prompt) {
tracing::info!("🔍 Detected EDIT_FILE intent in prompt");
transformations
.push("\n\n**TOOL HINT**: Use the `edit_file` tool to modify existing files.");
}
if self.bash_regex.is_match(&lower_prompt) {
tracing::info!("🔍 Detected BASH intent in prompt");
transformations
.push("\n\n**TOOL HINT**: Use the `bash` tool to execute shell commands.");
}
if self.web_search_regex.is_match(&lower_prompt) {
tracing::info!("🔍 Detected WEB_SEARCH intent in prompt");
transformations.push(
"\n\n**TOOL HINT**: Use the `web_search` tool to search the internet for \
real-time information.",
);
}
if !transformations.is_empty() {
let hint_section = transformations.join("");
format!("{}{}", prompt, hint_section)
} else {
prompt.to_string()
}
}
}
impl Default for PromptAnalyzer {
fn default() -> Self {
Self::new()
}
}