mirror of
https://github.com/ultraworkers/claw-code-parity.git
synced 2026-06-24 02:41:11 +00:00
feat(cli): add claw hook list command
Expose registered runtime and plugin hook entries through a direct claw hook list command and cover the new surface with CLI, help, and report tests. Constraint: Reuse the existing runtime/plugin configuration plumbing without introducing a separate hook registry Rejected: Hide the feature behind the REPL-only /hooks slash command | the request requires a direct top-level CLI command Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep the hook list output aligned with config-loaded hooks plus plugin enabled state when hook sources change Tested: cd rust && cargo build --workspace Tested: cd rust && cargo test --workspace Tested: cd rust && cargo run -q -p rusty-claude-cli --bin claw -- hook list Not-tested: Remote push/CI after publishing the branch
This commit is contained in:
parent
7db400c54e
commit
184188e986
@ -123,6 +123,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
permission_mode,
|
permission_mode,
|
||||||
} => print_status_snapshot(&model, permission_mode)?,
|
} => print_status_snapshot(&model, permission_mode)?,
|
||||||
CliAction::ConfigShow => print_config_json()?,
|
CliAction::ConfigShow => print_config_json()?,
|
||||||
|
CliAction::HookList => print_hook_list()?,
|
||||||
CliAction::Sandbox => print_sandbox_status_snapshot()?,
|
CliAction::Sandbox => print_sandbox_status_snapshot()?,
|
||||||
CliAction::Prompt {
|
CliAction::Prompt {
|
||||||
prompt,
|
prompt,
|
||||||
@ -173,6 +174,7 @@ enum CliAction {
|
|||||||
permission_mode: PermissionMode,
|
permission_mode: PermissionMode,
|
||||||
},
|
},
|
||||||
ConfigShow,
|
ConfigShow,
|
||||||
|
HookList,
|
||||||
Sandbox,
|
Sandbox,
|
||||||
Prompt {
|
Prompt {
|
||||||
prompt: String,
|
prompt: String,
|
||||||
@ -345,8 +347,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
|||||||
if rest.first().map(String::as_str) == Some("--resume") {
|
if rest.first().map(String::as_str) == Some("--resume") {
|
||||||
return parse_resume_args(&rest[1..]);
|
return parse_resume_args(&rest[1..]);
|
||||||
}
|
}
|
||||||
if let Some(action) = parse_single_word_command_alias(&rest, &model, permission_mode_override)
|
if let Some(action) = parse_single_word_command_alias(&rest, &model, permission_mode_override) {
|
||||||
{
|
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,6 +367,7 @@ fn parse_args(args: &[String]) -> Result<CliAction, String> {
|
|||||||
args: join_optional_args(&rest[1..]),
|
args: join_optional_args(&rest[1..]),
|
||||||
}),
|
}),
|
||||||
"system-prompt" => parse_system_prompt_args(&rest[1..]),
|
"system-prompt" => parse_system_prompt_args(&rest[1..]),
|
||||||
|
"hook" => parse_hook_args(&rest[1..]),
|
||||||
"login" => Ok(CliAction::Login),
|
"login" => Ok(CliAction::Login),
|
||||||
"logout" => Ok(CliAction::Logout),
|
"logout" => Ok(CliAction::Logout),
|
||||||
"init" => Ok(CliAction::Init),
|
"init" => Ok(CliAction::Init),
|
||||||
@ -399,7 +401,7 @@ fn parse_single_word_command_alias(
|
|||||||
model: &str,
|
model: &str,
|
||||||
permission_mode_override: Option<PermissionMode>,
|
permission_mode_override: Option<PermissionMode>,
|
||||||
) -> Option<Result<CliAction, String>> {
|
) -> Option<Result<CliAction, String>> {
|
||||||
if rest.len() != 1 || matches!(rest[0].as_str(), "branch" | "config") {
|
if rest.len() != 1 || matches!(rest[0].as_str(), "branch" | "config" | "hook") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -715,6 +717,16 @@ fn parse_config_args(args: &[String]) -> Result<CliAction, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_hook_args(args: &[String]) -> Result<CliAction, String> {
|
||||||
|
match args {
|
||||||
|
[] => Err("Usage: claw hook list".to_string()),
|
||||||
|
[action] if action == "list" => Ok(CliAction::HookList),
|
||||||
|
[action, ..] => Err(format!(
|
||||||
|
"unknown hook action: {action}. Usage: claw hook list"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_branch_args(args: &[String]) -> Result<CliAction, String> {
|
fn parse_branch_args(args: &[String]) -> Result<CliAction, String> {
|
||||||
match args {
|
match args {
|
||||||
[] => Err("Usage: claw branch delete".to_string()),
|
[] => Err("Usage: claw branch delete".to_string()),
|
||||||
@ -1956,37 +1968,38 @@ impl RuntimeMcpState {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|server_name| !failed_server_names.contains(server_name))
|
.filter(|server_name| !failed_server_names.contains(server_name))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let failed_servers = discovery
|
let failed_servers =
|
||||||
.failed_servers
|
discovery
|
||||||
.iter()
|
.failed_servers
|
||||||
.map(|failure| runtime::McpFailedServer {
|
.iter()
|
||||||
server_name: failure.server_name.clone(),
|
.map(|failure| runtime::McpFailedServer {
|
||||||
phase: runtime::McpLifecyclePhase::ToolDiscovery,
|
server_name: failure.server_name.clone(),
|
||||||
error: runtime::McpErrorSurface::new(
|
phase: runtime::McpLifecyclePhase::ToolDiscovery,
|
||||||
runtime::McpLifecyclePhase::ToolDiscovery,
|
|
||||||
Some(failure.server_name.clone()),
|
|
||||||
failure.error.clone(),
|
|
||||||
std::collections::BTreeMap::new(),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.chain(discovery.unsupported_servers.iter().map(|server| {
|
|
||||||
runtime::McpFailedServer {
|
|
||||||
server_name: server.server_name.clone(),
|
|
||||||
phase: runtime::McpLifecyclePhase::ServerRegistration,
|
|
||||||
error: runtime::McpErrorSurface::new(
|
error: runtime::McpErrorSurface::new(
|
||||||
runtime::McpLifecyclePhase::ServerRegistration,
|
runtime::McpLifecyclePhase::ToolDiscovery,
|
||||||
Some(server.server_name.clone()),
|
Some(failure.server_name.clone()),
|
||||||
server.reason.clone(),
|
failure.error.clone(),
|
||||||
std::collections::BTreeMap::from([(
|
std::collections::BTreeMap::new(),
|
||||||
"transport".to_string(),
|
true,
|
||||||
format!("{:?}", server.transport).to_ascii_lowercase(),
|
|
||||||
)]),
|
|
||||||
false,
|
|
||||||
),
|
),
|
||||||
}
|
})
|
||||||
}))
|
.chain(discovery.unsupported_servers.iter().map(|server| {
|
||||||
.collect::<Vec<_>>();
|
runtime::McpFailedServer {
|
||||||
|
server_name: server.server_name.clone(),
|
||||||
|
phase: runtime::McpLifecyclePhase::ServerRegistration,
|
||||||
|
error: runtime::McpErrorSurface::new(
|
||||||
|
runtime::McpLifecyclePhase::ServerRegistration,
|
||||||
|
Some(server.server_name.clone()),
|
||||||
|
server.reason.clone(),
|
||||||
|
std::collections::BTreeMap::from([(
|
||||||
|
"transport".to_string(),
|
||||||
|
format!("{:?}", server.transport).to_ascii_lowercase(),
|
||||||
|
)]),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let degraded_report = (!failed_servers.is_empty()).then(|| {
|
let degraded_report = (!failed_servers.is_empty()).then(|| {
|
||||||
runtime::McpDegradedReport::new(
|
runtime::McpDegradedReport::new(
|
||||||
working_servers,
|
working_servers,
|
||||||
@ -3448,10 +3461,10 @@ fn format_status_report(
|
|||||||
Worktrees {}
|
Worktrees {}
|
||||||
Entries {}
|
Entries {}
|
||||||
Recent commits {}",
|
Recent commits {}",
|
||||||
context
|
context.git_freshness.as_ref().map_or_else(
|
||||||
.git_freshness
|
|| "origin/main unavailable".to_string(),
|
||||||
.as_ref()
|
GitBranchFreshness::headline
|
||||||
.map_or_else(|| "origin/main unavailable".to_string(), GitBranchFreshness::headline),
|
),
|
||||||
if context.git_worktrees.is_empty() {
|
if context.git_worktrees.is_empty() {
|
||||||
"unavailable".to_string()
|
"unavailable".to_string()
|
||||||
} else {
|
} else {
|
||||||
@ -3647,6 +3660,127 @@ fn render_merged_runtime_config_json() -> Result<String, Box<dyn std::error::Err
|
|||||||
Ok(serde_json::to_string_pretty(&parsed)?)
|
Ok(serde_json::to_string_pretty(&parsed)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_hook_list() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let cwd = env::current_dir()?;
|
||||||
|
let loader = ConfigLoader::default_for(&cwd);
|
||||||
|
let runtime_config = loader.load()?;
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
render_hook_list_report_for(&cwd, &loader, &runtime_config)?
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
struct HookListEntry {
|
||||||
|
source: String,
|
||||||
|
event: &'static str,
|
||||||
|
command: String,
|
||||||
|
enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_hook_list_report_for(
|
||||||
|
cwd: &Path,
|
||||||
|
loader: &ConfigLoader,
|
||||||
|
runtime_config: &runtime::RuntimeConfig,
|
||||||
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let entries = collect_hook_list_entries(cwd, loader, runtime_config)?;
|
||||||
|
let enabled_count = entries.iter().filter(|entry| entry.enabled).count();
|
||||||
|
let mut lines = vec![format!(
|
||||||
|
"Hooks\n Registered {}\n Enabled {}",
|
||||||
|
entries.len(),
|
||||||
|
enabled_count
|
||||||
|
)];
|
||||||
|
|
||||||
|
if entries.is_empty() {
|
||||||
|
lines.push(" No hooks registered.".to_string());
|
||||||
|
return Ok(lines.join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push("Entries".to_string());
|
||||||
|
lines.push(format!(
|
||||||
|
" {:<7} {:<32} {:<19} {}",
|
||||||
|
"Enabled", "Source", "Event", "Command"
|
||||||
|
));
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
lines.push(format!(
|
||||||
|
" {:<7} {:<32} {:<19} {}",
|
||||||
|
if entry.enabled { "yes" } else { "no" },
|
||||||
|
entry.source,
|
||||||
|
entry.event,
|
||||||
|
entry.command
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(lines.join("\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_hook_list_entries(
|
||||||
|
cwd: &Path,
|
||||||
|
loader: &ConfigLoader,
|
||||||
|
runtime_config: &runtime::RuntimeConfig,
|
||||||
|
) -> Result<Vec<HookListEntry>, Box<dyn std::error::Error>> {
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
extend_hook_list_entries(
|
||||||
|
&mut entries,
|
||||||
|
"config".to_string(),
|
||||||
|
true,
|
||||||
|
runtime_config.hooks().pre_tool_use(),
|
||||||
|
runtime_config.hooks().post_tool_use(),
|
||||||
|
runtime_config.hooks().post_tool_use_failure(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let plugin_manager = build_plugin_manager(cwd, loader, runtime_config);
|
||||||
|
let plugin_registry = plugin_manager.plugin_registry()?;
|
||||||
|
for plugin in plugin_registry.plugins() {
|
||||||
|
extend_hook_list_entries(
|
||||||
|
&mut entries,
|
||||||
|
format!("plugin:{}", plugin.metadata().id),
|
||||||
|
plugin.is_enabled(),
|
||||||
|
&plugin.hooks().pre_tool_use,
|
||||||
|
&plugin.hooks().post_tool_use,
|
||||||
|
&plugin.hooks().post_tool_use_failure,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extend_hook_list_entries(
|
||||||
|
entries: &mut Vec<HookListEntry>,
|
||||||
|
source: String,
|
||||||
|
enabled: bool,
|
||||||
|
pre_tool_use: &[String],
|
||||||
|
post_tool_use: &[String],
|
||||||
|
post_tool_use_failure: &[String],
|
||||||
|
) {
|
||||||
|
append_hook_list_entries(entries, &source, enabled, "PreToolUse", pre_tool_use);
|
||||||
|
append_hook_list_entries(entries, &source, enabled, "PostToolUse", post_tool_use);
|
||||||
|
append_hook_list_entries(
|
||||||
|
entries,
|
||||||
|
&source,
|
||||||
|
enabled,
|
||||||
|
"PostToolUseFailure",
|
||||||
|
post_tool_use_failure,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_hook_list_entries(
|
||||||
|
entries: &mut Vec<HookListEntry>,
|
||||||
|
source: &str,
|
||||||
|
enabled: bool,
|
||||||
|
event: &'static str,
|
||||||
|
commands: &[String],
|
||||||
|
) {
|
||||||
|
entries.extend(commands.iter().cloned().map(|command| HookListEntry {
|
||||||
|
source: source.to_string(),
|
||||||
|
event,
|
||||||
|
command,
|
||||||
|
enabled,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
fn render_config_report(section: Option<&str>) -> Result<String, Box<dyn std::error::Error>> {
|
fn render_config_report(section: Option<&str>) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
let cwd = env::current_dir()?;
|
let cwd = env::current_dir()?;
|
||||||
let loader = ConfigLoader::default_for(&cwd);
|
let loader = ConfigLoader::default_for(&cwd);
|
||||||
@ -5943,6 +6077,11 @@ fn print_help_to(out: &mut impl Write) -> io::Result<()> {
|
|||||||
)?;
|
)?;
|
||||||
writeln!(out, " claw config show")?;
|
writeln!(out, " claw config show")?;
|
||||||
writeln!(out, " Print the merged runtime config as JSON")?;
|
writeln!(out, " Print the merged runtime config as JSON")?;
|
||||||
|
writeln!(out, " claw hook list")?;
|
||||||
|
writeln!(
|
||||||
|
out,
|
||||||
|
" Show registered hooks and whether they are enabled"
|
||||||
|
)?;
|
||||||
writeln!(out, " claw sandbox")?;
|
writeln!(out, " claw sandbox")?;
|
||||||
writeln!(out, " Show the current sandbox isolation snapshot")?;
|
writeln!(out, " Show the current sandbox isolation snapshot")?;
|
||||||
writeln!(out, " claw dump-manifests")?;
|
writeln!(out, " claw dump-manifests")?;
|
||||||
@ -6025,6 +6164,7 @@ fn print_help_to(out: &mut impl Write) -> io::Result<()> {
|
|||||||
" claw --resume {LATEST_SESSION_REFERENCE} /status /diff /export notes.txt"
|
" claw --resume {LATEST_SESSION_REFERENCE} /status /diff /export notes.txt"
|
||||||
)?;
|
)?;
|
||||||
writeln!(out, " claw config show")?;
|
writeln!(out, " claw config show")?;
|
||||||
|
writeln!(out, " claw hook list")?;
|
||||||
writeln!(out, " claw branch delete")?;
|
writeln!(out, " claw branch delete")?;
|
||||||
writeln!(out, " claw agents")?;
|
writeln!(out, " claw agents")?;
|
||||||
writeln!(out, " claw mcp show my-server")?;
|
writeln!(out, " claw mcp show my-server")?;
|
||||||
@ -6051,11 +6191,12 @@ mod tests {
|
|||||||
format_tool_result, format_ultraplan_report, format_unknown_slash_command,
|
format_tool_result, format_ultraplan_report, format_unknown_slash_command,
|
||||||
format_unknown_slash_command_message, git_ref_exists_in, normalize_permission_mode,
|
format_unknown_slash_command_message, git_ref_exists_in, normalize_permission_mode,
|
||||||
parse_args, parse_git_status_branch, parse_git_status_metadata_for,
|
parse_args, parse_git_status_branch, parse_git_status_metadata_for,
|
||||||
parse_git_workspace_summary, parse_git_worktrees, parse_recent_commits, permission_policy,
|
parse_git_workspace_summary, parse_git_worktrees, parse_hook_args, parse_recent_commits,
|
||||||
print_help_to, push_output_block, render_config_report, render_diff_report,
|
permission_policy, print_help_to, push_output_block, render_config_report,
|
||||||
render_diff_report_for, render_memory_report, render_merged_runtime_config_json,
|
render_diff_report, render_diff_report_for, render_hook_list_report_for,
|
||||||
render_repl_help, render_resume_usage, resolve_model_alias, resolve_session_reference,
|
render_memory_report, render_merged_runtime_config_json, render_repl_help,
|
||||||
response_to_events, resume_supported_slash_commands, run_resume_command,
|
render_resume_usage, resolve_model_alias, resolve_session_reference, response_to_events,
|
||||||
|
resume_supported_slash_commands, run_resume_command,
|
||||||
slash_command_completion_candidates_with_sessions, status_context, validate_no_args,
|
slash_command_completion_candidates_with_sessions, status_context, validate_no_args,
|
||||||
write_mcp_server_fixture, CliAction, CliOutputFormat, CliToolExecutor, GitBranchFreshness,
|
write_mcp_server_fixture, CliAction, CliOutputFormat, CliToolExecutor, GitBranchFreshness,
|
||||||
GitCommitEntry, GitWorkspaceSummary, GitWorktreeEntry, InternalPromptProgressEvent,
|
GitCommitEntry, GitWorkspaceSummary, GitWorktreeEntry, InternalPromptProgressEvent,
|
||||||
@ -6483,6 +6624,21 @@ mod tests {
|
|||||||
assert!(error.contains("Usage: claw config show"));
|
assert!(error.contains("Usage: claw config show"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_hook_list_subcommand() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_args(&["hook".to_string(), "list".to_string()]).expect("hook list should parse"),
|
||||||
|
CliAction::HookList
|
||||||
|
);
|
||||||
|
|
||||||
|
let error = parse_args(&["hook".to_string()]).expect_err("missing action should fail");
|
||||||
|
assert!(error.contains("Usage: claw hook list"));
|
||||||
|
|
||||||
|
let error = parse_hook_args(&["run".to_string()]).expect_err("unknown action should fail");
|
||||||
|
assert!(error.contains("unknown hook action: run"));
|
||||||
|
assert!(error.contains("Usage: claw hook list"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_single_word_command_aliases_without_falling_back_to_prompt_mode() {
|
fn parses_single_word_command_aliases_without_falling_back_to_prompt_mode() {
|
||||||
let _guard = env_lock();
|
let _guard = env_lock();
|
||||||
@ -6905,6 +7061,7 @@ mod tests {
|
|||||||
assert!(help.contains("claw help"));
|
assert!(help.contains("claw help"));
|
||||||
assert!(help.contains("claw version"));
|
assert!(help.contains("claw version"));
|
||||||
assert!(help.contains("claw status"));
|
assert!(help.contains("claw status"));
|
||||||
|
assert!(help.contains("claw hook list"));
|
||||||
assert!(help.contains("claw sandbox"));
|
assert!(help.contains("claw sandbox"));
|
||||||
assert!(help.contains("claw init"));
|
assert!(help.contains("claw init"));
|
||||||
assert!(help.contains("claw agents"));
|
assert!(help.contains("claw agents"));
|
||||||
@ -7180,6 +7337,49 @@ mod tests {
|
|||||||
assert!(report.contains("Merged JSON"));
|
assert!(report.contains("Merged JSON"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hook_list_report_shows_config_and_plugin_hooks_with_enabled_state() {
|
||||||
|
let config_home = temp_dir();
|
||||||
|
let workspace = temp_dir();
|
||||||
|
let source_root = temp_dir();
|
||||||
|
fs::create_dir_all(&config_home).expect("config home");
|
||||||
|
fs::create_dir_all(workspace.join(".claw")).expect("workspace config dir");
|
||||||
|
fs::create_dir_all(&source_root).expect("source root");
|
||||||
|
fs::write(
|
||||||
|
workspace.join(".claw").join("settings.json"),
|
||||||
|
r#"{"hooks":{"PostToolUse":["printf 'config post'"]}}"#,
|
||||||
|
)
|
||||||
|
.expect("workspace settings should write");
|
||||||
|
write_plugin_fixture(&source_root, "hook-report-demo", true, false);
|
||||||
|
|
||||||
|
let mut manager = PluginManager::new(PluginManagerConfig::new(&config_home));
|
||||||
|
manager
|
||||||
|
.install(source_root.to_str().expect("utf8 source path"))
|
||||||
|
.expect("plugin install should succeed");
|
||||||
|
manager
|
||||||
|
.disable("hook-report-demo@external")
|
||||||
|
.expect("plugin disable should succeed");
|
||||||
|
|
||||||
|
let loader = ConfigLoader::new(&workspace, &config_home);
|
||||||
|
let runtime_config = loader.load().expect("runtime config should load");
|
||||||
|
let report = render_hook_list_report_for(&workspace, &loader, &runtime_config)
|
||||||
|
.expect("hook list report should render");
|
||||||
|
|
||||||
|
assert!(report.contains("Hooks"));
|
||||||
|
assert!(report.contains("Registered "));
|
||||||
|
assert!(report.contains("Enabled "));
|
||||||
|
assert!(report.contains("yes config"));
|
||||||
|
assert!(report.contains("PostToolUse"));
|
||||||
|
assert!(report.contains("printf 'config post'"));
|
||||||
|
assert!(report.contains("no plugin:hook-report-demo@external"));
|
||||||
|
assert!(report.contains("PreToolUse"));
|
||||||
|
assert!(report.contains("hooks/pre.sh"));
|
||||||
|
|
||||||
|
let _ = fs::remove_dir_all(config_home);
|
||||||
|
let _ = fs::remove_dir_all(workspace);
|
||||||
|
let _ = fs::remove_dir_all(source_root);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_git_status_metadata() {
|
fn parses_git_status_metadata() {
|
||||||
let _guard = env_lock();
|
let _guard = env_lock();
|
||||||
@ -7494,6 +7694,7 @@ UU conflicted.rs",
|
|||||||
print_help_to(&mut help).expect("help should render");
|
print_help_to(&mut help).expect("help should render");
|
||||||
let help = String::from_utf8(help).expect("help should be utf8");
|
let help = String::from_utf8(help).expect("help should be utf8");
|
||||||
assert!(help.contains("claw config show"));
|
assert!(help.contains("claw config show"));
|
||||||
|
assert!(help.contains("claw hook list"));
|
||||||
assert!(help.contains("claw branch delete"));
|
assert!(help.contains("claw branch delete"));
|
||||||
assert!(help.contains("claw --resume [SESSION.jsonl|session-id|latest]"));
|
assert!(help.contains("claw --resume [SESSION.jsonl|session-id|latest]"));
|
||||||
assert!(help.contains("Use `latest` with --resume, /resume, or /session switch"));
|
assert!(help.contains("Use `latest` with --resume, /resume, or /session switch"));
|
||||||
@ -8137,8 +8338,12 @@ UU conflicted.rs",
|
|||||||
let runtime_config = loader.load().expect("runtime config should load");
|
let runtime_config = loader.load().expect("runtime config should load");
|
||||||
let state = build_runtime_plugin_state_with_loader(&workspace, &loader, &runtime_config)
|
let state = build_runtime_plugin_state_with_loader(&workspace, &loader, &runtime_config)
|
||||||
.expect("runtime plugin state should load");
|
.expect("runtime plugin state should load");
|
||||||
let mut executor =
|
let mut executor = CliToolExecutor::new(
|
||||||
CliToolExecutor::new(None, false, state.tool_registry.clone(), state.mcp_state.clone());
|
None,
|
||||||
|
false,
|
||||||
|
state.tool_registry.clone(),
|
||||||
|
state.mcp_state.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let search_output = executor
|
let search_output = executor
|
||||||
.execute("ToolSearch", r#"{"query":"remote","max_results":5}"#)
|
.execute("ToolSearch", r#"{"query":"remote","max_results":5}"#)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user