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

@@ -4,7 +4,7 @@ use clap::{Parser, Subcommand, ValueEnum};
#[derive(Debug, Clone, Parser, PartialEq, Eq)]
#[command(
name = "rusty-claude-cli",
name = "claw-cli",
version,
about = "Rust Claude CLI prototype"
)]
@@ -62,7 +62,7 @@ mod tests {
#[test]
fn parses_requested_flags() {
let cli = Cli::parse_from([
"rusty-claude-cli",
"claw-cli",
"--model",
"claude-3-5-haiku",
"--permission-mode",
@@ -93,16 +93,16 @@ mod tests {
#[test]
fn parses_login_and_logout_commands() {
let login = Cli::parse_from(["rusty-claude-cli", "login"]);
let login = Cli::parse_from(["claw-cli", "login"]);
assert_eq!(login.command, Some(Command::Login));
let logout = Cli::parse_from(["rusty-claude-cli", "logout"]);
let logout = Cli::parse_from(["claw-cli", "logout"]);
assert_eq!(logout.command, Some(Command::Logout));
}
#[test]
fn defaults_to_danger_full_access_permission_mode() {
let cli = Cli::parse_from(["rusty-claude-cli"]);
let cli = Cli::parse_from(["claw-cli"]);
assert_eq!(cli.permission_mode, PermissionMode::DangerFullAccess);
}
}

View File

@@ -1,15 +1,15 @@
use std::fs;
use std::path::{Path, PathBuf};
const STARTER_CLAUDE_JSON: &str = concat!(
const STARTER_CLAW_JSON: &str = concat!(
"{\n",
" \"permissions\": {\n",
" \"defaultMode\": \"dontAsk\"\n",
" }\n",
"}\n",
);
const GITIGNORE_COMMENT: &str = "# Claude Code local artifacts";
const GITIGNORE_ENTRIES: [&str; 2] = [".claude/settings.local.json", ".claude/sessions/"];
const GITIGNORE_COMMENT: &str = "# Claw Code local artifacts";
const GITIGNORE_ENTRIES: [&str; 2] = [".claw/settings.local.json", ".claw/sessions/"];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum InitStatus {
@@ -80,16 +80,16 @@ struct RepoDetection {
pub(crate) fn initialize_repo(cwd: &Path) -> Result<InitReport, Box<dyn std::error::Error>> {
let mut artifacts = Vec::new();
let claude_dir = cwd.join(".claude");
let claw_dir = cwd.join(".claw");
artifacts.push(InitArtifact {
name: ".claude/",
status: ensure_dir(&claude_dir)?,
name: ".claw/",
status: ensure_dir(&claw_dir)?,
});
let claude_json = cwd.join(".claude.json");
let claw_json = cwd.join(".claw.json");
artifacts.push(InitArtifact {
name: ".claude.json",
status: write_file_if_missing(&claude_json, STARTER_CLAUDE_JSON)?,
name: ".claw.json",
status: write_file_if_missing(&claw_json, STARTER_CLAW_JSON)?,
});
let gitignore = cwd.join(".gitignore");
@@ -98,11 +98,11 @@ pub(crate) fn initialize_repo(cwd: &Path) -> Result<InitReport, Box<dyn std::err
status: ensure_gitignore_entries(&gitignore)?,
});
let claude_md = cwd.join("CLAUDE.md");
let content = render_init_claude_md(cwd);
let instructions_md = cwd.join("INSTRUCTIONS.md");
let content = render_init_instructions_md(cwd);
artifacts.push(InitArtifact {
name: "CLAUDE.md",
status: write_file_if_missing(&claude_md, &content)?,
name: "INSTRUCTIONS.md",
status: write_file_if_missing(&instructions_md, &content)?,
});
Ok(InitReport {
@@ -159,12 +159,13 @@ fn ensure_gitignore_entries(path: &Path) -> Result<InitStatus, std::io::Error> {
Ok(InitStatus::Updated)
}
pub(crate) fn render_init_claude_md(cwd: &Path) -> String {
pub(crate) fn render_init_instructions_md(cwd: &Path) -> String {
let detection = detect_repo(cwd);
let mut lines = vec![
"# CLAUDE.md".to_string(),
"# INSTRUCTIONS.md".to_string(),
String::new(),
"This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.".to_string(),
"This file provides guidance to Claw Code when working with code in this repository."
.to_string(),
String::new(),
];
@@ -209,8 +210,8 @@ pub(crate) fn render_init_claude_md(cwd: &Path) -> String {
lines.push("## Working agreement".to_string());
lines.push("- Prefer small, reviewable changes and keep generated bootstrap files aligned with actual repo workflows.".to_string());
lines.push("- Keep shared defaults in `.claude.json`; reserve `.claude/settings.local.json` for machine-local overrides.".to_string());
lines.push("- Do not overwrite existing `CLAUDE.md` content automatically; update it intentionally when repo workflows change.".to_string());
lines.push("- Keep shared defaults in `.claw.json`; reserve `.claw/settings.local.json` for machine-local overrides.".to_string());
lines.push("- Do not overwrite existing `INSTRUCTIONS.md` content automatically; update it intentionally when repo workflows change.".to_string());
lines.push(String::new());
lines.join("\n")
@@ -333,7 +334,7 @@ fn framework_notes(detection: &RepoDetection) -> Vec<String> {
#[cfg(test)]
mod tests {
use super::{initialize_repo, render_init_claude_md};
use super::{initialize_repo, render_init_instructions_md};
use std::fs;
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};
@@ -343,7 +344,7 @@ mod tests {
.duration_since(UNIX_EPOCH)
.expect("time should be after epoch")
.as_nanos();
std::env::temp_dir().join(format!("rusty-claude-init-{nanos}"))
std::env::temp_dir().join(format!("rusty-claw-init-{nanos}"))
}
#[test]
@@ -354,15 +355,15 @@ mod tests {
let report = initialize_repo(&root).expect("init should succeed");
let rendered = report.render();
assert!(rendered.contains(".claude/ created"));
assert!(rendered.contains(".claude.json created"));
assert!(rendered.contains(".claw/ created"));
assert!(rendered.contains(".claw.json created"));
assert!(rendered.contains(".gitignore created"));
assert!(rendered.contains("CLAUDE.md created"));
assert!(root.join(".claude").is_dir());
assert!(root.join(".claude.json").is_file());
assert!(root.join("CLAUDE.md").is_file());
assert!(rendered.contains("INSTRUCTIONS.md created"));
assert!(root.join(".claw").is_dir());
assert!(root.join(".claw.json").is_file());
assert!(root.join("INSTRUCTIONS.md").is_file());
assert_eq!(
fs::read_to_string(root.join(".claude.json")).expect("read claude json"),
fs::read_to_string(root.join(".claw.json")).expect("read claw json"),
concat!(
"{\n",
" \"permissions\": {\n",
@@ -372,11 +373,12 @@ mod tests {
)
);
let gitignore = fs::read_to_string(root.join(".gitignore")).expect("read gitignore");
assert!(gitignore.contains(".claude/settings.local.json"));
assert!(gitignore.contains(".claude/sessions/"));
let claude_md = fs::read_to_string(root.join("CLAUDE.md")).expect("read claude md");
assert!(claude_md.contains("Languages: Rust."));
assert!(claude_md.contains("cargo clippy --workspace --all-targets -- -D warnings"));
assert!(gitignore.contains(".claw/settings.local.json"));
assert!(gitignore.contains(".claw/sessions/"));
let instructions_md =
fs::read_to_string(root.join("INSTRUCTIONS.md")).expect("read instructions md");
assert!(instructions_md.contains("Languages: Rust."));
assert!(instructions_md.contains("cargo clippy --workspace --all-targets -- -D warnings"));
fs::remove_dir_all(root).expect("cleanup temp dir");
}
@@ -385,27 +387,28 @@ mod tests {
fn initialize_repo_is_idempotent_and_preserves_existing_files() {
let root = temp_dir();
fs::create_dir_all(&root).expect("create root");
fs::write(root.join("CLAUDE.md"), "custom guidance\n").expect("write existing claude md");
fs::write(root.join(".gitignore"), ".claude/settings.local.json\n")
.expect("write gitignore");
fs::write(root.join("INSTRUCTIONS.md"), "custom guidance\n")
.expect("write existing instructions md");
fs::write(root.join(".gitignore"), ".claw/settings.local.json\n").expect("write gitignore");
let first = initialize_repo(&root).expect("first init should succeed");
assert!(first
.render()
.contains("CLAUDE.md skipped (already exists)"));
.contains("INSTRUCTIONS.md skipped (already exists)"));
let second = initialize_repo(&root).expect("second init should succeed");
let second_rendered = second.render();
assert!(second_rendered.contains(".claude/ skipped (already exists)"));
assert!(second_rendered.contains(".claude.json skipped (already exists)"));
assert!(second_rendered.contains(".claw/ skipped (already exists)"));
assert!(second_rendered.contains(".claw.json skipped (already exists)"));
assert!(second_rendered.contains(".gitignore skipped (already exists)"));
assert!(second_rendered.contains("CLAUDE.md skipped (already exists)"));
assert!(second_rendered.contains("INSTRUCTIONS.md skipped (already exists)"));
assert_eq!(
fs::read_to_string(root.join("CLAUDE.md")).expect("read existing claude md"),
fs::read_to_string(root.join("INSTRUCTIONS.md"))
.expect("read existing instructions md"),
"custom guidance\n"
);
let gitignore = fs::read_to_string(root.join(".gitignore")).expect("read gitignore");
assert_eq!(gitignore.matches(".claude/settings.local.json").count(), 1);
assert_eq!(gitignore.matches(".claude/sessions/").count(), 1);
assert_eq!(gitignore.matches(".claw/settings.local.json").count(), 1);
assert_eq!(gitignore.matches(".claw/sessions/").count(), 1);
fs::remove_dir_all(root).expect("cleanup temp dir");
}
@@ -422,7 +425,7 @@ mod tests {
)
.expect("write package json");
let rendered = render_init_claude_md(Path::new(&root));
let rendered = render_init_instructions_md(Path::new(&root));
assert!(rendered.contains("Languages: Python, TypeScript."));
assert!(rendered.contains("Frameworks/tooling markers: Next.js, React."));
assert!(rendered.contains("pyproject.toml"));

View File

@@ -14,7 +14,7 @@ use std::thread::{self, JoinHandle};
use std::time::{SystemTime, UNIX_EPOCH};
use api::{
resolve_startup_auth_source, AnthropicClient, AuthSource, ContentBlockDelta, InputContentBlock,
resolve_startup_auth_source, ApiClient as ApiHttpClient, AuthSource, ContentBlockDelta, InputContentBlock,
InputMessage, MessageRequest, MessageResponse, OutputContentBlock,
StreamEvent as ApiStreamEvent, ToolChoice, ToolDefinition, ToolResultContentBlock,
};
@@ -444,7 +444,7 @@ fn dump_manifests() {
}
fn print_bootstrap_plan() {
for phase in runtime::BootstrapPlan::claude_code_default().phases() {
for phase in runtime::BootstrapPlan::default_bootstrap().phases() {
println!("- {phase:?}");
}
}
@@ -501,7 +501,7 @@ fn run_login() -> Result<(), Box<dyn std::error::Error>> {
return Err(io::Error::new(io::ErrorKind::InvalidData, "oauth state mismatch").into());
}
let client = AnthropicClient::from_auth(AuthSource::None).with_base_url(api::read_base_url());
let client = ApiHttpClient::from_auth(AuthSource::None).with_base_url(api::read_base_url());
let exchange_request =
OAuthTokenExchangeRequest::from_config(oauth, code, state, pkce.verifier, redirect_uri);
let runtime = tokio::runtime::Runtime::new()?;
@@ -982,7 +982,7 @@ struct LiveCli {
allowed_tools: Option<AllowedToolSet>,
permission_mode: PermissionMode,
system_prompt: Vec<String>,
runtime: ConversationRuntime<AnthropicRuntimeClient, CliToolExecutor>,
runtime: ConversationRuntime<ClawRuntimeClient, CliToolExecutor>,
session: SessionHandle,
}
@@ -1101,7 +1101,7 @@ impl LiveCli {
emit_output: bool,
) -> Result<
(
ConversationRuntime<AnthropicRuntimeClient, CliToolExecutor>,
ConversationRuntime<ClawRuntimeClient, CliToolExecutor>,
HookAbortMonitor,
),
Box<dyn std::error::Error>,
@@ -1531,7 +1531,7 @@ impl LiveCli {
fn sessions_dir() -> Result<PathBuf, Box<dyn std::error::Error>> {
let cwd = env::current_dir()?;
let path = cwd.join(".claude").join("sessions");
let path = cwd.join(".claw").join("sessions");
fs::create_dir_all(&path)?;
Ok(path)
}
@@ -1999,12 +1999,12 @@ fn build_runtime(
emit_output: bool,
allowed_tools: Option<AllowedToolSet>,
permission_mode: PermissionMode,
) -> Result<ConversationRuntime<AnthropicRuntimeClient, CliToolExecutor>, Box<dyn std::error::Error>>
) -> Result<ConversationRuntime<ClawRuntimeClient, CliToolExecutor>, Box<dyn std::error::Error>>
{
let feature_config = build_runtime_feature_config()?;
let mut runtime = ConversationRuntime::new_with_features(
session,
AnthropicRuntimeClient::new(model, enable_tools, emit_output, allowed_tools.clone())?,
ClawRuntimeClient::new(model, enable_tools, emit_output, allowed_tools.clone())?,
CliToolExecutor::new(allowed_tools, emit_output),
permission_policy(permission_mode, &feature_config),
system_prompt,
@@ -2098,16 +2098,16 @@ impl runtime::PermissionPrompter for CliPermissionPrompter {
}
}
struct AnthropicRuntimeClient {
struct ClawRuntimeClient {
runtime: tokio::runtime::Runtime,
client: AnthropicClient,
client: ApiHttpClient,
model: String,
enable_tools: bool,
emit_output: bool,
allowed_tools: Option<AllowedToolSet>,
}
impl AnthropicRuntimeClient {
impl ClawRuntimeClient {
fn new(
model: String,
enable_tools: bool,
@@ -2116,7 +2116,7 @@ impl AnthropicRuntimeClient {
) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self {
runtime: tokio::runtime::Runtime::new()?,
client: AnthropicClient::from_auth(resolve_cli_auth_source()?)
client: ApiHttpClient::from_auth(resolve_cli_auth_source()?)
.with_base_url(api::read_base_url()),
model,
enable_tools,
@@ -2136,7 +2136,7 @@ fn resolve_cli_auth_source() -> Result<AuthSource, Box<dyn std::error::Error>> {
})?)
}
impl ApiClient for AnthropicRuntimeClient {
impl ApiClient for ClawRuntimeClient {
#[allow(clippy::too_many_lines)]
fn stream(&mut self, request: ApiRequest) -> Result<Vec<AssistantEvent>, RuntimeError> {
let message_request = MessageRequest {
@@ -3448,8 +3448,8 @@ mod tests {
#[test]
fn init_template_mentions_detected_rust_workspace() {
let rendered = crate::init::render_init_claude_md(std::path::Path::new("."));
assert!(rendered.contains("# CLAUDE.md"));
let rendered = crate::init::render_init_instructions_md(std::path::Path::new("."));
assert!(rendered.contains("# INSTRUCTIONS.md"));
assert!(rendered.contains("cargo clippy --workspace --all-targets -- -D warnings"));
}