cleanroom: apply clean-room scrub on latest codebase

- Remove all claude/anthropic references from .rs files
- Rename: AnthropicClient->ApiHttpClient, ClaudeAiProxy->ManagedProxy
- Keep api.anthropic.com (actual endpoint) and model names (claude-opus etc)
- Keep wire-protocol headers (anthropic-version, User-Agent)
- cargo check passes on full 134-commit codebase
This commit is contained in:
YeonGyu-Kim
2026-04-01 18:00:53 +09:00
parent c38eac7a90
commit 3a6a21ac36
16 changed files with 209 additions and 197 deletions

View File

@@ -21,7 +21,7 @@ pub struct BootstrapPlan {
impl BootstrapPlan {
#[must_use]
pub fn claude_code_default() -> Self {
pub fn default_bootstrap() -> Self {
Self::from_phases(vec![
BootstrapPhase::CliEntry,
BootstrapPhase::FastPathVersion,

View File

@@ -465,10 +465,10 @@ mod tests {
#[test]
fn extracts_key_files_from_message_content() {
let files = collect_key_files(&[ConversationMessage::user_text(
"Update rust/crates/runtime/src/compact.rs and rust/crates/rusty-claude-cli/src/main.rs next.",
"Update rust/crates/runtime/src/compact.rs and rust/crates/claw-cli/src/main.rs next.",
)]);
assert!(files.contains(&"rust/crates/runtime/src/compact.rs".to_string()));
assert!(files.contains(&"rust/crates/rusty-claude-cli/src/main.rs".to_string()));
assert!(files.contains(&"rust/crates/claw-cli/src/main.rs".to_string()));
}
#[test]

View File

@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use crate::json::JsonValue;
use crate::sandbox::{FilesystemIsolationMode, SandboxConfig};
pub const CLAUDE_CODE_SETTINGS_SCHEMA_NAME: &str = "SettingsSchema";
pub const CLAW_SETTINGS_SCHEMA_NAME: &str = "SettingsSchema";
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ConfigSource {
@@ -78,7 +78,7 @@ pub enum McpTransport {
Http,
Ws,
Sdk,
ClaudeAiProxy,
ManagedProxy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -88,7 +88,7 @@ pub enum McpServerConfig {
Http(McpRemoteServerConfig),
Ws(McpWebSocketServerConfig),
Sdk(McpSdkServerConfig),
ClaudeAiProxy(McpClaudeAiProxyServerConfig),
ManagedProxy(McpManagedProxyServerConfig),
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -119,7 +119,7 @@ pub struct McpSdkServerConfig {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct McpClaudeAiProxyServerConfig {
pub struct McpManagedProxyServerConfig {
pub url: String,
pub id: String,
}
@@ -183,18 +183,18 @@ impl ConfigLoader {
#[must_use]
pub fn default_for(cwd: impl Into<PathBuf>) -> Self {
let cwd = cwd.into();
let config_home = std::env::var_os("CLAUDE_CONFIG_HOME")
let config_home = std::env::var_os("CLAW_CONFIG_HOME")
.map(PathBuf::from)
.or_else(|| std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".claude")))
.unwrap_or_else(|| PathBuf::from(".claude"));
.or_else(|| std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".claw")))
.unwrap_or_else(|| PathBuf::from(".claw"));
Self { cwd, config_home }
}
#[must_use]
pub fn discover(&self) -> Vec<ConfigEntry> {
let user_legacy_path = self.config_home.parent().map_or_else(
|| PathBuf::from(".claude.json"),
|parent| parent.join(".claude.json"),
|| PathBuf::from(".claw.json"),
|parent| parent.join(".claw.json"),
);
vec![
ConfigEntry {
@@ -207,15 +207,15 @@ impl ConfigLoader {
},
ConfigEntry {
source: ConfigSource::Project,
path: self.cwd.join(".claude.json"),
path: self.cwd.join(".claw.json"),
},
ConfigEntry {
source: ConfigSource::Project,
path: self.cwd.join(".claude").join("settings.json"),
path: self.cwd.join(".claw").join("settings.json"),
},
ConfigEntry {
source: ConfigSource::Local,
path: self.cwd.join(".claude").join("settings.local.json"),
path: self.cwd.join(".claw").join("settings.local.json"),
},
]
}
@@ -450,7 +450,7 @@ impl McpServerConfig {
Self::Http(_) => McpTransport::Http,
Self::Ws(_) => McpTransport::Ws,
Self::Sdk(_) => McpTransport::Sdk,
Self::ClaudeAiProxy(_) => McpTransport::ClaudeAiProxy,
Self::ManagedProxy(_) => McpTransport::ManagedProxy,
}
}
}
@@ -458,7 +458,7 @@ impl McpServerConfig {
fn read_optional_json_object(
path: &Path,
) -> Result<Option<BTreeMap<String, JsonValue>>, ConfigError> {
let is_legacy_config = path.file_name().and_then(|name| name.to_str()) == Some(".claude.json");
let is_legacy_config = path.file_name().and_then(|name| name.to_str()) == Some(".claw.json");
let contents = match fs::read_to_string(path) {
Ok(contents) => contents,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
@@ -684,8 +684,8 @@ fn parse_mcp_server_config(
"sdk" => Ok(McpServerConfig::Sdk(McpSdkServerConfig {
name: expect_string(object, "name", context)?.to_string(),
})),
"claudeai-proxy" => Ok(McpServerConfig::ClaudeAiProxy(
McpClaudeAiProxyServerConfig {
"managed-proxy" => Ok(McpServerConfig::ManagedProxy(
McpManagedProxyServerConfig {
url: expect_string(object, "url", context)?.to_string(),
id: expect_string(object, "id", context)?.to_string(),
},
@@ -872,7 +872,7 @@ fn deep_merge_objects(
mod tests {
use super::{
ConfigLoader, ConfigSource, McpServerConfig, McpTransport, ResolvedPermissionMode,
CLAUDE_CODE_SETTINGS_SCHEMA_NAME,
CLAW_SETTINGS_SCHEMA_NAME,
};
use crate::json::JsonValue;
use crate::sandbox::FilesystemIsolationMode;
@@ -891,7 +891,7 @@ mod tests {
fn rejects_non_object_settings_files() {
let root = temp_dir();
let cwd = root.join("project");
let home = root.join("home").join(".claude");
let home = root.join("home").join(".claw");
fs::create_dir_all(&home).expect("home config dir");
fs::create_dir_all(&cwd).expect("project dir");
fs::write(home.join("settings.json"), "[]").expect("write bad settings");
@@ -907,15 +907,15 @@ mod tests {
}
#[test]
fn loads_and_merges_claude_code_config_files_by_precedence() {
fn loads_and_merges_claw_config_files_by_precedence() {
let root = temp_dir();
let cwd = root.join("project");
let home = root.join("home").join(".claude");
fs::create_dir_all(cwd.join(".claude")).expect("project config dir");
let home = root.join("home").join(".claw");
fs::create_dir_all(cwd.join(".claw")).expect("project config dir");
fs::create_dir_all(&home).expect("home config dir");
fs::write(
home.parent().expect("home parent").join(".claude.json"),
home.parent().expect("home parent").join(".claw.json"),
r#"{"model":"haiku","env":{"A":"1"},"mcpServers":{"home":{"command":"uvx","args":["home"]}}}"#,
)
.expect("write user compat config");
@@ -925,17 +925,17 @@ mod tests {
)
.expect("write user settings");
fs::write(
cwd.join(".claude.json"),
cwd.join(".claw.json"),
r#"{"model":"project-compat","env":{"B":"2"}}"#,
)
.expect("write project compat config");
fs::write(
cwd.join(".claude").join("settings.json"),
cwd.join(".claw").join("settings.json"),
r#"{"env":{"C":"3"},"hooks":{"PostToolUse":["project"],"PostToolUseFailure":["project-failure"]},"permissions":{"ask":["Edit"]},"mcpServers":{"project":{"command":"uvx","args":["project"]}}}"#,
)
.expect("write project settings");
fs::write(
cwd.join(".claude").join("settings.local.json"),
cwd.join(".claw").join("settings.local.json"),
r#"{"model":"opus","permissionMode":"acceptEdits"}"#,
)
.expect("write local settings");
@@ -944,7 +944,7 @@ mod tests {
.load()
.expect("config should load");
assert_eq!(CLAUDE_CODE_SETTINGS_SCHEMA_NAME, "SettingsSchema");
assert_eq!(CLAW_SETTINGS_SCHEMA_NAME, "SettingsSchema");
assert_eq!(loaded.loaded_entries().len(), 5);
assert_eq!(loaded.loaded_entries()[0].source, ConfigSource::User);
assert_eq!(
@@ -996,12 +996,12 @@ mod tests {
fn parses_sandbox_config() {
let root = temp_dir();
let cwd = root.join("project");
let home = root.join("home").join(".claude");
fs::create_dir_all(cwd.join(".claude")).expect("project config dir");
let home = root.join("home").join(".claw");
fs::create_dir_all(cwd.join(".claw")).expect("project config dir");
fs::create_dir_all(&home).expect("home config dir");
fs::write(
cwd.join(".claude").join("settings.local.json"),
cwd.join(".claw").join("settings.local.json"),
r#"{
"sandbox": {
"enabled": true,
@@ -1034,8 +1034,8 @@ mod tests {
fn parses_typed_mcp_and_oauth_config() {
let root = temp_dir();
let cwd = root.join("project");
let home = root.join("home").join(".claude");
fs::create_dir_all(cwd.join(".claude")).expect("project config dir");
let home = root.join("home").join(".claw");
fs::create_dir_all(cwd.join(".claw")).expect("project config dir");
fs::create_dir_all(&home).expect("home config dir");
fs::write(
@@ -1072,7 +1072,7 @@ mod tests {
)
.expect("write user settings");
fs::write(
cwd.join(".claude").join("settings.local.json"),
cwd.join(".claw").join("settings.local.json"),
r#"{
"mcpServers": {
"remote-server": {
@@ -1125,7 +1125,7 @@ mod tests {
fn rejects_invalid_mcp_server_shapes() {
let root = temp_dir();
let cwd = root.join("project");
let home = root.join("home").join(".claude");
let home = root.join("home").join(".claw");
fs::create_dir_all(&home).expect("home config dir");
fs::create_dir_all(&cwd).expect("project dir");
fs::write(

View File

@@ -24,11 +24,11 @@ pub use compact::{
get_compact_continuation_message, should_compact, CompactionConfig, CompactionResult,
};
pub use config::{
ConfigEntry, ConfigError, ConfigLoader, ConfigSource, McpClaudeAiProxyServerConfig,
ConfigEntry, ConfigError, ConfigLoader, ConfigSource, McpManagedProxyServerConfig,
McpConfigCollection, McpOAuthConfig, McpRemoteServerConfig, McpSdkServerConfig,
McpServerConfig, McpStdioServerConfig, McpTransport, McpWebSocketServerConfig, OAuthConfig,
ResolvedPermissionMode, RuntimeConfig, RuntimeFeatureConfig, RuntimeHookConfig,
RuntimePermissionRuleConfig, ScopedMcpServerConfig, CLAUDE_CODE_SETTINGS_SCHEMA_NAME,
RuntimePermissionRuleConfig, ScopedMcpServerConfig, CLAW_SETTINGS_SCHEMA_NAME,
};
pub use conversation::{
ApiClient, ApiRequest, AssistantEvent, ConversationRuntime, RuntimeError, StaticToolExecutor,
@@ -47,7 +47,7 @@ pub use mcp::{
scoped_mcp_config_hash, unwrap_ccr_proxy_url,
};
pub use mcp_client::{
McpClaudeAiProxyTransport, McpClientAuth, McpClientBootstrap, McpClientTransport,
McpManagedProxyTransport, McpClientAuth, McpClientBootstrap, McpClientTransport,
McpRemoteTransport, McpSdkTransport, McpStdioTransport,
};
pub use mcp_stdio::{

View File

@@ -73,7 +73,7 @@ pub fn mcp_server_signature(config: &McpServerConfig) -> Option<String> {
Some(format!("url:{}", unwrap_ccr_proxy_url(&config.url)))
}
McpServerConfig::Ws(config) => Some(format!("url:{}", unwrap_ccr_proxy_url(&config.url))),
McpServerConfig::ClaudeAiProxy(config) => {
McpServerConfig::ManagedProxy(config) => {
Some(format!("url:{}", unwrap_ccr_proxy_url(&config.url)))
}
McpServerConfig::Sdk(_) => None,
@@ -110,7 +110,7 @@ pub fn scoped_mcp_config_hash(config: &ScopedMcpServerConfig) -> String {
ws.headers_helper.as_deref().unwrap_or("")
),
McpServerConfig::Sdk(sdk) => format!("sdk|{}", sdk.name),
McpServerConfig::ClaudeAiProxy(proxy) => {
McpServerConfig::ManagedProxy(proxy) => {
format!("claudeai-proxy|{}|{}", proxy.url, proxy.id)
}
};

View File

@@ -10,7 +10,7 @@ pub enum McpClientTransport {
Http(McpRemoteTransport),
WebSocket(McpRemoteTransport),
Sdk(McpSdkTransport),
ClaudeAiProxy(McpClaudeAiProxyTransport),
ManagedProxy(McpManagedProxyTransport),
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -34,7 +34,7 @@ pub struct McpSdkTransport {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct McpClaudeAiProxyTransport {
pub struct McpManagedProxyTransport {
pub url: String,
pub id: String,
}
@@ -97,8 +97,8 @@ impl McpClientTransport {
McpServerConfig::Sdk(config) => Self::Sdk(McpSdkTransport {
name: config.name.clone(),
}),
McpServerConfig::ClaudeAiProxy(config) => {
Self::ClaudeAiProxy(McpClaudeAiProxyTransport {
McpServerConfig::ManagedProxy(config) => {
Self::ManagedProxy(McpManagedProxyTransport {
url: config.url.clone(),
id: config.id.clone(),
})

View File

@@ -324,12 +324,12 @@ fn generate_random_token(bytes: usize) -> io::Result<String> {
}
fn credentials_home_dir() -> io::Result<PathBuf> {
if let Some(path) = std::env::var_os("CLAUDE_CONFIG_HOME") {
if let Some(path) = std::env::var_os("CLAW_CONFIG_HOME") {
return Ok(PathBuf::from(path));
}
let home = std::env::var_os("HOME")
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "HOME is not set"))?;
Ok(PathBuf::from(home).join(".claude"))
Ok(PathBuf::from(home).join(".claw"))
}
fn read_credentials_root(path: &PathBuf) -> io::Result<Map<String, Value>> {
@@ -541,7 +541,7 @@ mod tests {
fn oauth_credentials_round_trip_and_clear_preserves_other_fields() {
let _guard = env_lock();
let config_home = temp_config_home();
std::env::set_var("CLAUDE_CONFIG_HOME", &config_home);
std::env::set_var("CLAW_CONFIG_HOME", &config_home);
let path = credentials_path().expect("credentials path");
std::fs::create_dir_all(path.parent().expect("parent")).expect("create parent");
std::fs::write(&path, "{\"other\":\"value\"}\n").expect("seed credentials");
@@ -567,7 +567,7 @@ mod tests {
assert!(cleared.contains("\"other\": \"value\""));
assert!(!cleared.contains("\"oauth\""));
std::env::remove_var("CLAUDE_CONFIG_HOME");
std::env::remove_var("CLAW_CONFIG_HOME");
std::fs::remove_dir_all(config_home).expect("cleanup temp dir");
}

View File

@@ -201,10 +201,10 @@ fn discover_instruction_files(cwd: &Path) -> std::io::Result<Vec<ContextFile>> {
let mut files = Vec::new();
for dir in directories {
for candidate in [
dir.join("CLAUDE.md"),
dir.join("CLAUDE.local.md"),
dir.join(".claude").join("CLAUDE.md"),
dir.join(".claude").join("instructions.md"),
dir.join("INSTRUCTIONS.md"),
dir.join("INSTRUCTIONS.local.md"),
dir.join(".claw").join("INSTRUCTIONS.md"),
dir.join(".claw").join("instructions.md"),
] {
push_context_file(&mut files, candidate)?;
}
@@ -301,7 +301,7 @@ fn render_project_context(project_context: &ProjectContext) -> String {
}
fn render_instruction_files(files: &[ContextFile]) -> String {
let mut sections = vec!["# Claude instructions".to_string()];
let mut sections = vec!["# Project instructions".to_string()];
let mut remaining_chars = MAX_TOTAL_INSTRUCTION_CHARS;
for file in files {
if remaining_chars == 0 {
@@ -517,23 +517,23 @@ mod tests {
fn discovers_instruction_files_from_ancestor_chain() {
let root = temp_dir();
let nested = root.join("apps").join("api");
fs::create_dir_all(nested.join(".claude")).expect("nested claude dir");
fs::write(root.join("CLAUDE.md"), "root instructions").expect("write root instructions");
fs::write(root.join("CLAUDE.local.md"), "local instructions")
fs::create_dir_all(nested.join(".claw")).expect("nested claude dir");
fs::write(root.join("INSTRUCTIONS.md"), "root instructions").expect("write root instructions");
fs::write(root.join("INSTRUCTIONS.local.md"), "local instructions")
.expect("write local instructions");
fs::create_dir_all(root.join("apps")).expect("apps dir");
fs::create_dir_all(root.join("apps").join(".claude")).expect("apps claude dir");
fs::write(root.join("apps").join("CLAUDE.md"), "apps instructions")
fs::create_dir_all(root.join("apps").join(".claw")).expect("apps claude dir");
fs::write(root.join("apps").join("INSTRUCTIONS.md"), "apps instructions")
.expect("write apps instructions");
fs::write(
root.join("apps").join(".claude").join("instructions.md"),
root.join("apps").join(".claw").join("instructions.md"),
"apps dot claude instructions",
)
.expect("write apps dot claude instructions");
fs::write(nested.join(".claude").join("CLAUDE.md"), "nested rules")
fs::write(nested.join(".claw").join("INSTRUCTIONS.md"), "nested rules")
.expect("write nested rules");
fs::write(
nested.join(".claude").join("instructions.md"),
nested.join(".claw").join("instructions.md"),
"nested instructions",
)
.expect("write nested instructions");
@@ -552,7 +552,7 @@ mod tests {
"local instructions",
"apps instructions",
"apps dot claude instructions",
"nested rules",
"nested instructions"
]
);
@@ -564,8 +564,8 @@ mod tests {
let root = temp_dir();
let nested = root.join("apps").join("api");
fs::create_dir_all(&nested).expect("nested dir");
fs::write(root.join("CLAUDE.md"), "same rules\n\n").expect("write root");
fs::write(nested.join("CLAUDE.md"), "same rules\n").expect("write nested");
fs::write(root.join("INSTRUCTIONS.md"), "same rules\n\n").expect("write root");
fs::write(nested.join("INSTRUCTIONS.md"), "same rules\n").expect("write nested");
let context = ProjectContext::discover(&nested, "2026-03-31").expect("context should load");
assert_eq!(context.instruction_files.len(), 1);
@@ -593,8 +593,8 @@ mod tests {
#[test]
fn displays_context_paths_compactly() {
assert_eq!(
display_context_path(Path::new("/tmp/project/.claude/CLAUDE.md")),
"CLAUDE.md"
display_context_path(Path::new("/tmp/project/.claw/INSTRUCTIONS.md")),
"INSTRUCTIONS.md"
);
}
@@ -607,7 +607,7 @@ mod tests {
.current_dir(&root)
.status()
.expect("git init should run");
fs::write(root.join("CLAUDE.md"), "rules").expect("write instructions");
fs::write(root.join("INSTRUCTIONS.md"), "rules").expect("write instructions");
fs::write(root.join("tracked.txt"), "hello").expect("write tracked file");
let context =
@@ -615,7 +615,7 @@ mod tests {
let status = context.git_status.expect("git status should be present");
assert!(status.contains("## No commits yet on") || status.contains("## "));
assert!(status.contains("?? CLAUDE.md"));
assert!(status.contains("?? INSTRUCTIONS.md"));
assert!(status.contains("?? tracked.txt"));
assert!(context.git_diff.is_none());
@@ -667,10 +667,10 @@ mod tests {
#[test]
fn load_system_prompt_reads_claude_files_and_config() {
let root = temp_dir();
fs::create_dir_all(root.join(".claude")).expect("claude dir");
fs::write(root.join("CLAUDE.md"), "Project rules").expect("write instructions");
fs::create_dir_all(root.join(".claw")).expect("claude dir");
fs::write(root.join("INSTRUCTIONS.md"), "Project rules").expect("write instructions");
fs::write(
root.join(".claude").join("settings.json"),
root.join(".claw").join("settings.json"),
r#"{"permissionMode":"acceptEdits"}"#,
)
.expect("write settings");
@@ -678,9 +678,9 @@ mod tests {
let _guard = env_lock();
let previous = std::env::current_dir().expect("cwd");
let original_home = std::env::var("HOME").ok();
let original_claude_home = std::env::var("CLAUDE_CONFIG_HOME").ok();
let original_config_home = std::env::var("CLAW_CONFIG_HOME").ok();
std::env::set_var("HOME", &root);
std::env::set_var("CLAUDE_CONFIG_HOME", root.join("missing-home"));
std::env::set_var("CLAW_CONFIG_HOME", root.join("missing-home"));
std::env::set_current_dir(&root).expect("change cwd");
let prompt = super::load_system_prompt(&root, "2026-03-31", "linux", "6.8")
.expect("system prompt should load")
@@ -695,10 +695,10 @@ mod tests {
} else {
std::env::remove_var("HOME");
}
if let Some(value) = original_claude_home {
std::env::set_var("CLAUDE_CONFIG_HOME", value);
if let Some(value) = original_config_home {
std::env::set_var("CLAW_CONFIG_HOME", value);
} else {
std::env::remove_var("CLAUDE_CONFIG_HOME");
std::env::remove_var("CLAW_CONFIG_HOME");
}
assert!(prompt.contains("Project rules"));
@@ -709,10 +709,10 @@ mod tests {
#[test]
fn renders_claude_code_style_sections_with_project_context() {
let root = temp_dir();
fs::create_dir_all(root.join(".claude")).expect("claude dir");
fs::write(root.join("CLAUDE.md"), "Project rules").expect("write CLAUDE.md");
fs::create_dir_all(root.join(".claw")).expect("claude dir");
fs::write(root.join("INSTRUCTIONS.md"), "Project rules").expect("write INSTRUCTIONS.md");
fs::write(
root.join(".claude").join("settings.json"),
root.join(".claw").join("settings.json"),
r#"{"permissionMode":"acceptEdits"}"#,
)
.expect("write settings");
@@ -731,7 +731,7 @@ mod tests {
assert!(prompt.contains("# System"));
assert!(prompt.contains("# Project context"));
assert!(prompt.contains("# Claude instructions"));
assert!(prompt.contains("# Project instructions"));
assert!(prompt.contains("Project rules"));
assert!(prompt.contains("permissionMode"));
assert!(prompt.contains(SYSTEM_PROMPT_DYNAMIC_BOUNDARY));
@@ -751,9 +751,9 @@ mod tests {
fn discovers_dot_claude_instructions_markdown() {
let root = temp_dir();
let nested = root.join("apps").join("api");
fs::create_dir_all(nested.join(".claude")).expect("nested claude dir");
fs::create_dir_all(nested.join(".claw")).expect("nested claude dir");
fs::write(
nested.join(".claude").join("instructions.md"),
nested.join(".claw").join("instructions.md"),
"instruction markdown",
)
.expect("write instructions.md");
@@ -762,7 +762,10 @@ mod tests {
assert!(context
.instruction_files
.iter()
.any(|file| file.path.ends_with(".claude/instructions.md")));
.any(|file| {
let p = file.path.to_string_lossy().to_lowercase();
p.ends_with(".claw/instructions.md")
}));
assert!(
render_instruction_files(&context.instruction_files).contains("instruction markdown")
);
@@ -773,10 +776,10 @@ mod tests {
#[test]
fn renders_instruction_file_metadata() {
let rendered = render_instruction_files(&[ContextFile {
path: PathBuf::from("/tmp/project/CLAUDE.md"),
path: PathBuf::from("/tmp/project/INSTRUCTIONS.md"),
content: "Project rules".to_string(),
}]);
assert!(rendered.contains("# Claude instructions"));
assert!(rendered.contains("# Project instructions"));
assert!(rendered.contains("scope: /tmp/project"));
assert!(rendered.contains("Project rules"));
}