1use serde_json::Value;
6
7use super::templates::{
8 BROWSE_DASHBOARD_INSTRUCTIONS, BROWSE_SYSTEM_RESPONSE, CAPTURE_ASSISTANT_SYSTEM,
9 CONTEXT_CAPTURE_INSTRUCTIONS, CONTEXT_CAPTURE_RESPONSE, DISCOVER_INSTRUCTIONS,
10 DISCOVER_RESPONSE, GENERATE_TUTORIAL_RESPONSE, GENERATE_TUTORIAL_STRUCTURE,
11 INTENT_SEARCH_INSTRUCTIONS, INTENT_SEARCH_RESPONSE, LIST_FORMAT_INSTRUCTIONS,
12 QUERY_SUGGEST_INSTRUCTIONS, QUERY_SUGGEST_RESPONSE, SEARCH_HELP_SYSTEM,
13 SESSION_START_INSTRUCTIONS, SESSION_START_RESPONSE, TUTORIAL_BEST_PRACTICES, TUTORIAL_CAPTURE,
14 TUTORIAL_NAMESPACES, TUTORIAL_OVERVIEW, TUTORIAL_SEARCH, TUTORIAL_WORKFLOWS,
15};
16use super::types::{PromptContent, PromptMessage};
17
18pub fn generate_tutorial_prompt(arguments: &Value) -> Vec<PromptMessage> {
20 let familiarity = arguments
21 .get("familiarity")
22 .and_then(|v| v.as_str())
23 .unwrap_or("beginner");
24
25 let focus = arguments
26 .get("focus")
27 .and_then(|v| v.as_str())
28 .unwrap_or("overview");
29
30 let intro = match familiarity {
31 "advanced" => {
32 "I see you're experienced with memory systems. Let me show you Subcog's advanced features."
33 },
34 "intermediate" => {
35 "Great, you have some familiarity with memory systems. Let me explain Subcog's key concepts."
36 },
37 _ => "Welcome to Subcog! I'll guide you through the basics of the memory system.",
38 };
39
40 let focus_content = match focus {
41 "capture" => TUTORIAL_CAPTURE,
42 "recall" | "search" => TUTORIAL_SEARCH,
43 "namespaces" => TUTORIAL_NAMESPACES,
44 "workflows" => TUTORIAL_WORKFLOWS,
45 "best-practices" => TUTORIAL_BEST_PRACTICES,
46 _ => TUTORIAL_OVERVIEW,
47 };
48
49 vec![
50 PromptMessage {
51 role: "user".to_string(),
52 content: PromptContent::Text {
53 text: format!(
54 "I'd like to learn about Subcog. My familiarity level is '{familiarity}' and I want to focus on '{focus}'."
55 ),
56 },
57 },
58 PromptMessage {
59 role: "assistant".to_string(),
60 content: PromptContent::Text {
61 text: format!("{intro}\n\n{focus_content}"),
62 },
63 },
64 ]
65}
66
67pub fn generate_generate_tutorial_messages(arguments: &Value) -> Vec<PromptMessage> {
71 let topic = arguments
72 .get("topic")
73 .and_then(|v| v.as_str())
74 .unwrap_or("general");
75
76 let level = arguments
77 .get("level")
78 .and_then(|v| v.as_str())
79 .unwrap_or("beginner");
80
81 let format = arguments
82 .get("format")
83 .and_then(|v| v.as_str())
84 .unwrap_or("markdown");
85
86 let level_description = match level {
87 "advanced" => "for an experienced developer who knows the fundamentals",
88 "intermediate" => "for a developer with some experience",
89 _ => "for someone new to this topic",
90 };
91
92 let format_instruction = match format {
93 "outline" => {
94 "Present the tutorial as a structured outline with main sections and sub-points."
95 },
96 "steps" => "Present the tutorial as numbered step-by-step instructions.",
97 _ => "Present the tutorial in full markdown with headings, examples, and explanations.",
98 };
99
100 let prompt = format!(
101 "Generate a comprehensive tutorial about **{topic}** {level_description}.\n\n\
102 **Instructions**:\n\
103 1. First, search for relevant memories using `mcp__plugin_subcog_subcog__subcog_recall` with query: \"{topic}\"\n\
104 2. Incorporate insights, decisions, and patterns from the memories found\n\
105 3. Structure the tutorial with clear sections\n\
106 4. Include practical examples where applicable\n\
107 5. Reference specific memories that inform the content\n\n\
108 **Format**: {format_instruction}\n\n\
109 {GENERATE_TUTORIAL_STRUCTURE}"
110 );
111
112 vec![
113 PromptMessage {
114 role: "user".to_string(),
115 content: PromptContent::Text { text: prompt },
116 },
117 PromptMessage {
118 role: "assistant".to_string(),
119 content: PromptContent::Text {
120 text: GENERATE_TUTORIAL_RESPONSE.to_string(),
121 },
122 },
123 ]
124}
125
126pub fn generate_capture_assistant_prompt(arguments: &Value) -> Vec<PromptMessage> {
128 let context = arguments
129 .get("context")
130 .or_else(|| arguments.get("content"))
131 .or_else(|| arguments.get("conversation"))
132 .and_then(|v| v.as_str())
133 .unwrap_or("");
134
135 vec![
136 PromptMessage {
137 role: "user".to_string(),
138 content: PromptContent::Text {
139 text: format!(
140 "Please analyze this context and suggest what memories to capture:\n\n{context}"
141 ),
142 },
143 },
144 PromptMessage {
145 role: "assistant".to_string(),
146 content: PromptContent::Text {
147 text: CAPTURE_ASSISTANT_SYSTEM.to_string(),
148 },
149 },
150 ]
151}
152
153pub fn generate_review_prompt(arguments: &Value) -> Vec<PromptMessage> {
155 let namespace = arguments
156 .get("namespace")
157 .and_then(|v| v.as_str())
158 .unwrap_or("all");
159
160 let action = arguments
161 .get("action")
162 .and_then(|v| v.as_str())
163 .unwrap_or("summarize");
164
165 vec![PromptMessage {
166 role: "user".to_string(),
167 content: PromptContent::Text {
168 text: format!(
169 "Please {action} the memories in the '{namespace}' namespace. Help me understand what we have and identify any gaps or improvements."
170 ),
171 },
172 }]
173}
174
175pub fn generate_decision_prompt(arguments: &Value) -> Vec<PromptMessage> {
177 let decision = arguments
178 .get("decision")
179 .and_then(|v| v.as_str())
180 .unwrap_or("");
181
182 let context = arguments
183 .get("context")
184 .and_then(|v| v.as_str())
185 .unwrap_or("");
186
187 let alternatives = arguments
188 .get("alternatives")
189 .and_then(|v| v.as_str())
190 .unwrap_or("");
191
192 let mut prompt =
193 format!("I need to document the following decision:\n\n**Decision**: {decision}\n");
194
195 if !context.is_empty() {
196 prompt.push_str(&format!("\n**Context**: {context}\n"));
197 }
198
199 if !alternatives.is_empty() {
200 prompt.push_str(&format!("\n**Alternatives considered**: {alternatives}\n"));
201 }
202
203 prompt.push_str(
204 "\nPlease help me document this decision in a structured way that captures:\n\
205 1. The context and problem being solved\n\
206 2. The decision and rationale\n\
207 3. Consequences and trade-offs\n\
208 4. Suggested tags for searchability",
209 );
210
211 vec![PromptMessage {
212 role: "user".to_string(),
213 content: PromptContent::Text { text: prompt },
214 }]
215}
216
217pub fn generate_search_help_prompt(arguments: &Value) -> Vec<PromptMessage> {
219 let goal = arguments
220 .get("goal")
221 .or_else(|| arguments.get("query"))
222 .and_then(|v| v.as_str())
223 .unwrap_or("");
224
225 vec![
226 PromptMessage {
227 role: "user".to_string(),
228 content: PromptContent::Text {
229 text: format!(
230 "I'm trying to find memories related to: {goal}\n\nHelp me craft effective search queries."
231 ),
232 },
233 },
234 PromptMessage {
235 role: "assistant".to_string(),
236 content: PromptContent::Text {
237 text: SEARCH_HELP_SYSTEM.to_string(),
238 },
239 },
240 ]
241}
242
243pub fn generate_browse_prompt(arguments: &Value) -> Vec<PromptMessage> {
245 let filter = arguments
246 .get("filter")
247 .and_then(|v| v.as_str())
248 .unwrap_or("");
249
250 let view = arguments
251 .get("view")
252 .and_then(|v| v.as_str())
253 .unwrap_or("dashboard");
254
255 let top = arguments
256 .get("top")
257 .and_then(|v| v.as_str())
258 .unwrap_or("10");
259
260 let mut prompt = String::from(
261 "Show me a memory browser dashboard.\n\n**IMPORTANT**: Use the `mcp__plugin_subcog_subcog__subcog_recall` tool to fetch memories with server-side filtering:\n",
262 );
263
264 if filter.is_empty() {
265 prompt.push_str(
266 "```json\n{ \"query\": \"*\", \"limit\": 100, \"detail\": \"medium\" }\n```\n\n",
267 );
268 prompt.push_str("No filters applied - show the full dashboard with:\n");
269 } else {
270 prompt.push_str(&format!(
271 "```json\n{{ \"query\": \"*\", \"filter\": \"{filter}\", \"limit\": 100, \"detail\": \"medium\" }}\n```\n\n"
272 ));
273 }
274
275 prompt.push_str(&format!(
276 "View mode: {view}\nShow top {top} items per facet.\n\n"
277 ));
278
279 prompt.push_str(BROWSE_DASHBOARD_INSTRUCTIONS);
280
281 vec![
282 PromptMessage {
283 role: "user".to_string(),
284 content: PromptContent::Text { text: prompt },
285 },
286 PromptMessage {
287 role: "assistant".to_string(),
288 content: PromptContent::Text {
289 text: BROWSE_SYSTEM_RESPONSE.to_string(),
290 },
291 },
292 ]
293}
294
295pub fn generate_list_prompt(arguments: &Value) -> Vec<PromptMessage> {
297 let filter = arguments
298 .get("filter")
299 .and_then(|v| v.as_str())
300 .unwrap_or("");
301
302 let format = arguments
303 .get("format")
304 .and_then(|v| v.as_str())
305 .unwrap_or("table");
306
307 let limit = arguments
308 .get("limit")
309 .and_then(|v| v.as_str())
310 .unwrap_or("50");
311
312 let mut prompt = String::from(
313 "List memories from Subcog.\n\n**IMPORTANT**: Use the `mcp__plugin_subcog_subcog__subcog_recall` tool to fetch memories with server-side filtering:\n",
314 );
315
316 if filter.is_empty() {
317 prompt.push_str(&format!(
318 "```json\n{{ \"query\": \"*\", \"limit\": {limit}, \"detail\": \"medium\" }}\n```\n\n"
319 ));
320 } else {
321 prompt.push_str(&format!(
322 "```json\n{{ \"query\": \"*\", \"filter\": \"{filter}\", \"limit\": {limit}, \"detail\": \"medium\" }}\n```\n\n"
323 ));
324 }
325
326 prompt.push_str(&format!("Format: {format}\n\n"));
327
328 prompt.push_str(LIST_FORMAT_INSTRUCTIONS);
329
330 vec![PromptMessage {
331 role: "user".to_string(),
332 content: PromptContent::Text { text: prompt },
333 }]
334}
335
336pub fn generate_intent_search_prompt(arguments: &Value) -> Vec<PromptMessage> {
340 let query = arguments
341 .get("query")
342 .and_then(|v| v.as_str())
343 .unwrap_or("");
344
345 let intent = arguments
346 .get("intent")
347 .and_then(|v| v.as_str())
348 .unwrap_or("");
349
350 let context = arguments
351 .get("context")
352 .and_then(|v| v.as_str())
353 .unwrap_or("");
354
355 let mut prompt = format!("Search for memories related to: **{query}**\n\n");
356
357 if !intent.is_empty() {
358 prompt.push_str(&format!("Intent hint: {intent}\n\n"));
359 }
360
361 if !context.is_empty() {
362 prompt.push_str(&format!("Current context: {context}\n\n"));
363 }
364
365 prompt.push_str(INTENT_SEARCH_INSTRUCTIONS);
366
367 vec![
368 PromptMessage {
369 role: "user".to_string(),
370 content: PromptContent::Text { text: prompt },
371 },
372 PromptMessage {
373 role: "assistant".to_string(),
374 content: PromptContent::Text {
375 text: INTENT_SEARCH_RESPONSE.to_string(),
376 },
377 },
378 ]
379}
380
381pub fn generate_query_suggest_prompt(arguments: &Value) -> Vec<PromptMessage> {
383 let topic = arguments
384 .get("topic")
385 .or_else(|| arguments.get("query"))
386 .and_then(|v| v.as_str())
387 .unwrap_or("");
388
389 let namespace = arguments
390 .get("namespace")
391 .and_then(|v| v.as_str())
392 .unwrap_or("");
393
394 let mut prompt = String::from("Help me explore my memory collection.\n\n");
395
396 if !topic.is_empty() {
397 prompt.push_str(&format!("Topic area: **{topic}**\n"));
398 }
399
400 if !namespace.is_empty() {
401 prompt.push_str(&format!("Focus namespace: **{namespace}**\n"));
402 }
403
404 prompt.push('\n');
405 prompt.push_str(QUERY_SUGGEST_INSTRUCTIONS);
406
407 vec![
408 PromptMessage {
409 role: "user".to_string(),
410 content: PromptContent::Text { text: prompt },
411 },
412 PromptMessage {
413 role: "assistant".to_string(),
414 content: PromptContent::Text {
415 text: QUERY_SUGGEST_RESPONSE.to_string(),
416 },
417 },
418 ]
419}
420
421pub fn generate_context_capture_prompt(arguments: &Value) -> Vec<PromptMessage> {
423 let conversation = arguments
424 .get("conversation")
425 .or_else(|| arguments.get("context"))
426 .and_then(|v| v.as_str())
427 .unwrap_or("");
428
429 let threshold = arguments
430 .get("threshold")
431 .and_then(|v| v.as_str())
432 .unwrap_or("0.7");
433
434 let prompt = format!(
435 "Analyze this conversation/context and suggest memories to capture:\n\n\
436 ---\n{conversation}\n---\n\n\
437 Confidence threshold: {threshold}\n\n\
438 {CONTEXT_CAPTURE_INSTRUCTIONS}"
439 );
440
441 vec![
442 PromptMessage {
443 role: "user".to_string(),
444 content: PromptContent::Text { text: prompt },
445 },
446 PromptMessage {
447 role: "assistant".to_string(),
448 content: PromptContent::Text {
449 text: CONTEXT_CAPTURE_RESPONSE.to_string(),
450 },
451 },
452 ]
453}
454
455pub fn generate_discover_prompt(arguments: &Value) -> Vec<PromptMessage> {
457 let start = arguments
458 .get("start")
459 .or_else(|| arguments.get("topic"))
460 .or_else(|| arguments.get("tag"))
461 .and_then(|v| v.as_str())
462 .unwrap_or("");
463
464 let depth = arguments
465 .get("depth")
466 .and_then(|v| v.as_str())
467 .unwrap_or("2");
468
469 let mut prompt = String::from("Explore related memories and topics.\n\n");
470
471 if start.is_empty() {
472 prompt.push_str("No starting point specified - show an overview of available topics.\n");
473 } else {
474 prompt.push_str(&format!("Starting point: **{start}**\n"));
475 }
476
477 prompt.push_str(&format!("Exploration depth: {depth} hops\n\n"));
478 prompt.push_str(DISCOVER_INSTRUCTIONS);
479
480 vec![
481 PromptMessage {
482 role: "user".to_string(),
483 content: PromptContent::Text { text: prompt },
484 },
485 PromptMessage {
486 role: "assistant".to_string(),
487 content: PromptContent::Text {
488 text: DISCOVER_RESPONSE.to_string(),
489 },
490 },
491 ]
492}
493
494pub fn generate_session_start_prompt(arguments: &Value) -> Vec<PromptMessage> {
498 let include_recall = arguments
499 .get("include_recall")
500 .and_then(serde_json::Value::as_bool)
501 .unwrap_or(true);
502
503 let project = arguments
504 .get("project")
505 .and_then(|v| v.as_str())
506 .unwrap_or("");
507
508 let mut prompt = String::from("Initialize my Subcog session.\n\n");
509
510 if include_recall {
511 prompt.push_str(
512 "**Include context recall**: Yes - search for project setup and architecture.\n",
513 );
514 } else {
515 prompt.push_str("**Include context recall**: No - skip initial recall.\n");
516 }
517
518 if !project.is_empty() {
519 prompt.push_str(&format!("**Project context**: {project}\n"));
520 }
521
522 prompt.push('\n');
523 prompt.push_str(SESSION_START_INSTRUCTIONS);
524
525 vec![
526 PromptMessage {
527 role: "user".to_string(),
528 content: PromptContent::Text { text: prompt },
529 },
530 PromptMessage {
531 role: "assistant".to_string(),
532 content: PromptContent::Text {
533 text: SESSION_START_RESPONSE.to_string(),
534 },
535 },
536 ]
537}