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" => {
48 TaskCategory::Exploration
49 }
50 "ctx_tree" | "ctx_search" | "ctx_outline" | "ctx_graph" | "ctx_graph_diagram" => {
51 TaskCategory::Exploration
52 }
53 "ctx_semantic_search" | "ctx_architecture" | "ctx_impact" => TaskCategory::Architecture,
54 "ctx_overview" | "ctx_preload" | "ctx_task" | "ctx_intent" | "ctx_workflow" => {
55 TaskCategory::Planning
56 }
57 "ctx_handoff" | "ctx_agent" | "ctx_share" => TaskCategory::Delegation,
58 "ctx_session" | "ctx_knowledge" | "ctx_compress_memory" => TaskCategory::Knowledge,
59 "ctx_cost" | "ctx_gain" | "ctx_metrics" | "ctx_wrapped" | "ctx_heatmap" => {
60 TaskCategory::Review
61 }
62 "ctx_shell" | "ctx_execute" => TaskCategory::Debugging,
63 _ => TaskCategory::General,
64 }
65 }
66
67 pub fn classify_command_key(cmd_key: &str) -> TaskCategory {
68 let k = normalize(cmd_key);
69 if k.is_empty() {
70 return TaskCategory::General;
71 }
72 if k.starts_with("git ") || k == "git" {
73 return TaskCategory::Git;
74 }
75
76 if k.starts_with("cargo ") {
77 let sub = k.trim_start_matches("cargo ").trim();
78 if matches!(sub, "test" | "nextest" | "llvm-cov" | "tarpaulin") {
79 return TaskCategory::Testing;
80 }
81 if matches!(sub, "build" | "check" | "clippy" | "fmt" | "run" | "doc") {
82 return TaskCategory::BuildDeploy;
83 }
84 return TaskCategory::BuildDeploy;
85 }
86
87 if k.contains("test") || k.contains("pytest") || k.contains("jest") || k.contains("vitest")
88 {
89 return TaskCategory::Testing;
90 }
91 if k.contains("build")
92 || k.contains("deploy")
93 || k.contains("docker")
94 || k.contains("compose")
95 || k.contains("kubectl")
96 || k.contains("helm")
97 || k.contains("terraform")
98 {
99 return TaskCategory::BuildDeploy;
100 }
101 if k.contains("lint") || k.contains("clippy") || k.contains("fmt") || k.contains("format") {
102 return TaskCategory::BuildDeploy;
103 }
104 if k.contains("grep") || k.contains("rg") || k.contains("ripgrep") {
105 return TaskCategory::Exploration;
106 }
107
108 TaskCategory::General
109 }
110}
111
112fn normalize(s: &str) -> String {
113 s.trim().to_lowercase()
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn classify_git() {
122 assert_eq!(
123 TaskClassifier::classify_command_key("git status"),
124 TaskCategory::Git
125 );
126 }
127
128 #[test]
129 fn classify_tools() {
130 assert_eq!(
131 TaskClassifier::classify_tool("ctx_semantic_search"),
132 TaskCategory::Architecture
133 );
134 }
135}