Skip to content

Commit 759c3aa

Browse files
committed
docs: clarify AGENTS.md filtering
Signed-off-by: Hiroki Osame <hiroki.osame@gmail.com>
1 parent ba4f17a commit 759c3aa

8 files changed

Lines changed: 91 additions & 81 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/agents/claude.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ impl AgentRuleGenerator for ClaudeGenerator {
7171
if let Ok(skill_files) = claude_skills::generate_skills_for_optional_rules(
7272
&filtered_source_files,
7373
current_dir,
74-
)
75-
{
74+
) {
7675
all_files.extend(skill_files);
7776
}
7877
}
@@ -281,10 +280,8 @@ mod tests {
281280
assert!(
282281
claude_content.contains("@ai-rules/.generated-ai-rules/ai-rules-generated-always1.md")
283282
);
284-
assert!(
285-
claude_content
286-
.contains("@ai-rules/.generated-ai-rules/ai-rules-generated-optional-claude.md")
287-
);
283+
assert!(claude_content
284+
.contains("@ai-rules/.generated-ai-rules/ai-rules-generated-optional-claude.md"));
288285
}
289286

290287
#[test]

src/agents/single_file_based.rs

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,12 @@ mod tests {
190190
),
191191
];
192192

193-
let result =
194-
generate_agent_file_contents(
195-
&source_files,
196-
temp_dir.path(),
197-
"CLAUDE.md",
198-
AGENTS_MD_GROUP_NAME,
199-
);
193+
let result = generate_agent_file_contents(
194+
&source_files,
195+
temp_dir.path(),
196+
"CLAUDE.md",
197+
AGENTS_MD_GROUP_NAME,
198+
);
200199

201200
assert_eq!(result.len(), 1);
202201
let expected_path = temp_dir.path().join("CLAUDE.md");
@@ -229,13 +228,12 @@ mod tests {
229228
),
230229
];
231230

232-
let result =
233-
generate_agent_file_contents(
234-
&source_files,
235-
temp_dir.path(),
236-
"CLAUDE.md",
237-
AGENTS_MD_GROUP_NAME,
238-
);
231+
let result = generate_agent_file_contents(
232+
&source_files,
233+
temp_dir.path(),
234+
"CLAUDE.md",
235+
AGENTS_MD_GROUP_NAME,
236+
);
239237

240238
assert_eq!(result.len(), 1);
241239
let expected_path = temp_dir.path().join("CLAUDE.md");
@@ -275,13 +273,12 @@ mod tests {
275273
),
276274
];
277275

278-
let result =
279-
generate_agent_file_contents(
280-
&source_files,
281-
temp_dir.path(),
282-
"CLAUDE.md",
283-
AGENTS_MD_GROUP_NAME,
284-
);
276+
let result = generate_agent_file_contents(
277+
&source_files,
278+
temp_dir.path(),
279+
"CLAUDE.md",
280+
AGENTS_MD_GROUP_NAME,
281+
);
285282

286283
assert_eq!(result.len(), 1);
287284
let expected_path = temp_dir.path().join("CLAUDE.md");
@@ -326,14 +323,13 @@ mod tests {
326323
"rule1 body",
327324
)];
328325

329-
let result =
330-
check_in_sync(
331-
&source_files,
332-
temp_dir.path(),
333-
"CLAUDE.md",
334-
AGENTS_MD_GROUP_NAME,
335-
)
336-
.unwrap();
326+
let result = check_in_sync(
327+
&source_files,
328+
temp_dir.path(),
329+
"CLAUDE.md",
330+
AGENTS_MD_GROUP_NAME,
331+
)
332+
.unwrap();
337333

338334
assert!(!result)
339335
}
@@ -351,14 +347,13 @@ mod tests {
351347

352348
create_file(temp_dir.path(), "CLAUDE.md", "wrong content");
353349

354-
let result =
355-
check_in_sync(
356-
&source_files,
357-
temp_dir.path(),
358-
"CLAUDE.md",
359-
AGENTS_MD_GROUP_NAME,
360-
)
361-
.unwrap();
350+
let result = check_in_sync(
351+
&source_files,
352+
temp_dir.path(),
353+
"CLAUDE.md",
354+
AGENTS_MD_GROUP_NAME,
355+
)
356+
.unwrap();
362357

363358
assert!(!result);
364359
}
@@ -386,14 +381,13 @@ mod tests {
386381
let expected_content = "@ai-rules/.generated-ai-rules/ai-rules-generated-always1.md\n\n@ai-rules/.generated-ai-rules/ai-rules-generated-optional-agents-md.md\n";
387382
create_file(temp_dir.path(), "CLAUDE.md", expected_content);
388383

389-
let result =
390-
check_in_sync(
391-
&source_files,
392-
temp_dir.path(),
393-
"CLAUDE.md",
394-
AGENTS_MD_GROUP_NAME,
395-
)
396-
.unwrap();
384+
let result = check_in_sync(
385+
&source_files,
386+
temp_dir.path(),
387+
"CLAUDE.md",
388+
AGENTS_MD_GROUP_NAME,
389+
)
390+
.unwrap();
397391

398392
assert!(result);
399393
}

src/commands/generate.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::agents::AgentToolRegistry;
22
use crate::cli::ResolvedGenerateArgs;
3+
use crate::constants::AGENTS_MD_AGENTS;
4+
use crate::models::source_file::warn_on_partial_group_rules;
35
use crate::operations::source_reader::detect_symlink_mode;
46
use crate::operations::{self, GenerationResult};
57
use crate::utils::file_utils::{traverse_project_directories, write_directory_files};
@@ -137,6 +139,12 @@ fn collect_all_files_for_directory(
137139
let mut files_by_agent: HashMap<String, Vec<PathBuf>> = HashMap::new();
138140

139141
if !source_files.is_empty() {
142+
let has_agents_md_group = agents
143+
.iter()
144+
.any(|agent| AGENTS_MD_AGENTS.iter().any(|name| name == &agent.as_str()));
145+
if has_agents_md_group {
146+
warn_on_partial_group_rules(&source_files, &AGENTS_MD_AGENTS, "AGENTS.md");
147+
}
140148
let body_files = operations::generate_body_contents(&source_files, current_dir);
141149
directory_files_to_write.extend(body_files);
142150
let optional_files =
@@ -344,16 +352,8 @@ blockedAgents: [goose]
344352
Not for goose optional rule
345353
"#;
346354

347-
create_file(
348-
temp_dir.path(),
349-
"ai-rules/claude-only.md",
350-
claude_only_rule,
351-
);
352-
create_file(
353-
temp_dir.path(),
354-
"ai-rules/not-goose.md",
355-
no_goose_rule,
356-
);
355+
create_file(temp_dir.path(), "ai-rules/claude-only.md", claude_only_rule);
356+
create_file(temp_dir.path(), "ai-rules/not-goose.md", no_goose_rule);
357357

358358
let args = ResolvedGenerateArgs {
359359
agents: Some(vec!["claude".to_string(), "goose".to_string()]),

src/commands/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ mod tests {
9898
let out_of_sync_agents: Vec<_> = status_result
9999
.agent_statuses
100100
.iter()
101-
.filter_map(|(agent, in_sync)| (!in_sync).then(|| agent.clone()))
101+
.filter(|(_, in_sync)| !**in_sync)
102+
.map(|(agent, _)| agent.clone())
102103
.collect();
103104
assert!(
104105
out_of_sync_agents.is_empty(),

src/constants.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ pub const GENERATED_RULE_BODY_DIR: &str = ".generated-ai-rules";
44
pub const AGENTS_MD_FILENAME: &str = "AGENTS.md";
55
pub const AGENTS_MD_GROUP_NAME: &str = "agents-md";
66
pub const AGENTS_MD_AGENTS: [&str; 7] = [
7-
"amp",
8-
"cline",
9-
"codex",
10-
"copilot",
11-
"goose",
12-
"kilocode",
13-
"roo",
7+
"amp", "cline", "codex", "copilot", "goose", "kilocode", "roo",
148
];
159
pub const AI_RULE_CONFIG_FILENAME: &str = "ai-rules-config.yaml";
1610
pub const GENERATED_FILE_PREFIX: &str = "ai-rules-generated-";

src/models/source_file.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ impl SourceFile {
114114
true
115115
}
116116

117-
118117
pub fn from_file<P: AsRef<Path>>(file_path: P) -> Result<Self> {
119118
let path = file_path.as_ref();
120119
let content = std::fs::read_to_string(path)
@@ -232,6 +231,35 @@ pub fn filter_source_files_for_agent_group(
232231
.collect()
233232
}
234233

234+
pub fn warn_on_partial_group_rules(
235+
source_files: &[SourceFile],
236+
agent_names: &[&str],
237+
group_label: &str,
238+
) {
239+
for source_file in source_files {
240+
if source_file.applies_to_agents(agent_names) {
241+
continue;
242+
}
243+
244+
let applies_to_any = agent_names
245+
.iter()
246+
.any(|agent| source_file.applies_to_agent(agent));
247+
if !applies_to_any {
248+
continue;
249+
}
250+
251+
let identifier = if source_file.base_file_name.is_empty() {
252+
source_file.front_matter.description.as_str()
253+
} else {
254+
source_file.base_file_name.as_str()
255+
};
256+
eprintln!(
257+
"Warning: Rule '{}' applies to a subset of {} agents; it will be excluded from {}.",
258+
identifier, group_label, group_label
259+
);
260+
}
261+
}
262+
235263
#[cfg(test)]
236264
mod tests {
237265
use super::*;

src/operations/body_generator.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,8 @@ pub fn generate_optional_rule_files_for_agents(
108108
if AGENTS_MD_AGENTS.iter().any(|name| name == &agent.as_str()) {
109109
continue;
110110
}
111-
let filtered_source_files = crate::models::source_file::filter_source_files_for_agent(
112-
source_files,
113-
agent,
114-
);
111+
let filtered_source_files =
112+
crate::models::source_file::filter_source_files_for_agent(source_files, agent);
115113
let optional_content = generate_optional_rules_content(&filtered_source_files);
116114
if optional_content.is_empty() {
117115
continue;
@@ -161,12 +159,10 @@ mod tests {
161159
description: description.to_string(),
162160
always_apply,
163161
file_matching_patterns: None,
164-
allowed_agents: allowed_agents.map(|agents| {
165-
agents.into_iter().map(|agent| agent.to_string()).collect()
166-
}),
167-
blocked_agents: blocked_agents.map(|agents| {
168-
agents.into_iter().map(|agent| agent.to_string()).collect()
169-
}),
162+
allowed_agents: allowed_agents
163+
.map(|agents| agents.into_iter().map(|agent| agent.to_string()).collect()),
164+
blocked_agents: blocked_agents
165+
.map(|agents| agents.into_iter().map(|agent| agent.to_string()).collect()),
170166
},
171167
body: body.to_string(),
172168
base_file_name: base_file_name.to_string(),

0 commit comments

Comments
 (0)