lean_ctx/core/gain/
task_classifier.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
4pub enum TaskCategory {
5 Coding,
6 Debugging,
7 Refactoring,
8 Testing,
9 Exploration,
10 Planning,
11 Delegation,
12 Git,
13 BuildDeploy,
14 Knowledge,
15 Architecture,
16 Review,
17 General,
18}
19
20impl TaskCategory {
21 pub fn label(&self) -> &'static str {
22 match self {
23 TaskCategory::Coding => "Coding",
24 TaskCategory::Debugging => "Debugging",
25 TaskCategory::Refactoring => "Refactoring",
26 TaskCategory::Testing => "Testing",
27 TaskCategory::Exploration => "Exploration",
28 TaskCategory::Planning => "Planning",
29 TaskCategory::Delegation => "Delegation",
30 TaskCategory::Git => "Git",
31 TaskCategory::BuildDeploy => "Build/Deploy",
32 TaskCategory::Knowledge => "Knowledge",
33 TaskCategory::Architecture => "Architecture",
34 TaskCategory::Review => "Review",
35 TaskCategory::General => "General",
36 }
37 }
38}
39
40pub struct TaskClassifier;
41
42impl TaskClassifier {
43 pub fn classify_tool(tool_name: &str) -> TaskCategory {
44 let t = normalize(tool_name);
45 match t.as_str() {
46 "ctx_edit" | "ctx_fill" => TaskCategory::Refactoring,
47 "ctx_read" | "ctx_multi_read" | "ctx_smart_read" | "ctx_delta" | "ctx_tree"
48 | "ctx_search" | "ctx_outline" | "ctx_graph" | "ctx_graph_diagram"
49 | "ctx_callgraph" => TaskCategory::Exploration,
50 "ctx_semantic_search" | "ctx_architecture" | "ctx_impact" => TaskCategory::Architecture,
51 "ctx_overview" | "ctx_preload" | "ctx_task" | "ctx_intent" | "ctx_workflow" => {
52 TaskCategory::Planning
53 }
54 "ctx_handoff" | "ctx_agent" | "ctx_share" => TaskCategory::Delegation,
55 "ctx_session" | "ctx_knowledge" | "ctx_compress_memory" => TaskCategory::Knowledge,
56 "ctx_cost" | "ctx_gain" | "ctx_metrics" | "ctx_wrapped" | "ctx_heatmap" => {
57 TaskCategory::Review
58 }
59 "ctx_shell" | "ctx_execute" => TaskCategory::Debugging,
60 _ => TaskCategory::General,
61 }
62 }
63
64 pub fn classify_command_key(cmd_key: &str) -> TaskCategory {
65 let k = normalize(cmd_key);
66 if k.is_empty() {
67 return TaskCategory::General;
68 }
69 if k.starts_with("git ") || k == "git" {
70 return TaskCategory::Git;
71 }
72
73 if k.starts_with("cargo ") {
74 let sub = k.trim_start_matches("cargo ").trim();
75 if matches!(sub, "test" | "nextest" | "llvm-cov" | "tarpaulin") {
76 return TaskCategory::Testing;
77 }
78 if matches!(sub, "build" | "check" | "clippy" | "fmt" | "run" | "doc") {
79 return TaskCategory::BuildDeploy;
80 }
81 return TaskCategory::BuildDeploy;
82 }
83
84 if k.contains("test") || k.contains("pytest") || k.contains("jest") || k.contains("vitest")
85 {
86 return TaskCategory::Testing;
87 }
88 if k.contains("build")
89 || k.contains("deploy")
90 || k.contains("docker")
91 || k.contains("compose")
92 || k.contains("kubectl")
93 || k.contains("helm")
94 || k.contains("terraform")
95 {
96 return TaskCategory::BuildDeploy;
97 }
98 if k.contains("lint") || k.contains("clippy") || k.contains("fmt") || k.contains("format") {
99 return TaskCategory::BuildDeploy;
100 }
101 if k.contains("grep") || k.contains("rg") || k.contains("ripgrep") {
102 return TaskCategory::Exploration;
103 }
104
105 TaskCategory::General
106 }
107}
108
109fn normalize(s: &str) -> String {
110 s.trim().to_lowercase()
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn classify_git() {
119 assert_eq!(
120 TaskClassifier::classify_command_key("git status"),
121 TaskCategory::Git
122 );
123 }
124
125 #[test]
126 fn classify_tools() {
127 assert_eq!(
128 TaskClassifier::classify_tool("ctx_semantic_search"),
129 TaskCategory::Architecture
130 );
131 }
132}