feat(v2): complete multi-LLM providers, TUI redesign, and advanced agent features
Multi-LLM Provider Support: - Add llm-core crate with LlmProvider trait abstraction - Implement Anthropic Claude API client with streaming - Implement OpenAI API client with streaming - Add token counting with SimpleTokenCounter and ClaudeTokenCounter - Add retry logic with exponential backoff and jitter Borderless TUI Redesign: - Rewrite theme system with terminal capability detection (Full/Unicode256/Basic) - Add provider tabs component with keybind switching [1]/[2]/[3] - Implement vim-modal input (Normal/Insert/Visual/Command modes) - Redesign chat panel with timestamps and streaming indicators - Add multi-provider status bar with cost tracking - Add Nerd Font icons with graceful ASCII fallbacks - Add syntax highlighting (syntect) and markdown rendering (pulldown-cmark) Advanced Agent Features: - Add system prompt builder with configurable components - Enhance subagent orchestration with parallel execution - Add git integration module for safe command detection - Add streaming tool results via channels - Expand tool set: AskUserQuestion, TodoWrite, LS, MultiEdit, BashOutput, KillShell - Add WebSearch with provider abstraction Plugin System Enhancement: - Add full agent definition parsing from YAML frontmatter - Add skill system with progressive disclosure - Wire plugin hooks into HookManager 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
11
crates/tools/ask/Cargo.toml
Normal file
11
crates/tools/ask/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "tools-ask"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1", features = ["sync"] }
|
||||
color-eyre = "0.6"
|
||||
60
crates/tools/ask/src/lib.rs
Normal file
60
crates/tools/ask/src/lib.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
//! AskUserQuestion tool for interactive user input
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
/// A question option
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct QuestionOption {
|
||||
pub label: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// A question to ask the user
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Question {
|
||||
pub question: String,
|
||||
pub header: String,
|
||||
pub options: Vec<QuestionOption>,
|
||||
pub multi_select: bool,
|
||||
}
|
||||
|
||||
/// Request sent to the UI to ask questions
|
||||
#[derive(Debug)]
|
||||
pub struct AskRequest {
|
||||
pub questions: Vec<Question>,
|
||||
pub response_tx: oneshot::Sender<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
/// Channel for sending ask requests to the UI
|
||||
pub type AskSender = mpsc::Sender<AskRequest>;
|
||||
pub type AskReceiver = mpsc::Receiver<AskRequest>;
|
||||
|
||||
/// Create a channel pair for ask requests
|
||||
pub fn create_ask_channel() -> (AskSender, AskReceiver) {
|
||||
mpsc::channel(1)
|
||||
}
|
||||
|
||||
/// Ask the user questions (called by agent)
|
||||
pub async fn ask_user(
|
||||
sender: &AskSender,
|
||||
questions: Vec<Question>,
|
||||
) -> color_eyre::Result<HashMap<String, String>> {
|
||||
let (response_tx, response_rx) = oneshot::channel();
|
||||
|
||||
sender.send(AskRequest { questions, response_tx }).await
|
||||
.map_err(|_| color_eyre::eyre::eyre!("Failed to send ask request"))?;
|
||||
|
||||
response_rx.await
|
||||
.map_err(|_| color_eyre::eyre::eyre!("Failed to receive ask response"))
|
||||
}
|
||||
|
||||
/// Parse questions from JSON tool input
|
||||
pub fn parse_questions(input: &serde_json::Value) -> color_eyre::Result<Vec<Question>> {
|
||||
let questions = input.get("questions")
|
||||
.ok_or_else(|| color_eyre::eyre::eyre!("Missing 'questions' field"))?;
|
||||
|
||||
serde_json::from_value(questions.clone())
|
||||
.map_err(|e| color_eyre::eyre::eyre!("Invalid questions format: {}", e))
|
||||
}
|
||||
Reference in New Issue
Block a user