1use serde::{Deserialize, Serialize};
4
5pub fn sanitize(text: &str) -> String {
7 text.replace("\n---\n", "\n\\---\n")
8}
9
10pub fn desanitize(text: &str) -> String {
12 text.replace("\n\\---\n", "\n---\n")
13}
14
15pub fn split_blocks(text: &str) -> Vec<String> {
20 let raw: Vec<&str> = text.split("---\n").collect();
21 let mut blocks: Vec<String> = Vec::new();
22 let mut i = 0;
23 while i < raw.len() {
24 let mut block = raw[i].to_string();
25 while block.ends_with('\\') && i + 1 < raw.len() {
27 i += 1;
28 block.push_str("---\n");
29 block.push_str(raw[i]);
30 }
31 blocks.push(block);
32 i += 1;
33 }
34 blocks
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct OTARecord {
41 pub step: usize,
42 pub timestamp: String,
43 pub observation: String,
44 pub thought: String,
45 pub action: String,
46}
47
48impl OTARecord {
49 pub fn to_markdown(&self) -> String {
50 format!(
51 "### Step {} — {}\n**Observation:** {}\n\n**Thought:** {}\n\n**Action:** {}\n\n---\n",
52 self.step,
53 self.timestamp,
54 sanitize(&self.observation),
55 sanitize(&self.thought),
56 sanitize(&self.action),
57 )
58 }
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct CommitRecord {
65 pub commit_id: String,
66 pub branch_name: String,
67 pub branch_purpose: String,
68 pub previous_progress_summary: String,
69 pub this_commit_contribution: String,
70 pub timestamp: String,
71}
72
73impl CommitRecord {
74 pub fn to_markdown(&self) -> String {
75 format!(
76 "## Commit `{}`\n**Timestamp:** {}\n\n**Branch Purpose:** {}\n\n\
77 **Previous Progress Summary:** {}\n\n\
78 **This Commit's Contribution:** {}\n\n---\n",
79 self.commit_id,
80 self.timestamp,
81 sanitize(&self.branch_purpose),
82 sanitize(&self.previous_progress_summary),
83 sanitize(&self.this_commit_contribution),
84 )
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct BranchMetadata {
92 pub name: String,
93 pub purpose: String,
94 pub created_from: String,
95 pub created_at: String,
96 pub status: String, pub merged_into: Option<String>,
98 pub merged_at: Option<String>,
99}
100
101#[derive(Debug, Clone)]
104pub struct ContextResult {
105 pub branch_name: String,
106 pub k: usize,
107 pub commits: Vec<CommitRecord>,
108 pub ota_records: Vec<OTARecord>,
109 pub main_roadmap: String,
110 pub metadata: Option<BranchMetadata>,
111}
112
113impl ContextResult {
114 pub fn summary(&self) -> String {
115 let mut out = format!(
116 "# CONTEXT — branch `{}` (K={})\n\n",
117 self.branch_name, self.k
118 );
119 out.push_str("## Global Roadmap\n");
120 out.push_str(&self.main_roadmap);
121 out.push_str("\n\n");
122 out.push_str(&format!("## Last {} Commit(s)\n", self.k));
123 for c in &self.commits {
124 out.push_str(&c.to_markdown());
125 }
126 if !self.ota_records.is_empty() {
127 let recent = self.ota_records.iter().rev().take(5).collect::<Vec<_>>();
128 out.push_str(&format!(
129 "\n## Recent OTA Steps (showing last {} of {})\n",
130 recent.len(),
131 self.ota_records.len()
132 ));
133 for r in recent.into_iter().rev() {
134 out.push_str(&r.to_markdown());
135 }
136 }
137 out
138 }
139}