feat(agent): inject user/bot identity and recent chat context into agent and TUI sessions
This commit is contained in:
26
src/agent.rs
26
src/agent.rs
@@ -73,13 +73,13 @@ impl Agent {
|
|||||||
let (child, mut stdin, stdout) = claude::spawn_claude(Some(cwd))?;
|
let (child, mut stdin, stdout) = claude::spawn_claude(Some(cwd))?;
|
||||||
let pid = child.id();
|
let pid = child.id();
|
||||||
|
|
||||||
// Inject self-awareness + git context
|
// Inject self-awareness + user/bot context
|
||||||
|
let user_ctx = load_user_context();
|
||||||
let identity = format!(
|
let identity = format!(
|
||||||
"[system]\n\
|
"[system]\n\
|
||||||
You are agent '{name}' running inside aishell.\n\
|
You are agent '{name}' running inside aishell.\n\
|
||||||
aishell is a multi-agent development tool. You are one of potentially several agents working in parallel.\n\
|
{user_ctx}\
|
||||||
If you find issues or have follow-up work, you can propose: @agent-name task -c dir\n\
|
If you find issues or have follow-up work, propose: @agent-name task -c dir\n"
|
||||||
Your feedback about aishell itself is also valuable.\n"
|
|
||||||
);
|
);
|
||||||
let git_ctx = git_context(cwd)
|
let git_ctx = git_context(cwd)
|
||||||
.map(|ctx| format!("\n[git context]\n{ctx}"))
|
.map(|ctx| format!("\n[git context]\n{ctx}"))
|
||||||
@@ -290,6 +290,24 @@ impl Drop for Agent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load user/bot identity from shared config.
|
||||||
|
fn load_user_context() -> String {
|
||||||
|
let home = std::env::var("HOME").unwrap_or_default();
|
||||||
|
let path = if cfg!(target_os = "macos") {
|
||||||
|
format!("{home}/Library/Application Support/ai.syui.log/config.json")
|
||||||
|
} else {
|
||||||
|
format!("{home}/.config/ai.syui.log/config.json")
|
||||||
|
};
|
||||||
|
let config: serde_json::Value = std::fs::read_to_string(&path).ok()
|
||||||
|
.and_then(|s| serde_json::from_str(&s).ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let user = config["handle"].as_str().unwrap_or("");
|
||||||
|
let bot = config["bot"]["handle"].as_str().unwrap_or("");
|
||||||
|
if user.is_empty() { return String::new(); }
|
||||||
|
format!("user: {user}, ai: {bot}\n")
|
||||||
|
}
|
||||||
|
|
||||||
/// Load last 3 session summaries for agent context.
|
/// Load last 3 session summaries for agent context.
|
||||||
fn recent_session_summary() -> String {
|
fn recent_session_summary() -> String {
|
||||||
let dir = if cfg!(target_os = "macos") {
|
let dir = if cfg!(target_os = "macos") {
|
||||||
|
|||||||
75
src/tui.rs
75
src/tui.rs
@@ -92,13 +92,14 @@ impl App {
|
|||||||
should_quit: false,
|
should_quit: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send protocol + project context
|
// Send protocol + identity + project context
|
||||||
if let Some(ref mut claude) = app.claude {
|
if let Some(ref mut claude) = app.claude {
|
||||||
let cwd = std::env::current_dir()
|
let cwd = std::env::current_dir()
|
||||||
.map(|p| p.display().to_string())
|
.map(|p| p.display().to_string())
|
||||||
.unwrap_or_else(|_| ".".to_string());
|
.unwrap_or_else(|_| ".".to_string());
|
||||||
let ctx = crate::agent::git_context(&cwd).unwrap_or_default();
|
let git_ctx = crate::agent::git_context(&cwd).unwrap_or_default();
|
||||||
let msg = format!("{AI_IDENTITY}\n\n[project context]\n{ctx}");
|
let identity_ctx = load_identity_context();
|
||||||
|
let msg = format!("{AI_IDENTITY}\n\n{identity_ctx}[project]\n{git_ctx}");
|
||||||
claude.send(&msg);
|
claude.send(&msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,6 +662,74 @@ fn parse_agent_commands(text: &str) -> Vec<(String, String, String)> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load identity context from atproto config + recent chat.
|
||||||
|
fn load_identity_context() -> String {
|
||||||
|
let home = std::env::var("HOME").unwrap_or_default();
|
||||||
|
let config_path = if cfg!(target_os = "macos") {
|
||||||
|
format!("{home}/Library/Application Support/ai.syui.log/config.json")
|
||||||
|
} else {
|
||||||
|
format!("{home}/.config/ai.syui.log/config.json")
|
||||||
|
};
|
||||||
|
|
||||||
|
let config: serde_json::Value = match std::fs::read_to_string(&config_path) {
|
||||||
|
Ok(s) => serde_json::from_str(&s).unwrap_or_default(),
|
||||||
|
Err(_) => return String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ctx = String::new();
|
||||||
|
|
||||||
|
// User & bot identity
|
||||||
|
let user_handle = config["handle"].as_str().unwrap_or("?");
|
||||||
|
let bot_handle = config["bot"]["handle"].as_str().unwrap_or("?");
|
||||||
|
let bot_did = config["bot"]["did"].as_str().unwrap_or("?");
|
||||||
|
let network = config["network"].as_str().unwrap_or("?");
|
||||||
|
|
||||||
|
ctx.push_str(&format!(
|
||||||
|
"[identity]\nuser: {user_handle}\nyou: {bot_handle} ({bot_did})\nnetwork: {network}\n\n"
|
||||||
|
));
|
||||||
|
|
||||||
|
// Recent chat (last 2 entries for context)
|
||||||
|
let bot_path = config["bot"]["path"].as_str().unwrap_or("");
|
||||||
|
let expanded = expand_tilde(bot_path);
|
||||||
|
let chat_dir = format!(
|
||||||
|
"{}/{}/ai.syui.log.chat",
|
||||||
|
if expanded.is_empty() { format!("{home}/.config/ai.syui.log/at") } else { expanded.clone() },
|
||||||
|
config["bot"]["did"].as_str().unwrap_or("did")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Also check user's chat dir
|
||||||
|
let user_did = config["did"].as_str().unwrap_or("");
|
||||||
|
let user_chat_dir = format!("{}/{}/ai.syui.log.chat",
|
||||||
|
if expanded.is_empty() { format!("{home}/.config/ai.syui.log/at") } else { expanded },
|
||||||
|
user_did
|
||||||
|
);
|
||||||
|
|
||||||
|
for dir in [&chat_dir, &user_chat_dir] {
|
||||||
|
if let Ok(mut entries) = std::fs::read_dir(dir) {
|
||||||
|
let mut files: Vec<_> = entries
|
||||||
|
.flatten()
|
||||||
|
.filter(|e| e.path().extension().is_some_and(|x| x == "json"))
|
||||||
|
.collect();
|
||||||
|
files.sort_by_key(|e| std::cmp::Reverse(e.file_name()));
|
||||||
|
|
||||||
|
for f in files.iter().take(1) {
|
||||||
|
if let Ok(content) = std::fs::read_to_string(f.path()) {
|
||||||
|
if let Ok(record) = serde_json::from_str::<serde_json::Value>(&content) {
|
||||||
|
let text = record["value"]["content"]["text"].as_str().unwrap_or("");
|
||||||
|
if !text.is_empty() {
|
||||||
|
let short: String = text.chars().take(200).collect();
|
||||||
|
ctx.push_str(&format!("[recent chat]\n{short}\n\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break; // Use first available dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx
|
||||||
|
}
|
||||||
|
|
||||||
fn expand_tilde(path: &str) -> String {
|
fn expand_tilde(path: &str) -> String {
|
||||||
if let Some(rest) = path.strip_prefix('~') {
|
if let Some(rest) = path.strip_prefix('~') {
|
||||||
if let Ok(home) = std::env::var("HOME") {
|
if let Ok(home) = std::env::var("HOME") {
|
||||||
|
|||||||
Reference in New Issue
Block a user