feat(tickets): création d'un assistant IA de ticket (backend + frontend)
Ajoute le chat assistant IA attaché à un ticket (#8). Backend Rust : - use cases OpenTicketAssistant/CloseTicketAssistant + tests - politique d'outils par agent (domain/agent_tool_policy) et policy MCP - store de contexte assistant + gabarit default_ticket_assistant.md + tests - events TicketAssistantOpened/Closed - commandes Tauri open_ticket_chat/close_ticket_chat et câblage state/lib/events Frontend : - gateway (ports, adapters ticket + mock, domain) - hook useTicketAssistant + composant TicketAssistantPanel - intégration dans TicketDetail Tests verts : vitest tickets.test.tsx (23), cargo test application::ticket_assistant (1), infrastructure::assistant_context_store (2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -0,0 +1,5 @@
|
||||
# Ticket Assistant
|
||||
|
||||
You are IdeA's ticket editing assistant. Help improve the bound ticket only.
|
||||
Read the project context when needed, propose concise changes, and use ticket
|
||||
tools only for the ticket explicitly injected below.
|
||||
76
crates/infrastructure/src/assistant/mod.rs
Normal file
76
crates/infrastructure/src/assistant/mod.rs
Normal file
@ -0,0 +1,76 @@
|
||||
//! Ticket assistant infrastructure adapters.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use domain::{
|
||||
AssistantContextError, AssistantContextProvider, FileSystem, FsError, Issue, MarkdownDoc,
|
||||
PreparedContext, Project, RemotePath,
|
||||
};
|
||||
|
||||
const ASSISTANT_DIR: &str = "assistant";
|
||||
const TICKET_ASSISTANT_FILE: &str = "ticket-assistant.md";
|
||||
const DEFAULT_TICKET_ASSISTANT_CONTEXT: &str = include_str!("default_ticket_assistant.md");
|
||||
|
||||
/// File-backed provider for the IdeA-owned ticket assistant context.
|
||||
#[derive(Clone)]
|
||||
pub struct FsAssistantContextStore {
|
||||
fs: Arc<dyn FileSystem>,
|
||||
app_data_dir: String,
|
||||
}
|
||||
|
||||
impl FsAssistantContextStore {
|
||||
/// Builds the store from an injected filesystem and app-data directory.
|
||||
#[must_use]
|
||||
pub fn new(fs: Arc<dyn FileSystem>, app_data_dir: impl Into<String>) -> Self {
|
||||
Self {
|
||||
fs,
|
||||
app_data_dir: app_data_dir.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn join(&self, rel: &str) -> String {
|
||||
let base = self.app_data_dir.trim_end_matches(['/', '\\']);
|
||||
format!("{base}/{rel}")
|
||||
}
|
||||
|
||||
fn context_path(&self) -> RemotePath {
|
||||
RemotePath::new(self.join(&format!("{ASSISTANT_DIR}/{TICKET_ASSISTANT_FILE}")))
|
||||
}
|
||||
|
||||
async fn read_base_context(&self) -> Result<String, AssistantContextError> {
|
||||
let path = self.context_path();
|
||||
match self.fs.read(&path).await {
|
||||
Ok(bytes) => {
|
||||
String::from_utf8(bytes).map_err(|e| AssistantContextError::Store(e.to_string()))
|
||||
}
|
||||
Err(FsError::NotFound(_)) => Ok(DEFAULT_TICKET_ASSISTANT_CONTEXT.to_owned()),
|
||||
Err(e) => Err(AssistantContextError::Store(e.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AssistantContextProvider for FsAssistantContextStore {
|
||||
async fn prepare_ticket_assistant_context(
|
||||
&self,
|
||||
project: &Project,
|
||||
issue: &Issue,
|
||||
) -> Result<PreparedContext, AssistantContextError> {
|
||||
let mut content = self.read_base_context().await?;
|
||||
content.push_str("\n\n## Bound Ticket\n\n");
|
||||
content.push_str(&format!("- Ref: {}\n", issue.reference()));
|
||||
content.push_str(&format!("- Title: {}\n", issue.title));
|
||||
content.push_str("\n### Description\n\n");
|
||||
content.push_str(issue.description.as_str());
|
||||
content.push_str("\n\n### Carnet\n\n");
|
||||
content.push_str(issue.carnet.as_str());
|
||||
content.push('\n');
|
||||
|
||||
Ok(PreparedContext {
|
||||
content: MarkdownDoc::new(content),
|
||||
relative_path: TICKET_ASSISTANT_FILE.to_owned(),
|
||||
project_root: project.root.as_str().to_owned(),
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user