lean_ctx/tools/
ctx_skillify.rs1use crate::core::skillify;
5
6pub fn handle(project_root: &str, action: &str, slug: Option<&str>) -> String {
9 match action.trim() {
10 "" | "mine" => render_mine(project_root),
11 "list" => render_list(project_root),
12 "status" => render_status(project_root),
13 "promote" => render_promote(project_root, slug),
14 other => format!(
15 "ERR: unknown skillify action '{other}'. Use: mine | list | status | promote <slug>"
16 ),
17 }
18}
19
20fn render_mine(project_root: &str) -> String {
21 match skillify::mine(project_root) {
22 Err(e) => format!("skillify: {e}"),
23 Ok(r) => {
24 let mut out = format!(
25 "skillify mine → {} created, {} merged, {} unchanged, {} skipped ({} candidates)\n",
26 r.created.len(),
27 r.merged.len(),
28 r.unchanged.len(),
29 r.skipped.len(),
30 r.candidates_seen,
31 );
32 out.push_str(&format!("output: {}\n", r.output_dir));
33 for name in &r.created {
34 out.push_str(&format!(" + {name}\n"));
35 }
36 for name in &r.merged {
37 out.push_str(&format!(" ~ {name} (version bumped)\n"));
38 }
39 if r.created.is_empty() && r.merged.is_empty() {
40 out.push_str(" (no new or changed rules)\n");
41 }
42 out
43 }
44 }
45}
46
47fn render_list(project_root: &str) -> String {
48 let rules = skillify::list_rules(project_root);
49 if rules.is_empty() {
50 return "skillify: no generated rules yet — run `skillify mine`".to_string();
51 }
52 let mut out = format!("skillify rules ({}):\n", rules.len());
53 for r in rules {
54 out.push_str(&format!(" {} v{} — {}\n", r.slug, r.version, r.title));
55 }
56 out
57}
58
59fn render_status(project_root: &str) -> String {
60 let cfg = skillify::current_config();
61 let candidates = skillify::candidate::mine_candidates(project_root).len();
62 let rules = skillify::list_rules(project_root).len();
63 format!(
64 "skillify status\n enabled: {}\n scope: {}\n min_confidence: {:.2}\n min_recurrence: {}\n candidates available: {}\n generated rules: {}",
65 cfg.enabled, cfg.scope, cfg.min_confidence, cfg.min_recurrence, candidates, rules
66 )
67}
68
69fn render_promote(project_root: &str, slug: Option<&str>) -> String {
70 let Some(slug) = slug.filter(|s| !s.trim().is_empty()) else {
71 return "ERR: promote requires a rule slug (see `skillify list`)".to_string();
72 };
73 match skillify::promote(project_root, slug.trim()) {
74 Ok(dst) => format!("skillify: promoted `{slug}` → {dst}"),
75 Err(e) => format!("skillify: {e}"),
76 }
77}