From 6a5250ef02f106d84e538b0cb942a45eea42bc8f Mon Sep 17 00:00:00 2001 From: syui Date: Tue, 24 Mar 2026 14:19:17 +0900 Subject: [PATCH] feat(agent): inject identity, session history and diff content into agent context --- src/agent.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++----- src/tui.rs | 12 ++++++---- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index 4b2f318..4763b36 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -71,11 +71,21 @@ impl Agent { let (child, mut stdin, stdout) = claude::spawn_claude(Some(cwd))?; let pid = child.id(); - // Inject git context if available - let full_task = match git_context(cwd) { - Some(ctx) => format!("{task}\n\n[git context]\n{ctx}"), - None => task.to_string(), - }; + // Inject self-awareness + git context + let identity = format!( + "[system]\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\ + If you find issues or have follow-up work, you can propose: @agent-name task -c dir\n\ + Your feedback about aishell itself is also valuable.\n" + ); + let git_ctx = git_context(cwd) + .map(|ctx| format!("\n[git context]\n{ctx}")) + .unwrap_or_default(); + let history = recent_session_summary(); + let history_ctx = if history.is_empty() { String::new() } + else { format!("\n[session history]\n{history}") }; + let full_task = format!("{identity}\n[task]\n{task}{git_ctx}{history_ctx}"); claude::send_message(&mut stdin, &full_task); let (tx, rx) = mpsc::channel(); claude::spawn_reader(child, stdout, tx); @@ -271,6 +281,40 @@ impl Drop for Agent { } } +/// Load last 3 session summaries for agent context. +fn recent_session_summary() -> String { + let dir = if cfg!(target_os = "macos") { + let home = std::env::var("HOME").unwrap_or_default(); + format!("{home}/Library/Application Support/ai.syui.gpt/sessions") + } else { + let home = std::env::var("HOME").unwrap_or_default(); + format!("{home}/.local/share/aishell/sessions") + }; + + let mut files: Vec<_> = std::fs::read_dir(&dir) + .into_iter().flatten().flatten() + .filter(|e| { + let s = e.file_name().to_string_lossy().to_string(); + s.ends_with(".json") && s != "cmd_history.json" + }) + .collect(); + files.sort_by_key(|e| std::cmp::Reverse(e.file_name())); + + files.iter().take(3).filter_map(|f| { + let content = std::fs::read_to_string(f.path()).ok()?; + let d: serde_json::Value = serde_json::from_str(&content).ok()?; + let decision = d["decision"].as_str().unwrap_or(""); + let first_line = decision.lines() + .filter(|l| !l.starts_with('#') && !l.is_empty()) + .next() + .unwrap_or(""); + let agents: Vec<&str> = d["agents"].as_array() + .map(|a| a.iter().filter_map(|x| x["name"].as_str()).collect()) + .unwrap_or_default(); + Some(format!("- agents: {} | {}", agents.join(","), first_line)) + }).collect::>().join("\n") +} + pub fn git_context(cwd: &str) -> Option { use std::process::Command; let run = |args: &[&str]| -> String { @@ -290,13 +334,19 @@ pub fn git_context(cwd: &str) -> Option { let status = run(&["status", "--short"]); let log = run(&["log", "--oneline", "-5"]); let diff_stat = run(&["diff", "--stat", "HEAD"]); + // Actual diff content (truncated) + let diff_content = run(&["diff", "HEAD"]); + let diff_short: String = diff_content.lines().take(100).collect::>().join("\n"); let mut ctx = format!("branch: {branch}"); if !status.is_empty() { ctx.push_str(&format!("\nchanged:\n{status}")); } if !diff_stat.is_empty() { - ctx.push_str(&format!("\ndiff:\n{diff_stat}")); + ctx.push_str(&format!("\ndiff stat:\n{diff_stat}")); + } + if !diff_short.is_empty() { + ctx.push_str(&format!("\ndiff:\n{diff_short}")); } if !log.is_empty() { ctx.push_str(&format!("\nrecent:\n{log}")); diff --git a/src/tui.rs b/src/tui.rs index f2402ad..a063e20 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -54,10 +54,14 @@ pub struct App { should_quit: bool, } -/// Protocol prompt. Personality comes from aigpt MCP. This only teaches @agent syntax. -const AI_IDENTITY: &str = "エージェント起動コマンド(応答に書くと自動実行される): -@name task -c dir -例: @test cargo test -c ~/ai/shell +/// Protocol + self-awareness. Personality comes from aigpt MCP. +const AI_IDENTITY: &str = "\ +You are the main AI in aishell, a multi-agent development tool. +Your personality comes from aigpt MCP (core.md). +You oversee agents, integrate their results, and guide the user. +You can spawn agents with: @name task -c dir + +Report any usability issues you notice about aishell itself. 一言だけ挨拶してください。";