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:
74
crates/core/agent/examples/git_demo.rs
Normal file
74
crates/core/agent/examples/git_demo.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
//! Example demonstrating the git integration module
|
||||
//!
|
||||
//! Run with: cargo run -p agent-core --example git_demo
|
||||
|
||||
use agent_core::{detect_git_state, format_git_status, is_safe_git_command, is_destructive_git_command};
|
||||
use std::env;
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
// Get current working directory
|
||||
let cwd = env::current_dir()?;
|
||||
println!("Detecting git state in: {}\n", cwd.display());
|
||||
|
||||
// Detect git state
|
||||
let state = detect_git_state(&cwd)?;
|
||||
|
||||
// Display formatted status
|
||||
println!("{}\n", format_git_status(&state));
|
||||
|
||||
// Show detailed file status if there are changes
|
||||
if !state.status.is_empty() {
|
||||
println!("Detailed file status:");
|
||||
for status in &state.status {
|
||||
match status {
|
||||
agent_core::GitFileStatus::Modified { path } => {
|
||||
println!(" M {}", path);
|
||||
}
|
||||
agent_core::GitFileStatus::Added { path } => {
|
||||
println!(" A {}", path);
|
||||
}
|
||||
agent_core::GitFileStatus::Deleted { path } => {
|
||||
println!(" D {}", path);
|
||||
}
|
||||
agent_core::GitFileStatus::Renamed { from, to } => {
|
||||
println!(" R {} -> {}", from, to);
|
||||
}
|
||||
agent_core::GitFileStatus::Untracked { path } => {
|
||||
println!(" ? {}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
// Test command safety checking
|
||||
println!("Command safety checks:");
|
||||
let test_commands = vec![
|
||||
"git status",
|
||||
"git log --oneline",
|
||||
"git diff HEAD",
|
||||
"git commit -m 'test'",
|
||||
"git push --force origin main",
|
||||
"git reset --hard HEAD~1",
|
||||
"git rebase main",
|
||||
"git branch -D feature",
|
||||
];
|
||||
|
||||
for cmd in test_commands {
|
||||
let is_safe = is_safe_git_command(cmd);
|
||||
let (is_destructive, warning) = is_destructive_git_command(cmd);
|
||||
|
||||
print!(" {} - ", cmd);
|
||||
if is_safe {
|
||||
println!("SAFE (read-only)");
|
||||
} else if is_destructive {
|
||||
println!("DESTRUCTIVE: {}", warning);
|
||||
} else {
|
||||
println!("UNSAFE (modifies state)");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
92
crates/core/agent/examples/streaming_agent.rs
Normal file
92
crates/core/agent/examples/streaming_agent.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
/// Example demonstrating the streaming agent loop API
|
||||
///
|
||||
/// This example shows how to use `run_agent_loop_streaming` to receive
|
||||
/// real-time events during agent execution, including:
|
||||
/// - Text deltas as the LLM generates text
|
||||
/// - Tool execution start/end events
|
||||
/// - Tool output events
|
||||
/// - Final completion events
|
||||
///
|
||||
/// Run with: cargo run --example streaming_agent -p agent-core
|
||||
|
||||
use agent_core::{create_event_channel, run_agent_loop_streaming, AgentEvent, ToolContext};
|
||||
use llm_core::ChatOptions;
|
||||
use permissions::{Mode, PermissionManager};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> color_eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
// Note: This is a minimal example. In a real application, you would:
|
||||
// 1. Initialize a real LLM provider (e.g., OllamaClient)
|
||||
// 2. Configure the ChatOptions with your preferred model
|
||||
// 3. Set up appropriate permissions and tool context
|
||||
|
||||
println!("=== Streaming Agent Example ===\n");
|
||||
println!("This example demonstrates how to use the streaming agent loop API.");
|
||||
println!("To run with a real LLM provider, modify this example to:");
|
||||
println!(" 1. Create an LLM provider instance");
|
||||
println!(" 2. Set up permissions and tool context");
|
||||
println!(" 3. Call run_agent_loop_streaming with your prompt\n");
|
||||
|
||||
// Example code structure:
|
||||
println!("Example code:");
|
||||
println!("```rust");
|
||||
println!("// Create LLM provider");
|
||||
println!("let provider = OllamaClient::new(\"http://localhost:11434\");");
|
||||
println!();
|
||||
println!("// Set up permissions and context");
|
||||
println!("let perms = PermissionManager::new(Mode::Plan);");
|
||||
println!("let ctx = ToolContext::default();");
|
||||
println!();
|
||||
println!("// Create event channel");
|
||||
println!("let (tx, mut rx) = create_event_channel();");
|
||||
println!();
|
||||
println!("// Spawn agent loop");
|
||||
println!("let handle = tokio::spawn(async move {{");
|
||||
println!(" run_agent_loop_streaming(");
|
||||
println!(" &provider,");
|
||||
println!(" \"Your prompt here\",");
|
||||
println!(" &ChatOptions::default(),");
|
||||
println!(" &perms,");
|
||||
println!(" &ctx,");
|
||||
println!(" tx,");
|
||||
println!(" ).await");
|
||||
println!("}});");
|
||||
println!();
|
||||
println!("// Process events");
|
||||
println!("while let Some(event) = rx.recv().await {{");
|
||||
println!(" match event {{");
|
||||
println!(" AgentEvent::TextDelta(text) => {{");
|
||||
println!(" print!(\"{{text}}\");");
|
||||
println!(" }}");
|
||||
println!(" AgentEvent::ToolStart {{ tool_name, .. }} => {{");
|
||||
println!(" println!(\"\\n[Executing tool: {{tool_name}}]\");");
|
||||
println!(" }}");
|
||||
println!(" AgentEvent::ToolOutput {{ content, is_error, .. }} => {{");
|
||||
println!(" if is_error {{");
|
||||
println!(" eprintln!(\"Error: {{content}}\");");
|
||||
println!(" }} else {{");
|
||||
println!(" println!(\"Output: {{content}}\");");
|
||||
println!(" }}");
|
||||
println!(" }}");
|
||||
println!(" AgentEvent::ToolEnd {{ success, .. }} => {{");
|
||||
println!(" println!(\"[Tool finished: {{}}]\", if success {{ \"success\" }} else {{ \"failed\" }});");
|
||||
println!(" }}");
|
||||
println!(" AgentEvent::Done {{ final_response }} => {{");
|
||||
println!(" println!(\"\\n\\nFinal response: {{final_response}}\");");
|
||||
println!(" break;");
|
||||
println!(" }}");
|
||||
println!(" AgentEvent::Error(e) => {{");
|
||||
println!(" eprintln!(\"Error: {{e}}\");");
|
||||
println!(" break;");
|
||||
println!(" }}");
|
||||
println!(" }}");
|
||||
println!("}}");
|
||||
println!();
|
||||
println!("// Wait for completion");
|
||||
println!("let result = handle.await??;");
|
||||
println!("```");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user