thought: wire up agent and subconscious to use shared tools
- agent/tools/mod.rs: remove duplicated tool implementations, delegate
to thought::dispatch for shared tools, keep only agent-specific
tools (control, vision, working_stack)
- subconscious/api.rs: replace duplicated memory/tool dispatch with
thought::dispatch, use thought::all_definitions() for tool schemas
- Delete agent/tools/{bash,read,write,edit,grep,glob_tool,journal,memory}.rs
(now live in thought/)
Both poc-agent and subconscious agents now use the same tool
implementations through the thought layer. Agent-specific behavior
(node tracking in runner.rs, control tools) stays in agent/.
2026-03-27 15:27:33 -04:00
|
|
|
// tools/mod.rs — Agent-specific tool dispatch
|
2026-03-25 00:52:41 -04:00
|
|
|
//
|
thought: wire up agent and subconscious to use shared tools
- agent/tools/mod.rs: remove duplicated tool implementations, delegate
to thought::dispatch for shared tools, keep only agent-specific
tools (control, vision, working_stack)
- subconscious/api.rs: replace duplicated memory/tool dispatch with
thought::dispatch, use thought::all_definitions() for tool schemas
- Delete agent/tools/{bash,read,write,edit,grep,glob_tool,journal,memory}.rs
(now live in thought/)
Both poc-agent and subconscious agents now use the same tool
implementations through the thought layer. Agent-specific behavior
(node tracking in runner.rs, control tools) stays in agent/.
2026-03-27 15:27:33 -04:00
|
|
|
// Shared tools (memory, files, bash, journal) live in thought/.
|
|
|
|
|
// This module handles agent-specific tools (control, vision,
|
|
|
|
|
// working_stack) and delegates everything else to thought::dispatch.
|
2026-03-25 00:52:41 -04:00
|
|
|
|
2026-04-03 21:59:14 -04:00
|
|
|
// Core tools
|
2026-04-04 00:29:11 -04:00
|
|
|
mod bash;
|
2026-04-04 14:45:22 -04:00
|
|
|
pub mod channels;
|
2026-04-04 00:29:11 -04:00
|
|
|
mod edit;
|
|
|
|
|
mod glob;
|
|
|
|
|
mod grep;
|
2026-04-04 14:45:22 -04:00
|
|
|
pub mod memory;
|
2026-04-04 00:29:11 -04:00
|
|
|
mod read;
|
2026-04-04 12:18:11 -04:00
|
|
|
mod web;
|
2026-04-04 00:29:11 -04:00
|
|
|
mod write;
|
2026-04-03 21:59:14 -04:00
|
|
|
|
|
|
|
|
// Agent-specific tools
|
2026-03-25 00:52:41 -04:00
|
|
|
mod control;
|
|
|
|
|
mod vision;
|
|
|
|
|
pub mod working_stack;
|
|
|
|
|
|
2026-04-03 23:21:16 -04:00
|
|
|
use serde::{Serialize, Deserialize};
|
tools/memory: one function per tool
Split the monolithic dispatch(name, args) into individual public
functions (render, write, search, links, link_set, link_add, used,
weight_set, rename, supersede, query, output, journal_tail,
journal_new, journal_update) each with a matching _def() function.
The old dispatch() remains as a thin match for backward compat
until the Tool registry replaces it.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-04 15:03:04 -04:00
|
|
|
use std::future::Future;
|
|
|
|
|
use std::pin::Pin;
|
2026-04-03 23:21:16 -04:00
|
|
|
use std::time::Instant;
|
|
|
|
|
|
|
|
|
|
fn default_timeout() -> u64 { 120 }
|
|
|
|
|
|
tools/memory: one function per tool
Split the monolithic dispatch(name, args) into individual public
functions (render, write, search, links, link_set, link_add, used,
weight_set, rename, supersede, query, output, journal_tail,
journal_new, journal_update) each with a matching _def() function.
The old dispatch() remains as a thin match for backward compat
until the Tool registry replaces it.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-04 15:03:04 -04:00
|
|
|
/// Async tool handler function.
|
|
|
|
|
/// Agent is None when called from contexts without an agent (MCP server, subconscious).
|
|
|
|
|
pub type ToolHandler = fn(
|
|
|
|
|
Option<std::sync::Arc<tokio::sync::Mutex<super::Agent>>>,
|
|
|
|
|
serde_json::Value,
|
|
|
|
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<String>> + Send>>;
|
|
|
|
|
|
|
|
|
|
/// A tool with its definition and handler — single source of truth.
|
|
|
|
|
pub struct Tool {
|
|
|
|
|
pub def: ToolDef,
|
|
|
|
|
pub handler: ToolHandler,
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 23:21:16 -04:00
|
|
|
/// Function call within a tool call — name + JSON arguments.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct FunctionCall {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub arguments: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Function definition for tool schema.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct FunctionDef {
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub description: String,
|
|
|
|
|
pub parameters: serde_json::Value,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Partial function call within a streaming delta.
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub struct FunctionCallDelta {
|
|
|
|
|
pub name: Option<String>,
|
|
|
|
|
pub arguments: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tool definition sent to the model.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ToolDef {
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
|
pub tool_type: String,
|
|
|
|
|
pub function: FunctionDef,
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 00:29:11 -04:00
|
|
|
impl ToolDef {
|
|
|
|
|
pub fn new(name: &str, description: &str, parameters: serde_json::Value) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
tool_type: "function".to_string(),
|
|
|
|
|
function: FunctionDef {
|
|
|
|
|
name: name.to_string(),
|
|
|
|
|
description: description.to_string(),
|
|
|
|
|
parameters,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 23:21:16 -04:00
|
|
|
/// A tool call requested by the model.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ToolCall {
|
|
|
|
|
pub id: String,
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
|
pub call_type: String,
|
|
|
|
|
pub function: FunctionCall,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A partial tool call within a streaming delta. The first chunk for a
|
|
|
|
|
/// given tool call carries the id and function name; subsequent chunks
|
|
|
|
|
/// carry argument fragments.
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub struct ToolCallDelta {
|
|
|
|
|
pub index: usize,
|
|
|
|
|
pub id: Option<String>,
|
|
|
|
|
#[serde(rename = "type")]
|
|
|
|
|
pub call_type: Option<String>,
|
|
|
|
|
pub function: Option<FunctionCallDelta>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Result of dispatching a tool call.
|
|
|
|
|
pub struct ToolOutput {
|
|
|
|
|
pub text: String,
|
|
|
|
|
pub is_yield: bool,
|
|
|
|
|
/// Base64 data URIs for images to attach to the next message.
|
|
|
|
|
pub images: Vec<String>,
|
|
|
|
|
/// Model name to switch to (deferred to session level).
|
|
|
|
|
pub model_switch: Option<String>,
|
|
|
|
|
/// Agent requested DMN pause (deferred to session level).
|
|
|
|
|
pub dmn_pause: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ToolOutput {
|
|
|
|
|
pub fn error(e: impl std::fmt::Display) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
text: format!("Error: {}", e),
|
|
|
|
|
is_yield: false,
|
|
|
|
|
images: Vec::new(),
|
|
|
|
|
model_switch: None,
|
|
|
|
|
dmn_pause: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn text(s: String) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
text: s,
|
|
|
|
|
is_yield: false,
|
|
|
|
|
images: Vec::new(),
|
|
|
|
|
model_switch: None,
|
|
|
|
|
dmn_pause: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 23:42:27 -04:00
|
|
|
/// A tool call in flight — metadata for TUI + JoinHandle for
|
|
|
|
|
/// result collection and cancellation.
|
|
|
|
|
pub struct ActiveToolCall {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub detail: String,
|
|
|
|
|
pub started: Instant,
|
|
|
|
|
pub background: bool,
|
|
|
|
|
pub handle: tokio::task::JoinHandle<(ToolCall, ToolOutput)>,
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 23:21:16 -04:00
|
|
|
/// Truncate output if it exceeds max length, appending a truncation notice.
|
|
|
|
|
pub fn truncate_output(mut s: String, max: usize) -> String {
|
|
|
|
|
if s.len() > max {
|
|
|
|
|
s.truncate(max);
|
|
|
|
|
s.push_str("\n... (output truncated)");
|
|
|
|
|
}
|
|
|
|
|
s
|
|
|
|
|
}
|
2026-03-25 00:52:41 -04:00
|
|
|
|
|
|
|
|
/// Dispatch a tool call by name.
|
|
|
|
|
///
|
thought: wire up agent and subconscious to use shared tools
- agent/tools/mod.rs: remove duplicated tool implementations, delegate
to thought::dispatch for shared tools, keep only agent-specific
tools (control, vision, working_stack)
- subconscious/api.rs: replace duplicated memory/tool dispatch with
thought::dispatch, use thought::all_definitions() for tool schemas
- Delete agent/tools/{bash,read,write,edit,grep,glob_tool,journal,memory}.rs
(now live in thought/)
Both poc-agent and subconscious agents now use the same tool
implementations through the thought layer. Agent-specific behavior
(node tracking in runner.rs, control tools) stays in agent/.
2026-03-27 15:27:33 -04:00
|
|
|
/// Tries agent-specific tools first (control, vision), then
|
|
|
|
|
/// delegates to thought::dispatch for shared tools.
|
2026-03-25 00:52:41 -04:00
|
|
|
///
|
thought: wire up agent and subconscious to use shared tools
- agent/tools/mod.rs: remove duplicated tool implementations, delegate
to thought::dispatch for shared tools, keep only agent-specific
tools (control, vision, working_stack)
- subconscious/api.rs: replace duplicated memory/tool dispatch with
thought::dispatch, use thought::all_definitions() for tool schemas
- Delete agent/tools/{bash,read,write,edit,grep,glob_tool,journal,memory}.rs
(now live in thought/)
Both poc-agent and subconscious agents now use the same tool
implementations through the thought layer. Agent-specific behavior
(node tracking in runner.rs, control tools) stays in agent/.
2026-03-27 15:27:33 -04:00
|
|
|
/// Note: working_stack is handled in runner.rs before reaching this
|
2026-03-25 00:52:41 -04:00
|
|
|
/// function (it needs mutable context access).
|
2026-04-03 23:21:16 -04:00
|
|
|
/// Dispatch a tool call by name. Handles all tools:
|
|
|
|
|
/// agent-specific (control, vision), memory/journal, file/bash.
|
2026-03-25 00:52:41 -04:00
|
|
|
pub async fn dispatch(
|
|
|
|
|
name: &str,
|
|
|
|
|
args: &serde_json::Value,
|
|
|
|
|
) -> ToolOutput {
|
2026-04-03 23:21:16 -04:00
|
|
|
// Agent-specific tools
|
2026-03-25 00:52:41 -04:00
|
|
|
let rich_result = match name {
|
|
|
|
|
"pause" => Some(control::pause(args)),
|
|
|
|
|
"switch_model" => Some(control::switch_model(args)),
|
|
|
|
|
"yield_to_user" => Some(control::yield_to_user(args)),
|
|
|
|
|
"view_image" => Some(vision::view_image(args)),
|
|
|
|
|
_ => None,
|
|
|
|
|
};
|
|
|
|
|
if let Some(result) = rich_result {
|
|
|
|
|
return result.unwrap_or_else(ToolOutput::error);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 23:56:56 -04:00
|
|
|
if let Some(output) = dispatch_shared(name, args, None).await {
|
thought: wire up agent and subconscious to use shared tools
- agent/tools/mod.rs: remove duplicated tool implementations, delegate
to thought::dispatch for shared tools, keep only agent-specific
tools (control, vision, working_stack)
- subconscious/api.rs: replace duplicated memory/tool dispatch with
thought::dispatch, use thought::all_definitions() for tool schemas
- Delete agent/tools/{bash,read,write,edit,grep,glob_tool,journal,memory}.rs
(now live in thought/)
Both poc-agent and subconscious agents now use the same tool
implementations through the thought layer. Agent-specific behavior
(node tracking in runner.rs, control tools) stays in agent/.
2026-03-27 15:27:33 -04:00
|
|
|
return output;
|
2026-03-25 00:52:41 -04:00
|
|
|
}
|
thought: wire up agent and subconscious to use shared tools
- agent/tools/mod.rs: remove duplicated tool implementations, delegate
to thought::dispatch for shared tools, keep only agent-specific
tools (control, vision, working_stack)
- subconscious/api.rs: replace duplicated memory/tool dispatch with
thought::dispatch, use thought::all_definitions() for tool schemas
- Delete agent/tools/{bash,read,write,edit,grep,glob_tool,journal,memory}.rs
(now live in thought/)
Both poc-agent and subconscious agents now use the same tool
implementations through the thought layer. Agent-specific behavior
(node tracking in runner.rs, control tools) stays in agent/.
2026-03-27 15:27:33 -04:00
|
|
|
|
|
|
|
|
ToolOutput::error(format!("Unknown tool: {}", name))
|
2026-03-25 00:52:41 -04:00
|
|
|
}
|
|
|
|
|
|
2026-04-03 23:21:16 -04:00
|
|
|
/// Dispatch shared tools (memory, file, bash). Used by both the
|
|
|
|
|
/// interactive agent and subconscious agents. Provenance tracks
|
|
|
|
|
/// which agent made the call for memory attribution.
|
|
|
|
|
pub async fn dispatch_shared(
|
|
|
|
|
name: &str,
|
|
|
|
|
args: &serde_json::Value,
|
|
|
|
|
provenance: Option<&str>,
|
|
|
|
|
) -> Option<ToolOutput> {
|
|
|
|
|
// Memory and journal tools
|
|
|
|
|
if name.starts_with("memory_") || name.starts_with("journal_") || name == "output" {
|
|
|
|
|
let result = memory::dispatch(name, args, provenance);
|
|
|
|
|
return Some(match result {
|
|
|
|
|
Ok(s) => ToolOutput::text(s),
|
|
|
|
|
Err(e) => ToolOutput::error(e),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 14:45:22 -04:00
|
|
|
// Channel tools
|
|
|
|
|
if name.starts_with("channel_") {
|
|
|
|
|
let result = channels::dispatch(name, args).await;
|
|
|
|
|
return Some(match result {
|
|
|
|
|
Ok(s) => ToolOutput::text(s),
|
|
|
|
|
Err(e) => ToolOutput::error(e),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 23:21:16 -04:00
|
|
|
// File and execution tools
|
|
|
|
|
let result = match name {
|
|
|
|
|
"read_file" => read::read_file(args),
|
|
|
|
|
"write_file" => write::write_file(args),
|
|
|
|
|
"edit_file" => edit::edit_file(args),
|
2026-04-03 23:56:56 -04:00
|
|
|
"bash" => bash::run_bash(args).await,
|
2026-04-04 12:18:11 -04:00
|
|
|
"web_fetch" => web::web_fetch(args).await,
|
|
|
|
|
"web_search" => web::web_search(args).await,
|
2026-04-03 23:21:16 -04:00
|
|
|
"grep" => grep::grep(args),
|
|
|
|
|
"glob" => glob::glob_search(args),
|
|
|
|
|
_ => return None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Some(match result {
|
|
|
|
|
Ok(s) => ToolOutput::text(s),
|
|
|
|
|
Err(e) => ToolOutput::error(e),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-04 15:07:55 -04:00
|
|
|
/// Return all registered tools with definitions + handlers.
|
|
|
|
|
pub fn tools() -> Vec<Tool> {
|
|
|
|
|
vec![
|
|
|
|
|
// File tools
|
|
|
|
|
Tool { def: read::definition(), handler: |_a, v| Box::pin(async move { read::read_file(&v) }) },
|
|
|
|
|
Tool { def: write::definition(), handler: |_a, v| Box::pin(async move { write::write_file(&v) }) },
|
|
|
|
|
Tool { def: edit::definition(), handler: |_a, v| Box::pin(async move { edit::edit_file(&v) }) },
|
|
|
|
|
Tool { def: grep::definition(), handler: |_a, v| Box::pin(async move { grep::grep(&v) }) },
|
|
|
|
|
Tool { def: glob::definition(), handler: |_a, v| Box::pin(async move { glob::glob_search(&v) }) },
|
|
|
|
|
|
|
|
|
|
// Execution tools
|
|
|
|
|
Tool { def: bash::definition(), handler: |_a, v| Box::pin(async move { bash::run_bash(&v).await }) },
|
|
|
|
|
Tool { def: web::fetch_definition(), handler: |_a, v| Box::pin(async move { web::web_fetch(&v).await }) },
|
|
|
|
|
Tool { def: web::search_definition(), handler: |_a, v| Box::pin(async move { web::web_search(&v).await }) },
|
|
|
|
|
|
|
|
|
|
// Vision
|
|
|
|
|
Tool { def: vision::definition(), handler: |_a, v| Box::pin(async move { vision::view_image_text(&v) }) },
|
|
|
|
|
|
|
|
|
|
// Memory tools
|
|
|
|
|
Tool { def: memory::render_def(), handler: |_a, v| Box::pin(async move { memory::render(&v) }) },
|
|
|
|
|
Tool { def: memory::write_def(), handler: |_a, v| Box::pin(async move { memory::write(&v) }) },
|
|
|
|
|
Tool { def: memory::search_def(), handler: |_a, v| Box::pin(async move { memory::search(&v) }) },
|
|
|
|
|
Tool { def: memory::links_def(), handler: |_a, v| Box::pin(async move { memory::links(&v) }) },
|
|
|
|
|
Tool { def: memory::link_set_def(), handler: |_a, v| Box::pin(async move { memory::link_set(&v) }) },
|
|
|
|
|
Tool { def: memory::link_add_def(), handler: |_a, v| Box::pin(async move { memory::link_add(&v) }) },
|
|
|
|
|
Tool { def: memory::used_def(), handler: |_a, v| Box::pin(async move { memory::used(&v) }) },
|
|
|
|
|
Tool { def: memory::weight_set_def(), handler: |_a, v| Box::pin(async move { memory::weight_set(&v) }) },
|
|
|
|
|
Tool { def: memory::rename_def(), handler: |_a, v| Box::pin(async move { memory::rename(&v) }) },
|
|
|
|
|
Tool { def: memory::supersede_def(), handler: |_a, v| Box::pin(async move { memory::supersede(&v) }) },
|
|
|
|
|
Tool { def: memory::query_def(), handler: |_a, v| Box::pin(async move { memory::query(&v) }) },
|
|
|
|
|
Tool { def: memory::output_def(), handler: |_a, v| Box::pin(async move { memory::output(&v) }) },
|
|
|
|
|
|
|
|
|
|
// Channel tools
|
|
|
|
|
Tool { def: channels::definitions()[0].clone(), handler: |_a, v| Box::pin(async move { channels::dispatch("channel_list", &v).await }) },
|
|
|
|
|
Tool { def: channels::definitions()[1].clone(), handler: |_a, v| Box::pin(async move { channels::dispatch("channel_recv", &v).await }) },
|
|
|
|
|
Tool { def: channels::definitions()[2].clone(), handler: |_a, v| Box::pin(async move { channels::dispatch("channel_send", &v).await }) },
|
|
|
|
|
Tool { def: channels::definitions()[3].clone(), handler: |_a, v| Box::pin(async move { channels::dispatch("channel_notifications", &v).await }) },
|
|
|
|
|
|
|
|
|
|
// Control tools
|
|
|
|
|
Tool { def: control::definitions()[0].clone(),
|
|
|
|
|
handler: |_a, _v| Box::pin(async { Ok("Pausing autonomous behavior. Only user input will wake you.".into()) }) },
|
|
|
|
|
Tool { def: control::definitions()[1].clone(),
|
|
|
|
|
handler: |_a, v| Box::pin(async move {
|
|
|
|
|
let model = v.get("model").and_then(|v| v.as_str()).unwrap_or("");
|
|
|
|
|
Ok(format!("Switching to model: {}", model))
|
|
|
|
|
}) },
|
|
|
|
|
Tool { def: control::definitions()[2].clone(),
|
|
|
|
|
handler: |_a, v| Box::pin(async move {
|
|
|
|
|
let msg = v.get("message").and_then(|v| v.as_str()).unwrap_or("(yielding to user)");
|
|
|
|
|
Ok(msg.to_string())
|
|
|
|
|
}) },
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Return all tool definitions (extracted from tools()).
|
2026-03-25 00:52:41 -04:00
|
|
|
pub fn definitions() -> Vec<ToolDef> {
|
2026-04-04 15:07:55 -04:00
|
|
|
tools().into_iter().map(|t| t.def).collect()
|
2026-03-25 00:52:41 -04:00
|
|
|
}
|
2026-04-03 23:21:16 -04:00
|
|
|
|
|
|
|
|
/// Return memory + journal tool definitions only.
|
|
|
|
|
pub fn memory_and_journal_definitions() -> Vec<ToolDef> {
|
|
|
|
|
let mut defs = memory::definitions();
|
|
|
|
|
defs.extend(memory::journal_definitions());
|
|
|
|
|
defs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a short summary of tool args for the tools pane header.
|
|
|
|
|
pub fn summarize_args(tool_name: &str, args: &serde_json::Value) -> String {
|
|
|
|
|
match tool_name {
|
|
|
|
|
"read_file" | "write_file" | "edit_file" => args["file_path"]
|
|
|
|
|
.as_str()
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
"bash" => {
|
|
|
|
|
let cmd = args["command"].as_str().unwrap_or("");
|
|
|
|
|
if cmd.len() > 60 {
|
|
|
|
|
let end = cmd.char_indices()
|
|
|
|
|
.map(|(i, _)| i)
|
|
|
|
|
.take_while(|&i| i <= 60)
|
|
|
|
|
.last()
|
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
format!("{}...", &cmd[..end])
|
|
|
|
|
} else {
|
|
|
|
|
cmd.to_string()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"grep" => {
|
|
|
|
|
let pattern = args["pattern"].as_str().unwrap_or("");
|
|
|
|
|
let path = args["path"].as_str().unwrap_or(".");
|
|
|
|
|
format!("{} in {}", pattern, path)
|
|
|
|
|
}
|
|
|
|
|
"glob" => args["pattern"]
|
|
|
|
|
.as_str()
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
"view_image" => {
|
|
|
|
|
if let Some(pane) = args["pane_id"].as_str() {
|
|
|
|
|
format!("pane {}", pane)
|
|
|
|
|
} else {
|
|
|
|
|
args["file_path"].as_str().unwrap_or("").to_string()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"journal" => {
|
|
|
|
|
let entry = args["entry"].as_str().unwrap_or("");
|
|
|
|
|
if entry.len() > 60 {
|
|
|
|
|
format!("{}...", &entry[..60])
|
|
|
|
|
} else {
|
|
|
|
|
entry.to_string()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"yield_to_user" => args["message"]
|
|
|
|
|
.as_str()
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
"switch_model" => args["model"]
|
|
|
|
|
.as_str()
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
.to_string(),
|
|
|
|
|
"pause" => String::new(),
|
|
|
|
|
_ => String::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|