feat(cli): add skillforge command for auto-discovery of skills
Some checks failed
CodeQL Analysis / CodeQL Analysis (push) Has been cancelled
PR Hygiene / nudge-stale-prs (push) Has been cancelled
CI / Detect Change Scope (push) Has been cancelled
Docker / PR Docker Smoke (push) Has been cancelled
Docker / Build and Push Docker Image (push) Has been cancelled
Rust Package Security Audit / Security Audit (push) Has been cancelled
Rust Package Security Audit / License & Supply Chain (push) Has been cancelled
CI / Format & Lint (push) Has been cancelled
CI / Lint Strict Delta (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (Smoke) (push) Has been cancelled
CI / Docs-Only Fast Path (push) Has been cancelled
CI / Non-Rust Fast Path (push) Has been cancelled
CI / Docs Quality (push) Has been cancelled
CI / CI Required Gate (push) Has been cancelled
Stale / stale (push) Has been cancelled
Update Contributors NOTICE / Update NOTICE with new contributors (push) Has been cancelled
Some checks failed
CodeQL Analysis / CodeQL Analysis (push) Has been cancelled
PR Hygiene / nudge-stale-prs (push) Has been cancelled
CI / Detect Change Scope (push) Has been cancelled
Docker / PR Docker Smoke (push) Has been cancelled
Docker / Build and Push Docker Image (push) Has been cancelled
Rust Package Security Audit / Security Audit (push) Has been cancelled
Rust Package Security Audit / License & Supply Chain (push) Has been cancelled
CI / Format & Lint (push) Has been cancelled
CI / Lint Strict Delta (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Build (Smoke) (push) Has been cancelled
CI / Docs-Only Fast Path (push) Has been cancelled
CI / Non-Rust Fast Path (push) Has been cancelled
CI / Docs Quality (push) Has been cancelled
CI / CI Required Gate (push) Has been cancelled
Stale / stale (push) Has been cancelled
Update Contributors NOTICE / Update NOTICE with new contributors (push) Has been cancelled
- Add zeroclaw skillforge run --min-score 0.7 --dry-run - Add zeroclaw skillforge scout --query "ai agent" --limit 20 - Add zeroclaw skillforge status to show configuration - Wire SkillForgeConfig into main Config struct - Add new_with_query method to GitHubScout for custom searches
This commit is contained in:
@@ -16,6 +16,136 @@ use self::evaluate::{EvalResult, Evaluator, Recommendation};
|
||||
use self::integrate::Integrator;
|
||||
use self::scout::{GitHubScout, Scout, ScoutResult, ScoutSource};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
#[derive(Debug, Clone, clap::Subcommand)]
|
||||
pub enum SkillforgeCommands {
|
||||
Run {
|
||||
#[arg(long, default_value = "0.7")]
|
||||
min_score: f64,
|
||||
#[arg(long)]
|
||||
dry_run: bool,
|
||||
#[arg(long)]
|
||||
source: Option<String>,
|
||||
},
|
||||
Scout {
|
||||
#[arg(long, default_value = "zeroclaw skill")]
|
||||
query: String,
|
||||
#[arg(long, default_value = "20")]
|
||||
limit: usize,
|
||||
},
|
||||
Status,
|
||||
}
|
||||
|
||||
pub async fn handle_command(command: SkillforgeCommands, config: &Config) -> Result<()> {
|
||||
let _forge_config = config.skillforge.clone();
|
||||
|
||||
match command {
|
||||
SkillforgeCommands::Run { min_score, dry_run, source } => {
|
||||
run_forge(config, min_score, dry_run, source).await
|
||||
}
|
||||
SkillforgeCommands::Scout { query, limit } => {
|
||||
run_scout(config, query, limit).await
|
||||
}
|
||||
SkillforgeCommands::Status => {
|
||||
show_status(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_forge(config: &Config, min_score: f64, dry_run: bool, source: Option<String>) -> Result<()> {
|
||||
println!("🔮 SkillForge Pipeline");
|
||||
println!(" Min Score: {}", min_score);
|
||||
println!(" Dry Run: {}", dry_run);
|
||||
println!(" Source: {}", source.as_deref().unwrap_or("all"));
|
||||
println!();
|
||||
|
||||
let mut forge_config = config.skillforge.clone();
|
||||
forge_config.enabled = true;
|
||||
forge_config.min_score = min_score;
|
||||
forge_config.auto_integrate = !dry_run;
|
||||
|
||||
if let Some(src) = source {
|
||||
forge_config.sources = vec![src];
|
||||
}
|
||||
|
||||
let forge = SkillForge::new(forge_config);
|
||||
let report = forge.forge().await?;
|
||||
|
||||
println!("📊 Results:");
|
||||
println!(" Discovered: {}", report.discovered);
|
||||
println!(" Evaluated: {}", report.evaluated);
|
||||
println!(" Auto-integrated: {}", report.auto_integrated);
|
||||
println!(" Manual review: {}", report.manual_review);
|
||||
println!(" Skipped: {}", report.skipped);
|
||||
println!();
|
||||
|
||||
if !report.results.is_empty() {
|
||||
println!("📋 Top Candidates:");
|
||||
for (i, res) in report.results.iter().take(10).enumerate() {
|
||||
let status = match res.recommendation {
|
||||
Recommendation::Auto => "✅ AUTO",
|
||||
Recommendation::Manual => "⚠️ REVIEW",
|
||||
Recommendation::Skip => "❌ SKIP",
|
||||
};
|
||||
println!(" {}. {} [{:.2}] {}", i + 1, res.candidate.name, res.total_score, status);
|
||||
println!(" {}", res.candidate.description.chars().take(60).collect::<String>());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_scout(config: &Config, query: String, limit: usize) -> Result<()> {
|
||||
println!("🔍 SkillForge Scout");
|
||||
println!(" Query: {}", query);
|
||||
println!(" Limit: {}", limit);
|
||||
println!();
|
||||
|
||||
let github_token = config.skillforge.github_token.clone();
|
||||
let scout = GitHubScout::new_with_query(github_token, query.clone());
|
||||
|
||||
let results = scout.discover().await?;
|
||||
let limited: Vec<_> = results.into_iter().take(limit).collect();
|
||||
|
||||
println!("📋 Found {} candidates:", limited.len());
|
||||
for (i, candidate) in limited.iter().enumerate() {
|
||||
println!(" {}. {}", i + 1, candidate.name);
|
||||
println!(" URL: {}", candidate.url);
|
||||
println!(" ⭐ {} stars", candidate.stars);
|
||||
let desc: String = candidate.description.chars().take(60).collect();
|
||||
if !desc.is_empty() {
|
||||
println!(" {}", desc);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_status(config: &Config) -> Result<()> {
|
||||
println!("🔮 SkillForge Status");
|
||||
println!();
|
||||
println!("Configuration:");
|
||||
println!(" Enabled: {}", config.skillforge.enabled);
|
||||
println!(" Auto-integrate: {}", config.skillforge.auto_integrate);
|
||||
println!(" Sources: {:?}", config.skillforge.sources);
|
||||
println!(" Scan interval: {}h", config.skillforge.scan_interval_hours);
|
||||
println!(" Min score: {:.2}", config.skillforge.min_score);
|
||||
println!(" Output directory: {}", config.skillforge.output_dir);
|
||||
println!(" GitHub token: {}", if config.skillforge.github_token.is_some() { "configured" } else { "not set" });
|
||||
|
||||
let skills_dir = std::path::Path::new(&config.skillforge.output_dir);
|
||||
if skills_dir.exists() {
|
||||
let count = std::fs::read_dir(skills_dir)
|
||||
.map(|entries| entries.filter_map(|e| e.ok()).count())
|
||||
.unwrap_or(0);
|
||||
println!();
|
||||
println!("Integrated skills: {}", count);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user