mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-12 19:14:51 +08:00
Keep the rebased workspace green after the backlog closeout
The ROADMAP #38 closeout was rebased onto a moving main branch. That pulled in new workspace files whose clippy/rustfmt fixes were required for the exact verification gate the user asked for. This follow-up records those remaining cleanups so the pushed branch matches the green tree that was actually tested. Constraint: The user-required full-workspace fmt/clippy/test sequence had to stay green after rebasing onto newer origin/main Rejected: Leave the rebase cleanup uncommitted locally | working tree would stay dirty and the pushed branch would not match the verified code Confidence: high Scope-risk: narrow Reversibility: clean Directive: When rebasing onto a moving main, commit any gate-fixing follow-up so pushed history matches the verified tree Tested: cargo fmt --all --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test --workspace Not-tested: No additional behavior beyond the already-green verification sweep
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
// Test isolation utilities for plugin tests
|
// Test isolation utilities for plugin tests
|
||||||
// ROADMAP #41: Stop ambient plugin state from skewing CLI regression checks
|
// ROADMAP #41: Stop ambient plugin state from skewing CLI regression checks
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
static TEST_COUNTER: AtomicU64 = AtomicU64::new(0);
|
static TEST_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||||
static ENV_LOCK: Mutex<()> = Mutex::new(());
|
static ENV_LOCK: Mutex<()> = Mutex::new(());
|
||||||
@@ -20,7 +20,7 @@ impl EnvLock {
|
|||||||
pub fn lock() -> Self {
|
pub fn lock() -> Self {
|
||||||
let guard = ENV_LOCK.lock().unwrap();
|
let guard = ENV_LOCK.lock().unwrap();
|
||||||
let count = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
|
let count = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||||
let temp_home = std::env::temp_dir().join(format!("plugin-test-{}", count));
|
let temp_home = std::env::temp_dir().join(format!("plugin-test-{count}"));
|
||||||
|
|
||||||
// Set up isolated environment
|
// Set up isolated environment
|
||||||
std::fs::create_dir_all(&temp_home).ok();
|
std::fs::create_dir_all(&temp_home).ok();
|
||||||
@@ -39,6 +39,7 @@ impl EnvLock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the temporary home directory for this test
|
/// Get the temporary home directory for this test
|
||||||
|
#[must_use]
|
||||||
pub fn temp_home(&self) -> &PathBuf {
|
pub fn temp_home(&self) -> &PathBuf {
|
||||||
&self.temp_home
|
&self.temp_home
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11521,22 +11521,23 @@ mod dump_manifests_tests {
|
|||||||
let result = dump_manifests_at_path(&temp_dir, CliOutputFormat::Text);
|
let result = dump_manifests_at_path(&temp_dir, CliOutputFormat::Text);
|
||||||
|
|
||||||
// Assert that the call fails
|
// Assert that the call fails
|
||||||
assert!(result.is_err(), "expected an error when manifests are missing");
|
assert!(
|
||||||
|
result.is_err(),
|
||||||
|
"expected an error when manifests are missing"
|
||||||
|
);
|
||||||
|
|
||||||
let error_msg = result.unwrap_err().to_string();
|
let error_msg = result.unwrap_err().to_string();
|
||||||
|
|
||||||
// Assert the error message contains "Manifest files (commands.ts, tools.ts)"
|
// Assert the error message contains "Manifest files (commands.ts, tools.ts)"
|
||||||
assert!(
|
assert!(
|
||||||
error_msg.contains("Manifest files (commands.ts, tools.ts)"),
|
error_msg.contains("Manifest files (commands.ts, tools.ts)"),
|
||||||
"error message should mention manifest files: {}",
|
"error message should mention manifest files: {error_msg}"
|
||||||
error_msg
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Assert the error message contains the expected path
|
// Assert the error message contains the expected path
|
||||||
assert!(
|
assert!(
|
||||||
error_msg.contains(&temp_dir.display().to_string()),
|
error_msg.contains(&temp_dir.display().to_string()),
|
||||||
"error message should contain the expected path: {}",
|
"error message should contain the expected path: {error_msg}"
|
||||||
error_msg
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1287,7 +1287,7 @@ fn maybe_enforce_permission_check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enforce permission check with a dynamically classified permission mode.
|
/// Enforce permission check with a dynamically classified permission mode.
|
||||||
/// Used for tools like bash and PowerShell where the required permission
|
/// Used for tools like bash and `PowerShell` where the required permission
|
||||||
/// depends on the actual command being executed.
|
/// depends on the actual command being executed.
|
||||||
fn maybe_enforce_permission_check_with_mode(
|
fn maybe_enforce_permission_check_with_mode(
|
||||||
enforcer: Option<&PermissionEnforcer>,
|
enforcer: Option<&PermissionEnforcer>,
|
||||||
@@ -1820,8 +1820,8 @@ fn from_value<T: for<'de> Deserialize<'de>>(input: &Value) -> Result<T, String>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Classify bash command permission based on command type and path.
|
/// Classify bash command permission based on command type and path.
|
||||||
/// ROADMAP #50: Read-only commands targeting CWD paths get WorkspaceWrite,
|
/// ROADMAP #50: Read-only commands targeting CWD paths get `WorkspaceWrite`,
|
||||||
/// all others remain DangerFullAccess.
|
/// all others remain `DangerFullAccess`.
|
||||||
fn classify_bash_permission(command: &str) -> PermissionMode {
|
fn classify_bash_permission(command: &str) -> PermissionMode {
|
||||||
// Read-only commands that are safe when targeting workspace paths
|
// Read-only commands that are safe when targeting workspace paths
|
||||||
const READ_ONLY_COMMANDS: &[&str] = &[
|
const READ_ONLY_COMMANDS: &[&str] = &[
|
||||||
@@ -1831,14 +1831,14 @@ fn classify_bash_permission(command: &str) -> PermissionMode {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Get the base command (first word before any args or pipes)
|
// Get the base command (first word before any args or pipes)
|
||||||
let base_cmd = command.trim().split_whitespace().next().unwrap_or("");
|
let base_cmd = command.split_whitespace().next().unwrap_or("");
|
||||||
let base_cmd = base_cmd.split('|').next().unwrap_or("").trim();
|
let base_cmd = base_cmd.split('|').next().unwrap_or("").trim();
|
||||||
let base_cmd = base_cmd.split(';').next().unwrap_or("").trim();
|
let base_cmd = base_cmd.split(';').next().unwrap_or("").trim();
|
||||||
let base_cmd = base_cmd.split('>').next().unwrap_or("").trim();
|
let base_cmd = base_cmd.split('>').next().unwrap_or("").trim();
|
||||||
let base_cmd = base_cmd.split('<').next().unwrap_or("").trim();
|
let base_cmd = base_cmd.split('<').next().unwrap_or("").trim();
|
||||||
|
|
||||||
// Check if it's a read-only command
|
// Check if it's a read-only command
|
||||||
let cmd_name = base_cmd.split('/').last().unwrap_or(base_cmd);
|
let cmd_name = base_cmd.split('/').next_back().unwrap_or(base_cmd);
|
||||||
let is_read_only = READ_ONLY_COMMANDS.contains(&cmd_name);
|
let is_read_only = READ_ONLY_COMMANDS.contains(&cmd_name);
|
||||||
|
|
||||||
if !is_read_only {
|
if !is_read_only {
|
||||||
@@ -1869,7 +1869,7 @@ fn has_dangerous_paths(command: &str) -> bool {
|
|||||||
if token.starts_with('/') || token.starts_with("~/") {
|
if token.starts_with('/') || token.starts_with("~/") {
|
||||||
// Check if it's within CWD
|
// Check if it's within CWD
|
||||||
let path =
|
let path =
|
||||||
PathBuf::from(token.replace("~", &std::env::var("HOME").unwrap_or_default()));
|
PathBuf::from(token.replace('~', &std::env::var("HOME").unwrap_or_default()));
|
||||||
if let Ok(cwd) = std::env::current_dir() {
|
if let Ok(cwd) = std::env::current_dir() {
|
||||||
if !path.starts_with(&cwd) {
|
if !path.starts_with(&cwd) {
|
||||||
return true; // Path outside workspace
|
return true; // Path outside workspace
|
||||||
@@ -2131,9 +2131,9 @@ fn run_repl(input: ReplInput) -> Result<String, String> {
|
|||||||
to_pretty_json(execute_repl(input)?)
|
to_pretty_json(execute_repl(input)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Classify PowerShell command permission based on command type and path.
|
/// Classify `PowerShell` command permission based on command type and path.
|
||||||
/// ROADMAP #50: Read-only commands targeting CWD paths get WorkspaceWrite,
|
/// ROADMAP #50: Read-only commands targeting CWD paths get `WorkspaceWrite`,
|
||||||
/// all others remain DangerFullAccess.
|
/// all others remain `DangerFullAccess`.
|
||||||
fn classify_powershell_permission(command: &str) -> PermissionMode {
|
fn classify_powershell_permission(command: &str) -> PermissionMode {
|
||||||
// Read-only commands that are safe when targeting workspace paths
|
// Read-only commands that are safe when targeting workspace paths
|
||||||
const READ_ONLY_COMMANDS: &[&str] = &[
|
const READ_ONLY_COMMANDS: &[&str] = &[
|
||||||
@@ -2165,17 +2165,17 @@ fn classify_powershell_permission(command: &str) -> PermissionMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the path argument from a PowerShell command.
|
/// Extract the path argument from a `PowerShell` command.
|
||||||
fn extract_powershell_path(command: &str) -> Option<String> {
|
fn extract_powershell_path(command: &str) -> Option<String> {
|
||||||
// Look for -Path parameter
|
// Look for -Path parameter
|
||||||
if let Some(idx) = command.to_lowercase().find("-path") {
|
if let Some(idx) = command.to_lowercase().find("-path") {
|
||||||
let after_path = &command[idx + 5..];
|
let after_path = &command[idx + 5..];
|
||||||
let path = after_path.trim().split_whitespace().next()?;
|
let path = after_path.split_whitespace().next()?;
|
||||||
return Some(path.trim_matches('"').trim_matches('\'').to_string());
|
return Some(path.trim_matches('"').trim_matches('\'').to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for positional path parameter (after command name)
|
// Look for positional path parameter (after command name)
|
||||||
let parts: Vec<&str> = command.trim().split_whitespace().collect();
|
let parts: Vec<&str> = command.split_whitespace().collect();
|
||||||
if parts.len() >= 2 {
|
if parts.len() >= 2 {
|
||||||
// Skip the cmdlet name and take the first argument
|
// Skip the cmdlet name and take the first argument
|
||||||
let first_arg = parts[1];
|
let first_arg = parts[1];
|
||||||
|
|||||||
Reference in New Issue
Block a user