pub fn hook_pre_tool_use() {
let tool_input = std::env::var("TOOL_INPUT").unwrap_or_default();
println!("{}", evaluate_hook_decision(&tool_input));
}
pub fn evaluate_hook_decision(tool_input: &str) -> String {
let block_msg = serde_json::json!({
"decision": "block",
"reason": "STOP: Use tokensave MCP tools (tokensave_context, tokensave_search, \
tokensave_callees, tokensave_callers, tokensave_impact, tokensave_files, \
tokensave_affected) instead of agents for code research. Tokensave is \
faster and more precise for symbol relationships, call paths, and code \
structure. Only use agents for code exploration if you have already tried \
tokensave and it cannot answer the question."
});
let parsed: serde_json::Value =
serde_json::from_str(tool_input).unwrap_or_else(|_| serde_json::json!({}));
if parsed.get("subagent_type").and_then(|v| v.as_str()) == Some("Explore") {
return block_msg.to_string();
}
if let Some(prompt) = parsed.get("prompt").and_then(|v| v.as_str()) {
let lower = prompt.to_ascii_lowercase();
let exploration_patterns = [
"explore", "codebase structure", "codebase architecture", "codebase overview",
"source files contents", "read every", "full contents", "entire codebase",
"architecture and structure", "call graph", "call path", "call chain",
"symbol relat", "symbol lookup", "who calls", "callers of", "callees of",
];
if exploration_patterns.iter().any(|pat| lower.contains(pat)) {
return block_msg.to_string();
}
}
r#"{"decision": "allow"}"#.to_string()
}