WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 391303c

Browse files
gouravjshahclaude
andcommitted
feat: Add library:// URI syntax and --prompt alias for run command
- Add library:// URI syntax for running agents from built-in library Format: library://domain/agent-name Example: aofctl run agent library://kubernetes/pod-doctor --prompt "debug CrashLoopBackOff" - Add --prompt as an alias for --input (more intuitive for LLM interactions) - Provide helpful error messages when agent not found, showing available agents - Updated CLI reference documentation with examples 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 33238e1 commit 391303c

File tree

4 files changed

+133
-13
lines changed

4 files changed

+133
-13
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- Displays domain (category), agent name, status, and model
2020
- Supports filtering by agent name: `aofctl get agents pod-doctor --library`
2121
- Supports JSON/YAML output formats
22+
- `library://` URI syntax for running agents from the built-in library
23+
- Format: `library://domain/agent-name`
24+
- Example: `aofctl run agent library://kubernetes/pod-doctor --prompt "debug CrashLoopBackOff"`
25+
- Helpful error messages showing available agents when agent not found
26+
- `--prompt` as an alias for `--input` in the run command
27+
- More intuitive for LLM-style interactions
2228

2329
### Fixed
2430
- Script node YAML field naming (`scriptConfig` camelCase)

crates/aofctl/src/cli.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ pub enum Commands {
3434
resource_type: String,
3535

3636
/// Resource name or configuration file
37+
/// Supports library:// URIs: library://kubernetes/pod-doctor
3738
name_or_config: String,
3839

39-
/// Input/query for the agent
40-
#[arg(short, long)]
40+
/// Input/query for the agent (alias: --prompt)
41+
#[arg(short, long, visible_alias = "prompt")]
4142
input: Option<String>,
4243

4344
/// Output format (json, yaml, text)

crates/aofctl/src/commands/run.rs

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,90 @@ pub async fn execute(
233233
}
234234
}
235235

236+
/// Resolve a library:// URI to the actual file path
237+
/// Format: library://domain/agent-name (e.g., library://kubernetes/pod-doctor)
238+
fn resolve_library_uri(uri: &str) -> Result<std::path::PathBuf> {
239+
// Parse library://domain/agent-name
240+
let path = uri.strip_prefix("library://")
241+
.ok_or_else(|| anyhow!("Invalid library URI: {}", uri))?;
242+
243+
let parts: Vec<&str> = path.split('/').collect();
244+
if parts.len() != 2 {
245+
return Err(anyhow!(
246+
"Invalid library URI format: {}\nExpected: library://domain/agent-name\nExample: library://kubernetes/pod-doctor",
247+
uri
248+
));
249+
}
250+
251+
let domain = parts[0];
252+
let agent_name = parts[1];
253+
254+
// Find the library directory
255+
let library_path = find_library_path()?;
256+
257+
// Try both .yaml and .yml extensions
258+
for ext in &["yaml", "yml"] {
259+
let agent_path = library_path.join(domain).join(format!("{}.{}", agent_name, ext));
260+
if agent_path.exists() {
261+
return Ok(agent_path);
262+
}
263+
}
264+
265+
// Agent not found - provide helpful error
266+
let available = list_library_agents(&library_path, domain);
267+
Err(anyhow!(
268+
"Agent '{}' not found in library domain '{}'\n\nAvailable agents in '{}':\n {}\n\nRun 'aofctl get agents --library' to see all available agents.",
269+
agent_name, domain, domain,
270+
available.join(", ")
271+
))
272+
}
273+
274+
/// Find the library directory
275+
fn find_library_path() -> Result<std::path::PathBuf> {
276+
let candidates = [
277+
std::path::PathBuf::from("library"),
278+
std::path::PathBuf::from("./library"),
279+
std::env::current_exe()
280+
.ok()
281+
.and_then(|p| p.parent().map(|p| p.join("library")))
282+
.unwrap_or_default(),
283+
std::env::current_exe()
284+
.ok()
285+
.and_then(|p| p.parent().and_then(|p| p.parent()).map(|p| p.join("library")))
286+
.unwrap_or_default(),
287+
];
288+
289+
for candidate in candidates {
290+
if candidate.exists() && candidate.is_dir() {
291+
return Ok(candidate);
292+
}
293+
}
294+
295+
Err(anyhow!(
296+
"Library directory not found. Make sure you're running from the project root or the library is installed."
297+
))
298+
}
299+
300+
/// List available agents in a library domain
301+
fn list_library_agents(library_path: &std::path::Path, domain: &str) -> Vec<String> {
302+
let domain_path = library_path.join(domain);
303+
let mut agents = Vec::new();
304+
305+
if let Ok(entries) = std::fs::read_dir(&domain_path) {
306+
for entry in entries.flatten() {
307+
let path = entry.path();
308+
if path.extension().map_or(false, |e| e == "yaml" || e == "yml") {
309+
if let Some(stem) = path.file_stem() {
310+
agents.push(stem.to_string_lossy().to_string());
311+
}
312+
}
313+
}
314+
}
315+
316+
agents.sort();
317+
agents
318+
}
319+
236320
/// Run an agent with configuration
237321
async fn run_agent(
238322
config: &str,
@@ -241,15 +325,23 @@ async fn run_agent(
241325
schema: Option<OutputSchema>,
242326
context: Option<&AofContext>,
243327
) -> Result<()> {
328+
// Resolve library:// URIs to actual file paths
329+
let config_path = if config.starts_with("library://") {
330+
resolve_library_uri(config)?
331+
} else {
332+
std::path::PathBuf::from(config)
333+
};
334+
let config_str = config_path.to_string_lossy();
335+
244336
// Check if interactive mode should be enabled (when no input provided and stdin is a TTY)
245337
let interactive = input.is_none() && io::stdin().is_terminal();
246338

247339
if interactive {
248340
// Load agent configuration
249-
let config_content = fs::read_to_string(config)
250-
.with_context(|| format!("Failed to read config file: {}", config))?;
341+
let config_content = fs::read_to_string(&config_path)
342+
.with_context(|| format!("Failed to read config file: {}", config_str))?;
251343

252-
let agent_config = parse_agent_config(&config_content, config)?;
344+
let agent_config = parse_agent_config(&config_content, &config_str)?;
253345

254346
let agent_name = agent_config.name.clone();
255347

@@ -266,18 +358,18 @@ async fn run_agent(
266358
}
267359

268360
// Non-interactive mode: normal logging to console
269-
info!("Loading agent config from: {}", config);
361+
info!("Loading agent config from: {}", config_str);
270362
if let Some(ctx) = context {
271363
info!("Context: {} (approval required: {})",
272364
ctx.name(),
273365
ctx.spec.approval.as_ref().map(|a| a.required).unwrap_or(false)
274366
);
275367
}
276368

277-
let config_content = fs::read_to_string(config)
278-
.with_context(|| format!("Failed to read config file: {}", config))?;
369+
let config_content = fs::read_to_string(&config_path)
370+
.with_context(|| format!("Failed to read config file: {}", config_str))?;
279371

280-
let agent_config = parse_agent_config(&config_content, config)?;
372+
let agent_config = parse_agent_config(&config_content, &config_str)?;
281373

282374
let agent_name = agent_config.name.clone();
283375
info!("Agent loaded: {}", agent_name);

docs/reference/aofctl.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,21 +185,42 @@ aofctl run <resource_type> <name_or_file> [flags]
185185
```
186186

187187
**Flags:**
188-
- `-i, --input string` - Input/query for the agent
188+
- `-i, --input string` - Input/query for the agent (alias: `--prompt`)
189189
- `-o, --output string` - Output format: json|yaml|text (default: text)
190190

191+
**Agent Sources:**
192+
- **File path**: `aofctl run agent my-agent.yaml`
193+
- **Library URI**: `aofctl run agent library://domain/agent-name`
194+
191195
**Examples:**
192196
```bash
193-
# Interactive mode
197+
# Interactive mode (opens REPL)
194198
aofctl run agent my-agent.yaml
195199

196-
# With query
197-
aofctl run agent my-agent.yaml -i "Show me all pods"
200+
# With query (non-interactive)
201+
aofctl run agent my-agent.yaml --input "Show me all pods"
202+
203+
# Using --prompt alias
204+
aofctl run agent my-agent.yaml --prompt "Show me all pods"
205+
206+
# Run agent from library
207+
aofctl run agent library://kubernetes/pod-doctor --prompt "Debug CrashLoopBackOff in namespace production"
208+
209+
# Run library agent with JSON output
210+
aofctl run agent library://incident/rca-agent --prompt "Analyze high latency" -o json
198211

199212
# Run workflow
200213
aofctl run workflow incident-response.yaml
201214
```
202215

216+
**Library Domains:**
217+
- `kubernetes` - Pod diagnostics, resource optimization
218+
- `incident` - RCA, incident command, postmortems
219+
- `cicd` - Pipeline troubleshooting, builds
220+
- `security` - Vulnerability scanning, compliance
221+
- `observability` - Alerts, logging, tracing
222+
- `cloud` - Cost optimization, drift detection
223+
203224
**Example Output:**
204225
```bash
205226
$ aofctl run agent k8s-helper.yaml --input "Show me all pods"

0 commit comments

Comments
 (0)