1use std::fs;
2use std::path::Path;
3
4use memnite_core::Scope;
5
6use crate::error::IngestError;
7use crate::parse::split_h2_sections;
8use crate::source::{IngestSource, IngestedMemory};
9
10pub struct RepoBrainSource;
16
17impl IngestSource for RepoBrainSource {
18 fn name(&self) -> &str {
19 "brain"
20 }
21
22 fn collect(&self, root: &Path) -> Result<Vec<IngestedMemory>, IngestError> {
23 let mut out = Vec::new();
24
25 let ul_path = root.join("UBIQUITOUS_LANGUAGE.md");
26 if ul_path.exists() {
27 let content = fs::read_to_string(&ul_path)?;
28 let sections = split_h2_sections(&content);
29 if let Some(start) = sections
32 .iter()
33 .position(|(h, _)| h.eq_ignore_ascii_case("Terms"))
34 {
35 for (heading, body) in sections.into_iter().skip(start + 1) {
36 out.push(IngestedMemory {
37 source_key: format!("ubiquitous-language::{heading}"),
38 title: heading,
39 body,
40 mem_type: "term".to_string(),
41 topic_key: Some("ubiquitous-language".to_string()),
42 scope: Scope::Repo,
43 });
44 }
45 }
46 }
47
48 for (rel, title, mem_type, topic) in [
49 (
50 "docs/wiki/ai/CONTRACTS.md",
51 "CONTRACTS",
52 "contract",
53 "contracts",
54 ),
55 (
56 "docs/wiki/ai/CONSTRAINTS.md",
57 "CONSTRAINTS",
58 "constraint",
59 "constraints",
60 ),
61 ] {
62 let path = root.join(rel);
63 if path.exists() {
64 let content = fs::read_to_string(&path)?;
65 out.push(IngestedMemory {
66 source_key: topic.to_string(),
67 title: title.to_string(),
68 body: content.trim().to_string(),
69 mem_type: mem_type.to_string(),
70 topic_key: Some(topic.to_string()),
71 scope: Scope::Repo,
72 });
73 }
74 }
75
76 Ok(out)
77 }
78}