1use std::sync::Arc;
2
3use anyhow::{Context as _, Result};
4use fs::Fs;
5use gpui::{App, AppContext, Task};
6use prompt_store::SystemPromptRulesFile;
7use util::maybe;
8use worktree::Worktree;
9
10const RULES_FILE_NAMES: [&'static str; 6] = [
11 ".rules",
12 ".cursorrules",
13 ".windsurfrules",
14 ".clinerules",
15 ".github/copilot-instructions.md",
16 "CLAUDE.md",
17];
18
19pub fn load_worktree_rules_file(
20 fs: Arc<dyn Fs>,
21 worktree: &Worktree,
22 cx: &App,
23) -> Option<Task<Result<SystemPromptRulesFile>>> {
24 let selected_rules_file = RULES_FILE_NAMES
25 .into_iter()
26 .filter_map(|name| {
27 worktree
28 .entry_for_path(name)
29 .filter(|entry| entry.is_file())
30 .map(|entry| (entry.path.clone(), worktree.absolutize(&entry.path)))
31 })
32 .next();
33
34 // Note that Cline supports `.clinerules` being a directory, but that is not currently
35 // supported. This doesn't seem to occur often in GitHub repositories.
36 selected_rules_file.map(|(path_in_worktree, abs_path)| {
37 let fs = fs.clone();
38 cx.background_spawn(maybe!(async move {
39 let abs_path = abs_path?;
40 let text = fs
41 .load(&abs_path)
42 .await
43 .with_context(|| format!("Failed to load assistant rules file {:?}", abs_path))?;
44 anyhow::Ok(SystemPromptRulesFile {
45 path_in_worktree,
46 abs_path: abs_path.into(),
47 text: text.trim().to_string(),
48 })
49 }))
50 })
51}