everruns_integrations_github/
lib.rs1mod client;
13mod tools;
14
15use everruns_core::capabilities::{
16 AgentBlueprint, BlueprintModel, Capability, CapabilityStatus, IntegrationPlugin,
17};
18use everruns_core::tools::Tool;
19use serde_json::json;
20
21use tools::{ReadGitHubFileTool, SearchGitHubCodeTool, SearchGitHubIssuesTool};
22
23inventory::submit! {
24 IntegrationPlugin {
25 experimental_only: false,
26 feature_flag: None,
27 factory: || Box::new(GitHubScoutCapability),
28 }
29}
30
31pub const GITHUB_API_BASE: &str = "https://api.github.com";
32pub const GITHUB_CONNECTION_PROVIDER: &str = "github";
33pub const GITHUB_TOKEN_SECRET: &str = "GITHUB_TOKEN";
34
35pub struct GitHubScoutCapability;
36
37impl Capability for GitHubScoutCapability {
38 fn id(&self) -> &str {
39 "github_scout"
40 }
41
42 fn name(&self) -> &str {
43 "GitHub Scout"
44 }
45
46 fn description(&self) -> &str {
47 "Blueprint-only GitHub repository scout that can spawn read-only GitHub exploration subagents."
48 }
49
50 fn status(&self) -> CapabilityStatus {
51 CapabilityStatus::Available
52 }
53
54 fn icon(&self) -> Option<&str> {
55 Some("github")
56 }
57
58 fn category(&self) -> Option<&str> {
59 Some("Integrations")
60 }
61
62 fn tools(&self) -> Vec<Box<dyn Tool>> {
63 vec![]
64 }
65
66 fn dependencies(&self) -> Vec<&'static str> {
67 vec!["subagents"]
68 }
69
70 fn agent_blueprints(&self) -> Vec<AgentBlueprint> {
71 vec![AgentBlueprint {
72 id: "github_scout",
73 name: "GitHub Scout",
74 description: "Search GitHub repositories for code, files, issues, and pull requests. Fast read-only agent for codebase exploration and pattern discovery.",
75 model: BlueprintModel::Fixed("claude-haiku-4-5-20251001".to_string()),
76 system_prompt: GITHUB_SCOUT_PROMPT,
77 tools: vec![
78 Box::new(SearchGitHubCodeTool),
79 Box::new(ReadGitHubFileTool),
80 Box::new(SearchGitHubIssuesTool),
81 ],
82 max_turns: Some(15),
83 config_schema: Some(json!({
84 "type": "object",
85 "properties": {
86 "repos": {
87 "type": "array",
88 "items": {
89 "type": "string",
90 "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
91 },
92 "description": "Repository list to scope searches, in owner/repo format."
93 }
94 },
95 "additionalProperties": false
96 })),
97 }]
98 }
99}
100
101const GITHUB_SCOUT_PROMPT: &str = r#"You are GitHub Scout, a read-only repository exploration agent.
102
103Use your GitHub tools to find concrete code, files, issues, and pull requests relevant to the task. Prefer targeted searches over broad scans. When the host provides config.repos, scope searches to those repositories unless the task clearly asks otherwise.
104
105Return a concise summary with:
106- the answer or finding,
107- the most relevant file paths, symbols, issues, or pull requests,
108- direct URLs when useful,
109- any uncertainty or gaps."#;
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn capability_is_blueprint_only() {
117 let cap = GitHubScoutCapability;
118 assert_eq!(cap.id(), "github_scout");
119 assert_eq!(cap.name(), "GitHub Scout");
120 assert!(cap.tools().is_empty());
121 assert_eq!(cap.dependencies(), vec!["subagents"]);
122 }
123
124 #[test]
125 fn contributes_github_scout_blueprint() {
126 let cap = GitHubScoutCapability;
127 let blueprints = cap.agent_blueprints();
128 assert_eq!(blueprints.len(), 1);
129
130 let scout = &blueprints[0];
131 assert_eq!(scout.id, "github_scout");
132 assert_eq!(scout.name, "GitHub Scout");
133 assert_eq!(scout.max_turns, Some(15));
134 assert!(matches!(
135 scout.model,
136 BlueprintModel::Fixed(ref model) if model == "claude-haiku-4-5-20251001"
137 ));
138
139 let tool_names: Vec<&str> = scout.tools.iter().map(|tool| tool.name()).collect();
140 assert_eq!(
141 tool_names,
142 vec![
143 "search_github_code",
144 "read_github_file",
145 "search_github_issues"
146 ]
147 );
148 }
149}