feat(agent): inject identity, session history and diff content into agent context
This commit is contained in:
62
src/agent.rs
62
src/agent.rs
@@ -71,11 +71,21 @@ 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 git context if available
|
// Inject self-awareness + git context
|
||||||
let full_task = match git_context(cwd) {
|
let identity = format!(
|
||||||
Some(ctx) => format!("{task}\n\n[git context]\n{ctx}"),
|
"[system]\n\
|
||||||
None => task.to_string(),
|
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);
|
claude::send_message(&mut stdin, &full_task);
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
claude::spawn_reader(child, stdout, tx);
|
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::<Vec<_>>().join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn git_context(cwd: &str) -> Option<String> {
|
pub fn git_context(cwd: &str) -> Option<String> {
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
let run = |args: &[&str]| -> String {
|
let run = |args: &[&str]| -> String {
|
||||||
@@ -290,13 +334,19 @@ pub fn git_context(cwd: &str) -> Option<String> {
|
|||||||
let status = run(&["status", "--short"]);
|
let status = run(&["status", "--short"]);
|
||||||
let log = run(&["log", "--oneline", "-5"]);
|
let log = run(&["log", "--oneline", "-5"]);
|
||||||
let diff_stat = run(&["diff", "--stat", "HEAD"]);
|
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::<Vec<_>>().join("\n");
|
||||||
|
|
||||||
let mut ctx = format!("branch: {branch}");
|
let mut ctx = format!("branch: {branch}");
|
||||||
if !status.is_empty() {
|
if !status.is_empty() {
|
||||||
ctx.push_str(&format!("\nchanged:\n{status}"));
|
ctx.push_str(&format!("\nchanged:\n{status}"));
|
||||||
}
|
}
|
||||||
if !diff_stat.is_empty() {
|
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() {
|
if !log.is_empty() {
|
||||||
ctx.push_str(&format!("\nrecent:\n{log}"));
|
ctx.push_str(&format!("\nrecent:\n{log}"));
|
||||||
|
|||||||
12
src/tui.rs
12
src/tui.rs
@@ -54,10 +54,14 @@ pub struct App {
|
|||||||
should_quit: bool,
|
should_quit: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Protocol prompt. Personality comes from aigpt MCP. This only teaches @agent syntax.
|
/// Protocol + self-awareness. Personality comes from aigpt MCP.
|
||||||
const AI_IDENTITY: &str = "エージェント起動コマンド(応答に書くと自動実行される):
|
const AI_IDENTITY: &str = "\
|
||||||
@name task -c dir
|
You are the main AI in aishell, a multi-agent development tool.
|
||||||
例: @test cargo test -c ~/ai/shell
|
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.
|
||||||
|
|
||||||
一言だけ挨拶してください。";
|
一言だけ挨拶してください。";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user