feat: Externalize research tool as embedded skill

Replaces the built-in research/research_status tools with a portable
skill-based approach:

- Add embedded skills infrastructure (skills compiled into binary)
- Add repo-local skills/ directory support (highest priority)
- Create research skill with SKILL.md and g3-research shell script
- Script extraction to .g3/bin/ with version tracking
- Filesystem-based handoff via .g3/research/<id>/status.json
- Remove PendingResearchManager and all research tool code
- Update system prompt to reference skill instead of tool

Benefits:
- No special tool infrastructure needed (just shell + read_file)
- Context-efficient (reports stay on disk until needed)
- Crash-resilient (state persisted to filesystem)
- Portable (skill can be overridden per-workspace)

Breaking change: research tool calls now return a deprecation message
pointing to the research skill.
This commit is contained in:
Dhanji R. Prasanna
2026-02-05 13:23:26 +11:00
parent bf9e3dc878
commit 39e586982c
19 changed files with 949 additions and 1638 deletions

View File

@@ -7,7 +7,7 @@ use anyhow::Result;
use tracing::{debug, warn};
use crate::tools::executor::ToolContext;
use crate::tools::{acd, file_ops, memory, misc, plan, research, shell, webdriver};
use crate::tools::{acd, file_ops, memory, misc, plan, shell, webdriver};
use crate::ui_writer::UiWriter;
use crate::ToolCall;
@@ -40,10 +40,11 @@ pub async fn dispatch_tool<W: UiWriter>(
// Miscellaneous tools
"code_search" => misc::execute_code_search(tool_call, ctx).await,
// Research tool
"research" => research::execute_research(tool_call, ctx).await,
"research_status" => research::execute_research_status(tool_call, ctx).await,
// Research tool (deprecated - now a skill)
"research" | "research_status" => {
Ok("⚠️ The `research` tool has been replaced by the **research skill**. Use `background_process` to run `.g3/bin/g3-research` instead. See the research skill documentation for details.".to_string())
}
// Workspace memory tools
"remember" => memory::execute_remember(tool_call, ctx).await,