use crate::parser;
use crate::store::Store;
use anyhow::Context as _;
use chrono::NaiveDate;
pub fn build_system_prompt(
store: &Store,
since: NaiveDate,
today: NaiveDate,
) -> anyhow::Result<(String, usize)> {
let files = store
.files_since(since)
.context("failed to list journal files")?;
let mut body = String::new();
let mut count = 0usize;
let mut dated_files: Vec<(NaiveDate, std::path::PathBuf)> = files
.into_iter()
.filter_map(|path| {
let name = path.file_name()?.to_str()?.to_string();
let date_str: String = name.chars().take(8).collect();
let date = NaiveDate::parse_from_str(&date_str, "%Y%m%d").ok()?;
if date <= today {
Some((date, path))
} else {
None
}
})
.collect();
dated_files.sort_by_key(|(d, _)| *d);
let mut i = 0;
while i < dated_files.len() {
let date = dated_files[i].0;
let mut day_elements: Vec<String> = Vec::new();
while i < dated_files.len() && dated_files[i].0 == date {
let path = &dated_files[i].1;
if let Ok(elements) = parser::parse_file(path) {
for el in elements.values() {
if !el.is_unknown() && !el.is_mps_group() {
day_elements.push(format_element(el));
}
}
}
i += 1;
}
if !day_elements.is_empty() {
body.push_str(&format!("\n--- {} ---\n", date.format("%Y-%m-%d")));
for line in &day_elements {
body.push_str(line);
body.push('\n');
}
count += day_elements.len();
}
}
let prompt = format!(
"You are a helpful assistant for mps, a personal productivity journal.\n\
Today is {}. Answer questions using the journal data below.\n\
Keep responses concise and practical.\n\
\n\
=== MPS DATA ({} to {}) ===\
{}",
today.format("%Y-%m-%d"),
since.format("%Y-%m-%d"),
today.format("%Y-%m-%d"),
if body.is_empty() {
"\n(no entries in this date range)".to_string()
} else {
body
}
);
Ok((prompt, count))
}
fn format_element(element: &crate::elements::Element) -> String {
let kind = element.kind().to_string();
let body = element.body_str();
let tags = element.tags();
let attrs = element.typed_attrs();
let tag_str = if tags.is_empty() {
String::new()
} else {
format!(
" {}",
tags.iter()
.map(|t| format!("#{}", t))
.collect::<Vec<_>>()
.join(" ")
)
};
let attr_str = if attrs.is_empty() {
String::new()
} else {
let parts: Vec<String> = attrs
.iter()
.map(|(k, v)| format!("[{}:{}]", k, v))
.collect();
format!(" {}", parts.join(" "))
};
format!("[{}] {}{}{}", kind, body, tag_str, attr_str)
}