1use super::tool::Tool;
7
8pub fn generate_bootstrap_markdown(tool: Tool) -> String {
10 let has_mcp = tool.supports_mcp();
11 let mut content = String::new();
12
13 content.push_str("# ACP Context\n\n");
15 content.push_str(
16 "This project uses **AI Context Protocol (ACP)** for structured AI assistance.\n\n",
17 );
18
19 content.push_str("## Before Modifying Files\n\n");
21 content.push_str("**ALWAYS check constraints before editing any file:**\n\n");
22
23 if has_mcp {
24 content.push_str("```\n");
25 content.push_str("acp_check_constraints({ path: \"path/to/file\" })\n");
26 content.push_str("```\n\n");
27 } else {
28 content.push_str("```bash\n");
29 content.push_str("acp check path/to/file\n");
30 content.push_str("# OR query directly:\n");
31 content.push_str("jq '.constraints.by_file[\"path/to/file\"]' .acp/acp.cache.json\n");
32 content.push_str("```\n\n");
33 }
34
35 content.push_str("### Constraint Levels\n\n");
37 content.push_str("| Level | Action Required |\n");
38 content.push_str("|-------|----------------|\n");
39 content.push_str("| `frozen` | **NEVER** modify - ask user for alternatives |\n");
40 content.push_str("| `restricted` | Explain changes and get approval first |\n");
41 content.push_str("| `approval-required` | Ask before any change |\n");
42 content.push_str("| `tests-required` | Include tests with changes |\n");
43 content.push_str("| `docs-required` | Update documentation |\n\n");
44
45 content.push_str("## Data Files\n\n");
47 content.push_str("| File | Contents |\n");
48 content.push_str("|------|----------|\n");
49 content.push_str("| `.acp/acp.cache.json` | Indexed codebase structure, symbols, domains |\n");
50 content.push_str("| `.acp/acp.vars.json` | Token-efficient variable definitions |\n");
51 content.push_str("| `.acp.config.json` | Project configuration |\n\n");
52
53 content.push_str("## Getting More Context\n\n");
55
56 if has_mcp {
57 content.push_str("Use MCP tools for queries:\n\n");
58 content.push_str("- `acp_get_architecture()` - Project overview\n");
59 content.push_str("- `acp_get_file_context({ path })` - File metadata\n");
60 content.push_str("- `acp_get_symbol_context({ name })` - Symbol details\n");
61 content.push_str("- `acp_get_hotpaths()` - Critical code paths\n");
62 content.push_str("- `acp_expand_variable({ name })` - Expand $VAR references\n");
63 } else {
64 content.push_str("Use CLI or jq for queries:\n\n");
65 content.push_str("```bash\n");
66 content.push_str("# Query symbol details\n");
67 content.push_str("acp query symbol <name>\n\n");
68 content.push_str("# Query file metadata\n");
69 content.push_str("acp query file <path>\n\n");
70 content.push_str("# List domains\n");
71 content.push_str("jq '.domains | keys' .acp/acp.cache.json\n\n");
72 content.push_str("# Find frozen files\n");
73 content.push_str("jq '.constraints.by_lock_level.frozen // []' .acp/acp.cache.json\n");
74 content.push_str("```\n");
75 }
76
77 content
78}
79
80pub fn generate_bootstrap_json(tool: Tool) -> String {
82 let markdown = generate_bootstrap_markdown(tool);
83
84 let config = serde_json::json!({
86 "_acp": {
87 "version": "1.0.0",
88 "tool": tool.name()
89 },
90 "systemMessage": format!("{}", markdown)
91 });
92
93 serde_json::to_string_pretty(&config).unwrap_or_default()
94}
95
96pub fn generate_bootstrap_yaml(tool: Tool) -> String {
98 let markdown = generate_bootstrap_markdown(tool);
99
100 let indented = markdown
102 .lines()
103 .map(|line| format!(" {}", line))
104 .collect::<Vec<_>>()
105 .join("\n");
106
107 format!(
108 "# ACP Configuration for {}\n\
109 # Generated by AI Context Protocol\n\n\
110 system-prompt: |\n{}\n",
111 tool.name(),
112 indented
113 )
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_generate_markdown_without_mcp() {
122 let content = generate_bootstrap_markdown(Tool::Cursor);
123 assert!(content.contains("# ACP Context"));
124 assert!(content.contains("acp check path/to/file"));
125 assert!(!content.contains("acp_check_constraints"));
126 }
127
128 #[test]
129 fn test_generate_markdown_with_mcp() {
130 let content = generate_bootstrap_markdown(Tool::ClaudeCode);
131 assert!(content.contains("# ACP Context"));
132 assert!(content.contains("acp_check_constraints"));
133 assert!(content.contains("acp_get_architecture"));
134 }
135
136 #[test]
137 fn test_generate_json() {
138 let content = generate_bootstrap_json(Tool::Continue);
139 assert!(content.contains("\"_acp\""));
140 assert!(content.contains("\"systemMessage\""));
141 let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
143 assert!(parsed.get("systemMessage").is_some());
144 }
145
146 #[test]
147 fn test_generate_yaml() {
148 let content = generate_bootstrap_yaml(Tool::Aider);
149 assert!(content.contains("system-prompt: |"));
150 assert!(content.contains("ACP Configuration for Aider"));
151 }
152}