feat(cli): align slash help/status/model handling
Centralize slash command parsing in the commands crate so the REPL can share help metadata and grow toward Claude Code parity without duplicating handlers. This adds shared /help and /model parsing, routes REPL dispatch through the shared parser, and upgrades /status to report model and token totals. To satisfy the required verification gate, this also fixes existing workspace clippy and test blockers in runtime, tools, api, and compat-harness that were unrelated to the new command behavior but prevented fmt/clippy/test from passing cleanly. Constraint: Preserve existing prompt-mode and REPL behavior while adding real slash commands Constraint: cargo fmt, clippy, and workspace tests must pass before shipping command-surface work Rejected: Keep command handling only in main.rs | would deepen duplication with commands crate and resume path Confidence: high Scope-risk: moderate Reversibility: clean Directive: Extend new slash commands through the shared commands crate first so REPL and resume entrypoints stay consistent Tested: cargo fmt; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace Not-tested: live Anthropic network execution beyond existing mocked/integration coverage
This commit is contained in:
@@ -138,9 +138,9 @@ pub fn read_file(
|
||||
let content = fs::read_to_string(&absolute_path)?;
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
let start_index = offset.unwrap_or(0).min(lines.len());
|
||||
let end_index = limit
|
||||
.map(|limit| start_index.saturating_add(limit).min(lines.len()))
|
||||
.unwrap_or(lines.len());
|
||||
let end_index = limit.map_or(lines.len(), |limit| {
|
||||
start_index.saturating_add(limit).min(lines.len())
|
||||
});
|
||||
let selected = lines[start_index..end_index].join("\n");
|
||||
|
||||
Ok(ReadFileOutput {
|
||||
@@ -285,7 +285,7 @@ pub fn grep_search(input: &GrepSearchInput) -> io::Result<GrepSearchOutput> {
|
||||
.output_mode
|
||||
.clone()
|
||||
.unwrap_or_else(|| String::from("files_with_matches"));
|
||||
let context = input.context.or(input.context_short).unwrap_or(0);
|
||||
let context_window = input.context.or(input.context_short).unwrap_or(0);
|
||||
|
||||
let mut filenames = Vec::new();
|
||||
let mut content_lines = Vec::new();
|
||||
@@ -296,12 +296,12 @@ pub fn grep_search(input: &GrepSearchInput) -> io::Result<GrepSearchOutput> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Ok(content) = fs::read_to_string(&file_path) else {
|
||||
let Ok(file_content) = fs::read_to_string(&file_path) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if output_mode == "count" {
|
||||
let count = regex.find_iter(&content).count();
|
||||
let count = regex.find_iter(&file_content).count();
|
||||
if count > 0 {
|
||||
filenames.push(file_path.to_string_lossy().into_owned());
|
||||
total_matches += count;
|
||||
@@ -309,7 +309,7 @@ pub fn grep_search(input: &GrepSearchInput) -> io::Result<GrepSearchOutput> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
let lines: Vec<&str> = file_content.lines().collect();
|
||||
let mut matched_lines = Vec::new();
|
||||
for (index, line) in lines.iter().enumerate() {
|
||||
if regex.is_match(line) {
|
||||
@@ -325,15 +325,15 @@ pub fn grep_search(input: &GrepSearchInput) -> io::Result<GrepSearchOutput> {
|
||||
filenames.push(file_path.to_string_lossy().into_owned());
|
||||
if output_mode == "content" {
|
||||
for index in matched_lines {
|
||||
let start = index.saturating_sub(input.before.unwrap_or(context));
|
||||
let end = (index + input.after.unwrap_or(context) + 1).min(lines.len());
|
||||
for current in start..end {
|
||||
let start = index.saturating_sub(input.before.unwrap_or(context_window));
|
||||
let end = (index + input.after.unwrap_or(context_window) + 1).min(lines.len());
|
||||
for (current, line_content) in lines.iter().enumerate().take(end).skip(start) {
|
||||
let prefix = if input.line_numbers.unwrap_or(true) {
|
||||
format!("{}:{}:", file_path.to_string_lossy(), current + 1)
|
||||
} else {
|
||||
format!("{}:", file_path.to_string_lossy())
|
||||
};
|
||||
content_lines.push(format!("{prefix}{}", lines[current]));
|
||||
content_lines.push(format!("{prefix}{line_content}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,8 +376,7 @@ fn collect_search_files(base_path: &Path) -> io::Result<Vec<PathBuf>> {
|
||||
|
||||
let mut files = Vec::new();
|
||||
for entry in WalkDir::new(base_path) {
|
||||
let entry =
|
||||
entry.map_err(|error| io::Error::new(io::ErrorKind::Other, error.to_string()))?;
|
||||
let entry = entry.map_err(|error| io::Error::other(error.to_string()))?;
|
||||
if entry.file_type().is_file() {
|
||||
files.push(entry.path().to_path_buf());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user