opys_engine/commands/
agent_rules.rs1use std::path::Path;
7
8use crate::cli::AgentTool;
9use crate::error::{usage, Result};
10use crate::templates::AGENT_RULE;
11use crate::Ctx;
12
13const CURSOR_FRONTMATTER: &str = "---\ndescription: opys feature inventory — operate the opys CLI when the project has a opys/ inventory.\nglobs: opys/**\nalwaysApply: false\n---\n\n";
15const COPILOT_FRONTMATTER: &str = "---\napplyTo: \"opys/**\"\n---\n\n";
17
18fn target(tool: AgentTool) -> (&'static str, &'static str) {
20 match tool {
21 AgentTool::Cursor => (".cursor/rules/opys.mdc", CURSOR_FRONTMATTER),
22 AgentTool::Windsurf => (".windsurf/rules/opys.md", ""),
23 AgentTool::Cline => (".clinerules/opys.md", ""),
24 AgentTool::Copilot => (
25 ".github/instructions/opys.instructions.md",
26 COPILOT_FRONTMATTER,
27 ),
28 AgentTool::Kiro => (".kiro/steering/opys.md", ""),
29 AgentTool::All => unreachable!("expanded before target()"),
30 }
31}
32
33pub fn run(ctx: &Ctx, tool: AgentTool, stdout: bool) -> Result<()> {
34 let tools = match tool {
35 AgentTool::All => vec![
36 AgentTool::Cursor,
37 AgentTool::Windsurf,
38 AgentTool::Cline,
39 AgentTool::Copilot,
40 AgentTool::Kiro,
41 ],
42 t => vec![t],
43 };
44 if stdout && tools.len() > 1 {
45 return Err(usage("--stdout works with a single --tool, not 'all'"));
46 }
47
48 for t in tools {
49 let (rel, frontmatter) = target(t);
50 let content = format!("{frontmatter}{AGENT_RULE}");
51 if stdout {
52 print!("{content}");
53 } else {
54 let path = Path::new(&ctx.root).join(rel);
55 if let Some(parent) = path.parent() {
56 std::fs::create_dir_all(parent)?;
57 }
58 std::fs::write(&path, content)?;
59 println!("wrote {}", path.display());
60 }
61 }
62 Ok(())
63}