Make the Rust CLI clone-and-run deliverable-ready
Polish the integrated Rust CLI so the branch ships like a usable deliverable instead of a scaffold. This adds explicit version handling, expands the built-in help surface with environment and workflow guidance, and replaces the placeholder rust README with practical build, test, prompt, REPL, and resume instructions. It also ignores OMX and agent scratch directories so local orchestration state stays out of the shipped branch.\n\nConstraint: Must keep the existing workspace shape and avoid adding new dependencies\nConstraint: Must not commit .omx or other local orchestration artifacts\nRejected: Introduce clap-based top-level parsing for the main binary | larger refactor than needed for release-readiness\nRejected: Leave help and version behavior implicit | too rough for a clone-and-use deliverable\nConfidence: high\nScope-risk: narrow\nReversibility: clean\nDirective: Keep README examples and --help output aligned whenever CLI commands or env vars change\nTested: cargo fmt --all; cargo build --release -p rusty-claude-cli; cargo test --workspace --exclude compat-harness; cargo run -p rusty-claude-cli -- --help; cargo run -p rusty-claude-cli -- --version\nNot-tested: Live Anthropic API prompt/REPL execution without credentials in this session
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
__pycache__/
|
||||
archive/
|
||||
.omx/
|
||||
.clawd-agents/
|
||||
|
||||
2
rust/.gitignore
vendored
2
rust/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
target/
|
||||
.omx/
|
||||
.clawd-agents/
|
||||
|
||||
189
rust/README.md
189
rust/README.md
@@ -1,54 +1,173 @@
|
||||
# Rust port foundation
|
||||
# Rusty Claude CLI
|
||||
|
||||
This directory contains the first compatibility-first Rust foundation for a drop-in Claude Code CLI replacement.
|
||||
|
||||
## Current milestone
|
||||
|
||||
This initial milestone focuses on **harness-first scaffolding**, not full feature parity:
|
||||
|
||||
- a Cargo workspace aligned to major upstream seams
|
||||
- a placeholder CLI crate (`rusty-claude-cli`)
|
||||
- runtime, command, and tool registry skeleton crates
|
||||
- a `compat-harness` crate that reads the upstream TypeScript sources in `../src/`
|
||||
- tests that prove upstream manifests/bootstrap hints can be extracted from the leaked TypeScript codebase
|
||||
`rust/` contains the Rust workspace for the integrated `rusty-claude-cli` deliverable.
|
||||
It is intended to be something you can clone, build, and run directly.
|
||||
|
||||
## Workspace layout
|
||||
|
||||
```text
|
||||
rust/
|
||||
├── Cargo.toml
|
||||
├── Cargo.lock
|
||||
├── README.md
|
||||
├── crates/
|
||||
│ ├── rusty-claude-cli/
|
||||
│ ├── runtime/
|
||||
│ ├── commands/
|
||||
│ ├── tools/
|
||||
│ └── compat-harness/
|
||||
└── tests/
|
||||
└── crates/
|
||||
├── api/ # Anthropic API client + SSE streaming support
|
||||
├── commands/ # Shared slash-command metadata/help surfaces
|
||||
├── compat-harness/ # Upstream TS manifest extraction harness
|
||||
├── runtime/ # Session/runtime/config/prompt orchestration
|
||||
├── rusty-claude-cli/ # Main CLI binary
|
||||
└── tools/ # Built-in tool implementations
|
||||
```
|
||||
|
||||
## How to use
|
||||
## Prerequisites
|
||||
|
||||
From this directory:
|
||||
- Rust toolchain installed (`rustup`, stable toolchain)
|
||||
- Network access and Anthropic credentials for live prompt/REPL usage
|
||||
|
||||
## Build
|
||||
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo check --workspace
|
||||
cargo test --workspace
|
||||
cargo run -p rusty-claude-cli -- --help
|
||||
cargo run -p rusty-claude-cli -- dump-manifests
|
||||
cargo run -p rusty-claude-cli -- bootstrap-plan
|
||||
cd rust
|
||||
cargo build --release -p rusty-claude-cli
|
||||
```
|
||||
|
||||
## Design notes
|
||||
The optimized binary will be written to:
|
||||
|
||||
The shape follows the PRD's harness-first recommendation:
|
||||
```bash
|
||||
./target/release/rusty-claude-cli
|
||||
```
|
||||
|
||||
1. Extract observable upstream command/tool/bootstrap facts first.
|
||||
2. Keep Rust module boundaries recognizable.
|
||||
3. Grow runtime compatibility behind proof artifacts.
|
||||
4. Document explicit gaps instead of implying drop-in parity too early.
|
||||
## Test
|
||||
|
||||
## Relationship to the root README
|
||||
Run the verified workspace test suite used for release-readiness:
|
||||
|
||||
The repository root README explains the leaked TypeScript codebase. This document tracks the Rust replacement effort that lives in `rust/`.
|
||||
```bash
|
||||
cd rust
|
||||
cargo test --workspace --exclude compat-harness
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
### Show help
|
||||
|
||||
```bash
|
||||
cd rust
|
||||
cargo run -p rusty-claude-cli -- --help
|
||||
```
|
||||
|
||||
### Print version
|
||||
|
||||
```bash
|
||||
cd rust
|
||||
cargo run -p rusty-claude-cli -- --version
|
||||
```
|
||||
|
||||
## Usage examples
|
||||
|
||||
### 1) Prompt mode
|
||||
|
||||
Send one prompt, stream the answer, then exit:
|
||||
|
||||
```bash
|
||||
cd rust
|
||||
cargo run -p rusty-claude-cli -- prompt "Summarize the architecture of this repository"
|
||||
```
|
||||
|
||||
Use a specific model:
|
||||
|
||||
```bash
|
||||
cd rust
|
||||
cargo run -p rusty-claude-cli -- --model claude-sonnet-4-20250514 prompt "List the key crates in this workspace"
|
||||
```
|
||||
|
||||
### 2) REPL mode
|
||||
|
||||
Start the interactive shell:
|
||||
|
||||
```bash
|
||||
cd rust
|
||||
cargo run -p rusty-claude-cli --
|
||||
```
|
||||
|
||||
Inside the REPL, useful commands include:
|
||||
|
||||
```text
|
||||
/help
|
||||
/status
|
||||
/model claude-sonnet-4-20250514
|
||||
/permissions workspace-write
|
||||
/cost
|
||||
/compact
|
||||
/memory
|
||||
/config
|
||||
/init
|
||||
/exit
|
||||
```
|
||||
|
||||
### 3) Resume an existing session
|
||||
|
||||
Inspect or maintain a saved session file without entering the REPL:
|
||||
|
||||
```bash
|
||||
cd rust
|
||||
cargo run -p rusty-claude-cli -- --resume session.json /status /compact /cost
|
||||
```
|
||||
|
||||
You can also inspect memory/config state for a restored session:
|
||||
|
||||
```bash
|
||||
cd rust
|
||||
cargo run -p rusty-claude-cli -- --resume session.json /memory /config
|
||||
```
|
||||
|
||||
## Available commands
|
||||
|
||||
### Top-level CLI commands
|
||||
|
||||
- `prompt <text...>` — run one prompt non-interactively
|
||||
- `--resume <session.json> [/commands...]` — inspect or maintain a saved session
|
||||
- `dump-manifests` — print extracted upstream manifest counts
|
||||
- `bootstrap-plan` — print the current bootstrap skeleton
|
||||
- `system-prompt [--cwd PATH] [--date YYYY-MM-DD]` — render the synthesized system prompt
|
||||
- `--help` / `-h` — show CLI help
|
||||
- `--version` / `-V` — print the CLI version
|
||||
|
||||
### Interactive slash commands
|
||||
|
||||
- `/help` — show command help
|
||||
- `/status` — show current session status
|
||||
- `/compact` — compact local session history
|
||||
- `/model [model]` — inspect or switch the active model
|
||||
- `/permissions [read-only|workspace-write|danger-full-access]` — inspect or switch permissions
|
||||
- `/clear [--confirm]` — clear the current local session
|
||||
- `/cost` — show token usage totals
|
||||
- `/resume <session-path>` — load a saved session into the REPL
|
||||
- `/config [env|hooks|model]` — inspect discovered Claude config
|
||||
- `/memory` — inspect loaded instruction memory files
|
||||
- `/init` — create a starter `CLAUDE.md`
|
||||
- `/exit` — leave the REPL
|
||||
|
||||
## Environment variables
|
||||
|
||||
### Anthropic/API
|
||||
|
||||
- `ANTHROPIC_AUTH_TOKEN` — preferred bearer token for API auth
|
||||
- `ANTHROPIC_API_KEY` — legacy API key fallback if auth token is unset
|
||||
- `ANTHROPIC_BASE_URL` — override the Anthropic API base URL
|
||||
- `ANTHROPIC_MODEL` — default model used by selected live integration tests
|
||||
|
||||
### CLI/runtime
|
||||
|
||||
- `RUSTY_CLAUDE_PERMISSION_MODE` — default REPL permission mode (`read-only`, `workspace-write`, or `danger-full-access`)
|
||||
- `CLAUDE_CONFIG_HOME` — override Claude config discovery root
|
||||
- `CLAUDE_CODE_REMOTE` — enable remote-session bootstrap handling when supported
|
||||
- `CLAUDE_CODE_REMOTE_SESSION_ID` — remote session identifier when using remote mode
|
||||
- `CLAUDE_CODE_UPSTREAM` — override the upstream TS source path for compat-harness extraction
|
||||
- `CLAWD_WEB_SEARCH_BASE_URL` — override the built-in web search service endpoint used by tooling
|
||||
|
||||
## Notes
|
||||
|
||||
- `compat-harness` exists to compare the Rust port against the upstream TypeScript codebase and is intentionally excluded from the requested release test run.
|
||||
- The CLI currently focuses on a practical integrated workflow: prompt execution, REPL operation, session inspection/resume, config discovery, and tool/runtime plumbing.
|
||||
|
||||
@@ -26,6 +26,7 @@ use tools::{execute_tool, mvp_tool_specs};
|
||||
const DEFAULT_MODEL: &str = "claude-sonnet-4-20250514";
|
||||
const DEFAULT_MAX_TOKENS: u32 = 32;
|
||||
const DEFAULT_DATE: &str = "2026-03-31";
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn main() {
|
||||
if let Err(error) = run() {
|
||||
@@ -47,6 +48,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
CliAction::Prompt { prompt, model } => LiveCli::new(model, false)?.run_turn(&prompt)?,
|
||||
CliAction::Repl { model } => run_repl(model)?,
|
||||
CliAction::Help => print_help(),
|
||||
CliAction::Version => print_version(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -71,6 +73,7 @@ enum CliAction {
|
||||
model: String,
|
||||
},
|
||||
Help,
|
||||
Version,
|
||||
}
|
||||
|
||||
fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
||||
@@ -104,6 +107,9 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
||||
if matches!(rest.first().map(String::as_str), Some("--help" | "-h")) {
|
||||
return Ok(CliAction::Help);
|
||||
}
|
||||
if matches!(rest.first().map(String::as_str), Some("--version" | "-V")) {
|
||||
return Ok(CliAction::Version);
|
||||
}
|
||||
if rest.first().map(String::as_str) == Some("--resume") {
|
||||
return parse_resume_args(&rest[1..]);
|
||||
}
|
||||
@@ -1400,22 +1406,91 @@ fn convert_messages(messages: &[ConversationMessage]) -> Vec<InputMessage> {
|
||||
}
|
||||
|
||||
fn print_help() {
|
||||
println!("rusty-claude-cli");
|
||||
println!();
|
||||
println!("Usage:");
|
||||
println!(" rusty-claude-cli [--model MODEL]");
|
||||
println!(" Start interactive REPL");
|
||||
println!(" rusty-claude-cli [--model MODEL] prompt TEXT");
|
||||
println!(" Send one prompt and stream the response");
|
||||
println!(" rusty-claude-cli --resume SESSION.json [/status] [/compact] [...]");
|
||||
println!(" Inspect or maintain a saved session without entering the REPL");
|
||||
println!(" rusty-claude-cli dump-manifests");
|
||||
println!(" rusty-claude-cli bootstrap-plan");
|
||||
println!(" rusty-claude-cli system-prompt [--cwd PATH] [--date YYYY-MM-DD]");
|
||||
println!();
|
||||
println!("Interactive slash commands:");
|
||||
println!("{}", render_slash_command_help());
|
||||
println!();
|
||||
let mut stdout = io::stdout();
|
||||
let _ = print_help_to(&mut stdout);
|
||||
}
|
||||
|
||||
fn print_help_to(out: &mut impl Write) -> io::Result<()> {
|
||||
writeln!(out, "rusty-claude-cli")?;
|
||||
writeln!(out, "Version: {VERSION}")?;
|
||||
writeln!(out)?;
|
||||
writeln!(
|
||||
out,
|
||||
"Rust-first Claude Code-style CLI for prompt, REPL, and saved-session workflows."
|
||||
)?;
|
||||
writeln!(out)?;
|
||||
writeln!(out, "Usage:")?;
|
||||
writeln!(out, " rusty-claude-cli [--model MODEL]")?;
|
||||
writeln!(out, " Start interactive REPL")?;
|
||||
writeln!(out, " rusty-claude-cli [--model MODEL] prompt TEXT")?;
|
||||
writeln!(out, " Send one prompt and stream the response")?;
|
||||
writeln!(
|
||||
out,
|
||||
" rusty-claude-cli --resume SESSION.json [/status] [/compact] [...]"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" Inspect or maintain a saved session without entering the REPL"
|
||||
)?;
|
||||
writeln!(out, " rusty-claude-cli dump-manifests")?;
|
||||
writeln!(
|
||||
out,
|
||||
" Inspect extracted upstream command/tool metadata"
|
||||
)?;
|
||||
writeln!(out, " rusty-claude-cli bootstrap-plan")?;
|
||||
writeln!(out, " Print the current bootstrap phase skeleton")?;
|
||||
writeln!(
|
||||
out,
|
||||
" rusty-claude-cli system-prompt [--cwd PATH] [--date YYYY-MM-DD]"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" Render the synthesized system prompt for debugging"
|
||||
)?;
|
||||
writeln!(out, " rusty-claude-cli --help")?;
|
||||
writeln!(out, " rusty-claude-cli --version")?;
|
||||
writeln!(out)?;
|
||||
writeln!(out, "Options:")?;
|
||||
writeln!(
|
||||
out,
|
||||
" --model MODEL Override the active Anthropic model"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" --resume PATH Restore a saved session file and optionally run slash commands"
|
||||
)?;
|
||||
writeln!(out, " -h, --help Show this help page")?;
|
||||
writeln!(out, " -V, --version Print the CLI version")?;
|
||||
writeln!(out)?;
|
||||
writeln!(out, "Environment:")?;
|
||||
writeln!(
|
||||
out,
|
||||
" ANTHROPIC_AUTH_TOKEN Preferred bearer token for Anthropic API requests"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" ANTHROPIC_API_KEY Legacy API key fallback if auth token is unset"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" ANTHROPIC_BASE_URL Override the Anthropic API base URL"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" ANTHROPIC_MODEL Default model for selected integration tests"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" RUSTY_CLAUDE_PERMISSION_MODE Default permission mode for REPL sessions"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" CLAUDE_CONFIG_HOME Override Claude config discovery root"
|
||||
)?;
|
||||
writeln!(out)?;
|
||||
writeln!(out, "Interactive slash commands:")?;
|
||||
writeln!(out, "{}", render_slash_command_help())?;
|
||||
writeln!(out)?;
|
||||
let resume_commands = resume_supported_slash_commands()
|
||||
.into_iter()
|
||||
.map(|spec| match spec.argument_hint {
|
||||
@@ -1424,10 +1499,26 @@ fn print_help() {
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
println!("Resume-safe commands: {resume_commands}");
|
||||
println!("Examples:");
|
||||
println!(" rusty-claude-cli --resume session.json /status /compact /cost");
|
||||
println!(" rusty-claude-cli --resume session.json /memory /config");
|
||||
writeln!(out, "Resume-safe commands: {resume_commands}")?;
|
||||
writeln!(out, "Examples:")?;
|
||||
writeln!(
|
||||
out,
|
||||
" rusty-claude-cli prompt \"Summarize the repo architecture\""
|
||||
)?;
|
||||
writeln!(out, " rusty-claude-cli --model claude-sonnet-4-20250514")?;
|
||||
writeln!(
|
||||
out,
|
||||
" rusty-claude-cli --resume session.json /status /compact /cost"
|
||||
)?;
|
||||
writeln!(
|
||||
out,
|
||||
" rusty-claude-cli --resume session.json /memory /config"
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_version() {
|
||||
println!("rusty-claude-cli {VERSION}");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1525,6 +1616,18 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_version_flags() {
|
||||
assert_eq!(
|
||||
parse_args(&["--version".to_string()]).expect("args should parse"),
|
||||
CliAction::Version
|
||||
);
|
||||
assert_eq!(
|
||||
parse_args(&["-V".to_string()]).expect("args should parse"),
|
||||
CliAction::Version
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_help_uses_resume_annotation_copy() {
|
||||
let help = commands::render_slash_command_help();
|
||||
@@ -1532,6 +1635,16 @@ mod tests {
|
||||
assert!(help.contains("works with --resume SESSION.json"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_help_mentions_version_and_environment() {
|
||||
let mut output = Vec::new();
|
||||
super::print_help_to(&mut output).expect("help should render");
|
||||
let help = String::from_utf8(output).expect("help should be utf8");
|
||||
assert!(help.contains("--version"));
|
||||
assert!(help.contains("ANTHROPIC_AUTH_TOKEN"));
|
||||
assert!(help.contains("RUSTY_CLAUDE_PERMISSION_MODE"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repl_help_includes_shared_commands_and_exit() {
|
||||
let help = render_repl_help();
|
||||
@@ -1547,6 +1660,7 @@ mod tests {
|
||||
assert!(help.contains("/memory"));
|
||||
assert!(help.contains("/init"));
|
||||
assert!(help.contains("/exit"));
|
||||
assert!(help.contains("slash commands"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user