Merge pull request #5 from dhanji/micn/agent-tweaks

load AGENTS.md if there
This commit is contained in:
Dhanji R. Prasanna
2025-10-16 15:06:14 +11:00
committed by GitHub
3 changed files with 105 additions and 33 deletions

View File

@@ -275,8 +275,11 @@ pub async fn run() -> Result<()> {
std::env::current_dir()?
};
// Check if we're in a project directory and read README if available
// This should happen in both interactive and autonomous modes
// Check if we're in a project directory and read README and AGENTS.md if available
// Load AGENTS.md first (if present) to provide agent-specific instructions
let agents_content = read_agents_config(&workspace_dir);
// Then load README for project context
let readme_content = read_project_readme(&workspace_dir);
// Create project model
@@ -320,10 +323,21 @@ pub async fn run() -> Result<()> {
// Initialize agent
let ui_writer = ConsoleUiWriter::new();
// Combine AGENTS.md and README content if both exist
let combined_content = match (agents_content.clone(), readme_content.clone()) {
(Some(agents), Some(readme)) => {
Some(format!("{}\n\n{}", agents, readme))
}
(Some(agents), None) => Some(agents),
(None, Some(readme)) => Some(readme),
(None, None) => None,
};
let mut agent = if cli.autonomous {
Agent::new_autonomous_with_readme_and_quiet(config.clone(), ui_writer, readme_content.clone(), cli.quiet).await?
Agent::new_autonomous_with_readme_and_quiet(config.clone(), ui_writer, combined_content.clone(), cli.quiet).await?
} else {
Agent::new_with_readme_and_quiet(config.clone(), ui_writer, readme_content.clone(), cli.quiet).await?
Agent::new_with_readme_and_quiet(config.clone(), ui_writer, combined_content.clone(), cli.quiet).await?
};
// Execute task, autonomous mode, or start interactive mode
@@ -364,20 +378,61 @@ pub async fn run() -> Result<()> {
cli.show_prompt,
cli.show_code,
cli.theme,
readme_content,
combined_content,
)
.await?;
} else {
// Use standard terminal UI
let output = SimpleOutput::new();
output.print(&format!("📁 Workspace: {}", project.workspace().display()));
run_interactive(agent, cli.show_prompt, cli.show_code, readme_content).await?;
run_interactive(agent, cli.show_prompt, cli.show_code, combined_content).await?;
}
}
Ok(())
}
/// Check if we're in a project directory and read AGENTS.md if available
fn read_agents_config(workspace_dir: &Path) -> Option<String> {
// Look for AGENTS.md in the current directory
let agents_path = workspace_dir.join("AGENTS.md");
if agents_path.exists() {
match std::fs::read_to_string(&agents_path) {
Ok(content) => {
// Return the content with a note about which file was read
info!("Loaded AGENTS.md from {}", agents_path.display());
Some(format!(
"🤖 Agent Configuration (from AGENTS.md):\n\n{}",
content
))
}
Err(e) => {
// Log the error but continue without the agents config
error!("Failed to read AGENTS.md: {}", e);
None
}
}
} else {
// Check for alternative names
let alt_path = workspace_dir.join("agents.md");
if alt_path.exists() {
match std::fs::read_to_string(&alt_path) {
Ok(content) => {
info!("Loaded agents.md from {}", alt_path.display());
Some(format!("🤖 Agent Configuration (from agents.md):\n\n{}", content))
}
Err(e) => {
error!("Failed to read agents.md: {}", e);
None
}
}
} else {
None
}
}
}
/// Check if we're in a project directory and read README if available
fn read_project_readme(workspace_dir: &Path) -> Option<String> {
// Check if we're in a project directory (contains .g3 or .git)
@@ -478,7 +533,7 @@ async fn run_interactive_retro(
show_prompt: bool,
show_code: bool,
theme_name: Option<String>,
readme_content: Option<String>,
combined_content: Option<String>,
) -> Result<()> {
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
use std::time::Duration;
@@ -500,24 +555,31 @@ async fn run_interactive_retro(
// Create agent with RetroTuiWriter
let ui_writer = RetroTuiWriter::new(tui.clone());
let mut agent = Agent::new_with_readme_and_quiet(config, ui_writer, readme_content.clone(), false).await?;
let mut agent = Agent::new_with_readme_and_quiet(config, ui_writer, combined_content.clone(), false).await?;
// Display initial system messages
tui.output("SYSTEM: AGENT ONLINE\n\n");
// Display message if README was loaded
if readme_content.is_some() {
// Extract the first heading or title from the README
let readme_snippet = if let Some(ref content) = readme_content {
extract_readme_heading(content)
.unwrap_or_else(|| "PROJECT DOCUMENTATION LOADED".to_string())
} else {
"PROJECT DOCUMENTATION LOADED".to_string()
};
tui.output(&format!(
"SYSTEM: PROJECT README LOADED - {}\n\n",
readme_snippet
));
// Display message if AGENTS.md or README was loaded
if let Some(ref content) = combined_content {
// Check what was loaded
let has_agents = content.contains("Agent Configuration");
let has_readme = content.contains("Project README");
if has_agents {
tui.output("SYSTEM: AGENT CONFIGURATION LOADED\n\n");
}
if has_readme {
// Extract the first heading or title from the README
let readme_snippet = extract_readme_heading(content)
.unwrap_or_else(|| "PROJECT DOCUMENTATION LOADED".to_string());
tui.output(&format!(
"SYSTEM: PROJECT README LOADED - {}\n\n",
readme_snippet
));
}
}
tui.output("SYSTEM: READY FOR INPUT\n\n");
tui.output("\n\n");
@@ -739,7 +801,7 @@ async fn run_interactive<W: UiWriter>(
mut agent: Agent<W>,
show_prompt: bool,
show_code: bool,
readme_content: Option<String>,
combined_content: Option<String>,
) -> Result<()> {
let output = SimpleOutput::new();
@@ -758,17 +820,23 @@ async fn run_interactive<W: UiWriter>(
}
}
// Display message if README was loaded
if readme_content.is_some() {
// Extract the first heading or title from the README
let readme_snippet = if let Some(ref content) = readme_content {
extract_readme_heading(content)
.unwrap_or_else(|| "Project documentation loaded".to_string())
} else {
"Project documentation loaded".to_string()
};
// Display message if AGENTS.md or README was loaded
if let Some(ref content) = combined_content {
// Check what was loaded
let has_agents = content.contains("Agent Configuration");
let has_readme = content.contains("Project README");
if has_agents {
output.print("🤖 AGENTS.md configuration loaded");
}
if has_readme {
// Extract the first heading or title from the README
let readme_snippet = extract_readme_heading(content)
.unwrap_or_else(|| "Project documentation loaded".to_string());
output.print(&format!("📚 detected: {}", readme_snippet));
output.print(&format!("📚 detected: {}", readme_snippet));
}
}
output.print("");

View File

@@ -250,7 +250,7 @@ impl TerminalState {
}
/// Parse markdown and convert to styled lines
fn parse_markdown_line(&self, line: &str) -> Line {
fn parse_markdown_line(&self, line: &str) -> Line<'_> {
// Skip parsing for special status lines to preserve their formatting
if line.starts_with("[SUCCESS]") ||
line.starts_with("[FAILED]") ||

View File

@@ -17,6 +17,7 @@ mod tests {
"test prompt".to_string(),
None,
100,
false, // quiet parameter
);
let result = retry_with_backoff(
@@ -55,6 +56,7 @@ mod tests {
"test prompt".to_string(),
None,
100,
false, // quiet parameter
);
let result: Result<&str, _> = retry_with_backoff(
@@ -87,6 +89,7 @@ mod tests {
"test prompt".to_string(),
None,
100,
false, // quiet parameter
);
let result: Result<&str, _> = retry_with_backoff(
@@ -118,6 +121,7 @@ mod tests {
long_prompt,
None,
100,
false, // quiet parameter
);
// The prompt should be truncated to 1000 chars