Pull out plain rules file loading code into a new agent_rules
crate (#28383)
Also renames for rules file templated into the system prompt Release Notes: - N/A
This commit is contained in:
parent
020a1071d5
commit
301fc7cd7b
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -52,6 +52,7 @@ dependencies = [
|
|||||||
name = "agent"
|
name = "agent"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"agent_rules",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assistant_context_editor",
|
"assistant_context_editor",
|
||||||
"assistant_settings",
|
"assistant_settings",
|
||||||
@ -161,6 +162,19 @@ dependencies = [
|
|||||||
"workspace-hack",
|
"workspace-hack",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "agent_rules"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"fs",
|
||||||
|
"gpui",
|
||||||
|
"indoc",
|
||||||
|
"prompt_store",
|
||||||
|
"util",
|
||||||
|
"worktree",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.8"
|
version = "0.7.8"
|
||||||
|
@ -3,6 +3,7 @@ resolver = "2"
|
|||||||
members = [
|
members = [
|
||||||
"crates/activity_indicator",
|
"crates/activity_indicator",
|
||||||
"crates/agent",
|
"crates/agent",
|
||||||
|
"crates/agent_rules",
|
||||||
"crates/anthropic",
|
"crates/anthropic",
|
||||||
"crates/askpass",
|
"crates/askpass",
|
||||||
"crates/assets",
|
"crates/assets",
|
||||||
@ -209,6 +210,7 @@ edition = "2024"
|
|||||||
|
|
||||||
activity_indicator = { path = "crates/activity_indicator" }
|
activity_indicator = { path = "crates/activity_indicator" }
|
||||||
agent = { path = "crates/agent" }
|
agent = { path = "crates/agent" }
|
||||||
|
agent_rules = { path = "crates/agent_rules" }
|
||||||
ai = { path = "crates/ai" }
|
ai = { path = "crates/ai" }
|
||||||
anthropic = { path = "crates/anthropic" }
|
anthropic = { path = "crates/anthropic" }
|
||||||
askpass = { path = "crates/askpass" }
|
askpass = { path = "crates/askpass" }
|
||||||
|
@ -155,7 +155,7 @@ There are rules that apply to these root directories:
|
|||||||
{{#each worktrees}}
|
{{#each worktrees}}
|
||||||
{{#if rules_file}}
|
{{#if rules_file}}
|
||||||
|
|
||||||
`{{root_name}}/{{rules_file.rel_path}}`:
|
`{{root_name}}/{{rules_file.path_in_worktree}}`:
|
||||||
|
|
||||||
``````
|
``````
|
||||||
{{{rules_file.text}}}
|
{{{rules_file.text}}}
|
||||||
|
@ -19,6 +19,7 @@ test-support = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
agent_rules.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
assistant_context_editor.workspace = true
|
assistant_context_editor.workspace = true
|
||||||
assistant_settings.workspace = true
|
assistant_settings.workspace = true
|
||||||
|
@ -2581,7 +2581,7 @@ impl ActiveThread {
|
|||||||
let label_text = match rules_files.as_slice() {
|
let label_text = match rules_files.as_slice() {
|
||||||
&[] => return div().into_any(),
|
&[] => return div().into_any(),
|
||||||
&[rules_file] => {
|
&[rules_file] => {
|
||||||
format!("Using {:?} file", rules_file.rel_path)
|
format!("Using {:?} file", rules_file.path_in_worktree)
|
||||||
}
|
}
|
||||||
rules_files => {
|
rules_files => {
|
||||||
format!("Using {} rules files", rules_files.len())
|
format!("Using {} rules files", rules_files.len())
|
||||||
|
@ -3,6 +3,7 @@ use std::io::Write;
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use agent_rules::load_worktree_rules_file;
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use assistant_settings::AssistantSettings;
|
use assistant_settings::AssistantSettings;
|
||||||
use assistant_tool::{ActionLog, Tool, ToolWorkingSet};
|
use assistant_tool::{ActionLog, Tool, ToolWorkingSet};
|
||||||
@ -21,13 +22,11 @@ use language_model::{
|
|||||||
};
|
};
|
||||||
use project::git_store::{GitStore, GitStoreCheckpoint, RepositoryState};
|
use project::git_store::{GitStore, GitStoreCheckpoint, RepositoryState};
|
||||||
use project::{Project, Worktree};
|
use project::{Project, Worktree};
|
||||||
use prompt_store::{
|
use prompt_store::{AssistantSystemPromptContext, PromptBuilder, WorktreeInfoForSystemPrompt};
|
||||||
AssistantSystemPromptContext, PromptBuilder, RulesFile, WorktreeInfoForSystemPrompt,
|
|
||||||
};
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use util::{ResultExt as _, TryFutureExt as _, maybe, post_inc};
|
use util::{ResultExt as _, TryFutureExt as _, post_inc};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::context::{AssistantContext, ContextId, format_context_as_string};
|
use crate::context::{AssistantContext, ContextId, format_context_as_string};
|
||||||
@ -854,41 +853,20 @@ impl Thread {
|
|||||||
let root_name = worktree.root_name().into();
|
let root_name = worktree.root_name().into();
|
||||||
let abs_path = worktree.abs_path();
|
let abs_path = worktree.abs_path();
|
||||||
|
|
||||||
// Note that Cline supports `.clinerules` being a directory, but that is not currently
|
let rules_task = load_worktree_rules_file(fs, worktree, cx);
|
||||||
// supported. This doesn't seem to occur often in GitHub repositories.
|
let Some(rules_task) = rules_task else {
|
||||||
const RULES_FILE_NAMES: [&'static str; 6] = [
|
return Task::ready((
|
||||||
".rules",
|
WorktreeInfoForSystemPrompt {
|
||||||
".cursorrules",
|
root_name,
|
||||||
".windsurfrules",
|
abs_path,
|
||||||
".clinerules",
|
rules_file: None,
|
||||||
".github/copilot-instructions.md",
|
},
|
||||||
"CLAUDE.md",
|
None,
|
||||||
];
|
));
|
||||||
let selected_rules_file = RULES_FILE_NAMES
|
};
|
||||||
.into_iter()
|
|
||||||
.filter_map(|name| {
|
|
||||||
worktree
|
|
||||||
.entry_for_path(name)
|
|
||||||
.filter(|entry| entry.is_file())
|
|
||||||
.map(|entry| (entry.path.clone(), worktree.absolutize(&entry.path)))
|
|
||||||
})
|
|
||||||
.next();
|
|
||||||
|
|
||||||
if let Some((rel_rules_path, abs_rules_path)) = selected_rules_file {
|
|
||||||
cx.spawn(async move |_| {
|
cx.spawn(async move |_| {
|
||||||
let rules_file_result = maybe!(async move {
|
let (rules_file, rules_file_error) = match rules_task.await {
|
||||||
let abs_rules_path = abs_rules_path?;
|
|
||||||
let text = fs.load(&abs_rules_path).await.with_context(|| {
|
|
||||||
format!("Failed to load assistant rules file {:?}", abs_rules_path)
|
|
||||||
})?;
|
|
||||||
anyhow::Ok(RulesFile {
|
|
||||||
rel_path: rel_rules_path,
|
|
||||||
abs_path: abs_rules_path.into(),
|
|
||||||
text: text.trim().to_string(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
let (rules_file, rules_file_error) = match rules_file_result {
|
|
||||||
Ok(rules_file) => (Some(rules_file), None),
|
Ok(rules_file) => (Some(rules_file), None),
|
||||||
Err(err) => (
|
Err(err) => (
|
||||||
None,
|
None,
|
||||||
@ -905,16 +883,6 @@ impl Thread {
|
|||||||
};
|
};
|
||||||
(worktree_info, rules_file_error)
|
(worktree_info, rules_file_error)
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
Task::ready((
|
|
||||||
WorktreeInfoForSystemPrompt {
|
|
||||||
root_name,
|
|
||||||
abs_path,
|
|
||||||
rules_file: None,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_to_model(
|
pub fn send_to_model(
|
||||||
|
24
crates/agent_rules/Cargo.toml
Normal file
24
crates/agent_rules/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "agent_rules"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/agent_rules.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
fs.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
prompt_store.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
worktree.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
indoc.workspace = true
|
1
crates/agent_rules/LICENSE-GPL
Symbolic link
1
crates/agent_rules/LICENSE-GPL
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../LICENSE-GPL
|
51
crates/agent_rules/src/agent_rules.rs
Normal file
51
crates/agent_rules/src/agent_rules.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::{Context as _, Result};
|
||||||
|
use fs::Fs;
|
||||||
|
use gpui::{App, AppContext, Task};
|
||||||
|
use prompt_store::SystemPromptRulesFile;
|
||||||
|
use util::maybe;
|
||||||
|
use worktree::Worktree;
|
||||||
|
|
||||||
|
const RULES_FILE_NAMES: [&'static str; 6] = [
|
||||||
|
".rules",
|
||||||
|
".cursorrules",
|
||||||
|
".windsurfrules",
|
||||||
|
".clinerules",
|
||||||
|
".github/copilot-instructions.md",
|
||||||
|
"CLAUDE.md",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn load_worktree_rules_file(
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
worktree: &Worktree,
|
||||||
|
cx: &App,
|
||||||
|
) -> Option<Task<Result<SystemPromptRulesFile>>> {
|
||||||
|
let selected_rules_file = RULES_FILE_NAMES
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|name| {
|
||||||
|
worktree
|
||||||
|
.entry_for_path(name)
|
||||||
|
.filter(|entry| entry.is_file())
|
||||||
|
.map(|entry| (entry.path.clone(), worktree.absolutize(&entry.path)))
|
||||||
|
})
|
||||||
|
.next();
|
||||||
|
|
||||||
|
// Note that Cline supports `.clinerules` being a directory, but that is not currently
|
||||||
|
// supported. This doesn't seem to occur often in GitHub repositories.
|
||||||
|
selected_rules_file.map(|(path_in_worktree, abs_path)| {
|
||||||
|
let fs = fs.clone();
|
||||||
|
cx.background_spawn(maybe!(async move {
|
||||||
|
let abs_path = abs_path?;
|
||||||
|
let text = fs
|
||||||
|
.load(&abs_path)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("Failed to load assistant rules file {:?}", abs_path))?;
|
||||||
|
anyhow::Ok(SystemPromptRulesFile {
|
||||||
|
path_in_worktree,
|
||||||
|
abs_path: abs_path.into(),
|
||||||
|
text: text.trim().to_string(),
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
@ -38,12 +38,12 @@ impl AssistantSystemPromptContext {
|
|||||||
pub struct WorktreeInfoForSystemPrompt {
|
pub struct WorktreeInfoForSystemPrompt {
|
||||||
pub root_name: String,
|
pub root_name: String,
|
||||||
pub abs_path: Arc<Path>,
|
pub abs_path: Arc<Path>,
|
||||||
pub rules_file: Option<RulesFile>,
|
pub rules_file: Option<SystemPromptRulesFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct RulesFile {
|
pub struct SystemPromptRulesFile {
|
||||||
pub rel_path: Arc<Path>,
|
pub path_in_worktree: Arc<Path>,
|
||||||
pub abs_path: Arc<Path>,
|
pub abs_path: Arc<Path>,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user