1use crate::core::knowledge::ProjectKnowledge;
2use crate::core::session::SessionState;
3
4#[allow(clippy::too_many_arguments)]
5pub fn handle(
6 project_root: &str,
7 action: &str,
8 category: Option<&str>,
9 key: Option<&str>,
10 value: Option<&str>,
11 query: Option<&str>,
12 session_id: &str,
13 pattern_type: Option<&str>,
14 examples: Option<Vec<String>>,
15 confidence: Option<f32>,
16) -> String {
17 match action {
18 "remember" => {
19 let cat = match category {
20 Some(c) => c,
21 None => return "Error: category is required for remember".to_string(),
22 };
23 let k = match key {
24 Some(k) => k,
25 None => return "Error: key is required for remember".to_string(),
26 };
27 let v = match value {
28 Some(v) => v,
29 None => return "Error: value is required for remember".to_string(),
30 };
31 let conf = confidence.unwrap_or(0.8);
32 let mut knowledge = ProjectKnowledge::load_or_create(project_root);
33 knowledge.remember(cat, k, v, session_id, conf);
34 match knowledge.save() {
35 Ok(()) => format!("Remembered [{cat}] {k}: {v} (confidence: {:.0}%)", conf * 100.0),
36 Err(e) => format!("Remembered but save failed: {e}"),
37 }
38 }
39
40 "recall" => {
41 let knowledge = match ProjectKnowledge::load(project_root) {
42 Some(k) => k,
43 None => return "No knowledge stored for this project yet.".to_string(),
44 };
45
46 if let Some(cat) = category {
47 let facts = knowledge.recall_by_category(cat);
48 if facts.is_empty() {
49 return format!("No facts in category '{cat}'.");
50 }
51 return format_facts(&facts, Some(cat));
52 }
53
54 if let Some(q) = query {
55 let facts = knowledge.recall(q);
56 if facts.is_empty() {
57 return format!("No facts matching '{q}'.");
58 }
59 return format_facts(&facts, None);
60 }
61
62 "Error: provide query or category for recall".to_string()
63 }
64
65 "pattern" => {
66 let pt = match pattern_type {
67 Some(p) => p,
68 None => return "Error: pattern_type is required".to_string(),
69 };
70 let desc = match value {
71 Some(v) => v,
72 None => return "Error: value (description) is required for pattern".to_string(),
73 };
74 let exs = examples.unwrap_or_default();
75 let mut knowledge = ProjectKnowledge::load_or_create(project_root);
76 knowledge.add_pattern(pt, desc, exs, session_id);
77 match knowledge.save() {
78 Ok(()) => format!("Pattern [{pt}] added: {desc}"),
79 Err(e) => format!("Pattern added but save failed: {e}"),
80 }
81 }
82
83 "status" => {
84 let knowledge = match ProjectKnowledge::load(project_root) {
85 Some(k) => k,
86 None => return "No knowledge stored for this project yet. Use ctx_knowledge(action=\"remember\") to start.".to_string(),
87 };
88
89 let mut out = format!(
90 "Project Knowledge: {} facts, {} patterns, {} history entries\n",
91 knowledge.facts.len(),
92 knowledge.patterns.len(),
93 knowledge.history.len()
94 );
95 out.push_str(&format!("Last updated: {}\n", knowledge.updated_at.format("%Y-%m-%d %H:%M UTC")));
96 out.push_str(&knowledge.format_summary());
97 out
98 }
99
100 "remove" => {
101 let cat = match category {
102 Some(c) => c,
103 None => return "Error: category is required for remove".to_string(),
104 };
105 let k = match key {
106 Some(k) => k,
107 None => return "Error: key is required for remove".to_string(),
108 };
109 let mut knowledge = ProjectKnowledge::load_or_create(project_root);
110 if knowledge.remove_fact(cat, k) {
111 match knowledge.save() {
112 Ok(()) => format!("Removed [{cat}] {k}"),
113 Err(e) => format!("Removed but save failed: {e}"),
114 }
115 } else {
116 format!("No fact found: [{cat}] {k}")
117 }
118 }
119
120 "export" => {
121 let knowledge = match ProjectKnowledge::load(project_root) {
122 Some(k) => k,
123 None => return "No knowledge to export.".to_string(),
124 };
125 match serde_json::to_string_pretty(&knowledge) {
126 Ok(json) => json,
127 Err(e) => format!("Export failed: {e}"),
128 }
129 }
130
131 "consolidate" => {
132 let session = match SessionState::load_latest() {
133 Some(s) => s,
134 None => return "No active session to consolidate.".to_string(),
135 };
136
137 let mut knowledge = ProjectKnowledge::load_or_create(project_root);
138 let mut consolidated = 0u32;
139
140 for finding in &session.findings {
141 let key_text = if let Some(ref file) = finding.file {
142 if let Some(line) = finding.line {
143 format!("{file}:{line}")
144 } else {
145 file.clone()
146 }
147 } else {
148 format!("finding-{consolidated}")
149 };
150
151 knowledge.remember(
152 "finding",
153 &key_text,
154 &finding.summary,
155 &session.id,
156 0.7,
157 );
158 consolidated += 1;
159 }
160
161 for decision in &session.decisions {
162 let key_text = decision
163 .summary
164 .chars()
165 .take(50)
166 .collect::<String>()
167 .replace(' ', "-")
168 .to_lowercase();
169
170 knowledge.remember(
171 "decision",
172 &key_text,
173 &decision.summary,
174 &session.id,
175 0.85,
176 );
177 consolidated += 1;
178 }
179
180 let task_desc = session
181 .task
182 .as_ref()
183 .map(|t| t.description.clone())
184 .unwrap_or_else(|| "(no task)".into());
185
186 let summary = format!(
187 "Session {}: {} — {} findings, {} decisions consolidated",
188 session.id,
189 task_desc,
190 session.findings.len(),
191 session.decisions.len()
192 );
193 knowledge.consolidate(&summary, vec![session.id.clone()]);
194
195 match knowledge.save() {
196 Ok(()) => format!(
197 "Consolidated {consolidated} items from session {} into project knowledge.\n\
198 Facts: {}, Patterns: {}, History: {}",
199 session.id,
200 knowledge.facts.len(),
201 knowledge.patterns.len(),
202 knowledge.history.len()
203 ),
204 Err(e) => format!("Consolidation done but save failed: {e}"),
205 }
206 }
207
208 _ => format!(
209 "Unknown action: {action}. Use: remember, recall, pattern, status, remove, export, consolidate"
210 ),
211 }
212}
213
214fn format_facts(
215 facts: &[&crate::core::knowledge::KnowledgeFact],
216 category: Option<&str>,
217) -> String {
218 let mut out = String::new();
219 if let Some(cat) = category {
220 out.push_str(&format!("Facts [{cat}] ({}):\n", facts.len()));
221 } else {
222 out.push_str(&format!("Matching facts ({}):\n", facts.len()));
223 }
224 for f in facts {
225 out.push_str(&format!(
226 " [{}/{}]: {} (confidence: {:.0}%, confirmed: {})\n",
227 f.category,
228 f.key,
229 f.value,
230 f.confidence * 100.0,
231 f.last_confirmed.format("%Y-%m-%d")
232 ));
233 }
234 out
235}