Merge feature/agent-skills-support: Agent Skills specification support
This commit is contained in:
@@ -6,7 +6,7 @@ use tracing::debug;
|
||||
use g3_core::ui_writer::UiWriter;
|
||||
use g3_core::Agent;
|
||||
|
||||
use crate::project_files::{combine_project_content, read_agents_config, read_include_prompt, read_workspace_memory};
|
||||
use crate::project_files::{combine_project_content, discover_and_format_skills, read_agents_config, read_include_prompt, read_workspace_memory};
|
||||
use crate::display::{LoadedContent, print_loaded_status, print_workspace_path};
|
||||
use crate::language_prompts::{get_language_prompts_for_workspace, get_agent_language_prompts_for_workspace_with_langs};
|
||||
use crate::simple_output::SimpleOutput;
|
||||
@@ -173,12 +173,16 @@ pub async fn run_agent_mode(
|
||||
system_prompt
|
||||
};
|
||||
|
||||
// Discover skills from configured paths
|
||||
let (_skills, skills_content) = discover_and_format_skills(&workspace_dir, &config.skills);
|
||||
|
||||
// Combine all content for the agent's context
|
||||
let combined_content = combine_project_content(
|
||||
agents_content_opt,
|
||||
memory_content_opt,
|
||||
language_content,
|
||||
include_prompt,
|
||||
skills_content,
|
||||
&workspace_dir,
|
||||
);
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ use accumulative::run_accumulative_mode;
|
||||
use agent_mode::run_agent_mode;
|
||||
use autonomous::run_autonomous;
|
||||
use interactive::run_interactive;
|
||||
use project_files::{combine_project_content, read_agents_config, read_include_prompt, read_workspace_memory};
|
||||
use project_files::{combine_project_content, discover_and_format_skills, read_agents_config, read_include_prompt, read_workspace_memory};
|
||||
use simple_output::SimpleOutput;
|
||||
use ui_writer_impl::ConsoleUiWriter;
|
||||
use g3_core::ui_writer::UiWriter;
|
||||
@@ -117,8 +117,13 @@ pub async fn run() -> Result<()> {
|
||||
// Load configuration with CLI overrides
|
||||
let config = load_config_with_cli_overrides(&cli)?;
|
||||
|
||||
// Discover skills from configured paths
|
||||
let (_skills, skills_content) = discover_and_format_skills(&workspace_dir, &config.skills);
|
||||
|
||||
// Combine AGENTS.md and memory content
|
||||
let combined_content = combine_project_content(agents_content, memory_content, language_content, include_prompt, &workspace_dir);
|
||||
let combined_content = combine_project_content(
|
||||
agents_content, memory_content, language_content, include_prompt, skills_content, &workspace_dir
|
||||
);
|
||||
|
||||
run_console_mode(cli, config, project, combined_content, workspace_dir).await
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
//! Reads AGENTS.md and workspace memory files from the workspace.
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tracing::error;
|
||||
|
||||
use crate::template::process_template;
|
||||
use g3_core::{discover_skills, generate_skills_prompt, Skill};
|
||||
use g3_config::SkillsConfig;
|
||||
|
||||
/// Read AGENTS.md configuration from the workspace directory.
|
||||
/// Returns formatted content with emoji prefix, or None if not found.
|
||||
@@ -86,15 +89,16 @@ pub fn combine_project_content(
|
||||
memory_content: Option<String>,
|
||||
language_content: Option<String>,
|
||||
include_prompt: Option<String>,
|
||||
skills_content: Option<String>,
|
||||
workspace_dir: &Path,
|
||||
) -> Option<String> {
|
||||
// Always include working directory to prevent LLM from hallucinating paths
|
||||
let cwd_info = format!("📂 Working Directory: {}", workspace_dir.display());
|
||||
|
||||
// Order: cwd → agents → language → include_prompt → memory
|
||||
// Order: cwd → agents → language → include_prompt → skills → memory
|
||||
// Include prompt comes BEFORE memory so memory is always last (most recent context)
|
||||
let parts: Vec<String> = [
|
||||
Some(cwd_info), agents_content, language_content, include_prompt, memory_content
|
||||
Some(cwd_info), agents_content, language_content, include_prompt, skills_content, memory_content
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
@@ -171,6 +175,38 @@ fn truncate_for_display(s: &str, max_len: usize) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Discover skills from configured paths and generate the skills prompt.
|
||||
///
|
||||
/// Returns the skills prompt section if any skills are found, None otherwise.
|
||||
/// Skills are discovered from:
|
||||
/// 1. Global: ~/.g3/skills/
|
||||
/// 2. Extra paths from config
|
||||
/// 3. Workspace: .g3/skills/ (highest priority)
|
||||
pub fn discover_and_format_skills(
|
||||
workspace_dir: &Path,
|
||||
skills_config: &SkillsConfig,
|
||||
) -> (Vec<Skill>, Option<String>) {
|
||||
if !skills_config.enabled {
|
||||
return (Vec::new(), None);
|
||||
}
|
||||
|
||||
// Convert extra_paths from config to PathBuf
|
||||
let extra_paths: Vec<PathBuf> = skills_config
|
||||
.extra_paths
|
||||
.iter()
|
||||
.map(|p| PathBuf::from(p))
|
||||
.collect();
|
||||
|
||||
let skills = discover_skills(Some(workspace_dir), &extra_paths);
|
||||
|
||||
if skills.is_empty() {
|
||||
return (Vec::new(), None);
|
||||
}
|
||||
|
||||
let prompt = generate_skills_prompt(&skills);
|
||||
(skills, Some(prompt))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -219,6 +255,7 @@ mod tests {
|
||||
Some("memory".to_string()),
|
||||
Some("language".to_string()),
|
||||
None, // include_prompt
|
||||
None, // skills_content
|
||||
&workspace,
|
||||
);
|
||||
assert!(result.is_some());
|
||||
@@ -232,7 +269,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_combine_project_content_partial() {
|
||||
let workspace = std::path::PathBuf::from("/test/workspace");
|
||||
let result = combine_project_content(None, Some("memory".to_string()), None, None, &workspace);
|
||||
let result = combine_project_content(None, Some("memory".to_string()), None, None, None, &workspace);
|
||||
assert!(result.is_some());
|
||||
let content = result.unwrap();
|
||||
assert!(content.contains("📂 Working Directory: /test/workspace"));
|
||||
@@ -242,7 +279,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_combine_project_content_all_none() {
|
||||
let workspace = std::path::PathBuf::from("/test/workspace");
|
||||
let result = combine_project_content(None, None, None, None, &workspace);
|
||||
let result = combine_project_content(None, None, None, None, None, &workspace);
|
||||
// Now always returns Some because we always include the working directory
|
||||
assert!(result.is_some());
|
||||
assert!(result.unwrap().contains("📂 Working Directory: /test/workspace"));
|
||||
@@ -256,6 +293,7 @@ mod tests {
|
||||
Some("memory".to_string()),
|
||||
Some("language".to_string()),
|
||||
Some("include_prompt".to_string()),
|
||||
None, // skills_content
|
||||
&workspace,
|
||||
);
|
||||
assert!(result.is_some());
|
||||
@@ -272,6 +310,7 @@ mod tests {
|
||||
Some("MEMORY_CONTENT".to_string()),
|
||||
Some("LANGUAGE_CONTENT".to_string()),
|
||||
Some("INCLUDE_PROMPT_CONTENT".to_string()),
|
||||
None, // skills_content
|
||||
&workspace,
|
||||
);
|
||||
let content = result.unwrap();
|
||||
@@ -297,6 +336,7 @@ mod tests {
|
||||
Some("MEMORY".to_string()),
|
||||
Some("LANGUAGE".to_string()),
|
||||
None, // no include_prompt
|
||||
None, // skills_content
|
||||
&workspace,
|
||||
);
|
||||
let content = result.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user