use std::path::Path;
use anyhow::{Context, Result};
const MARKER: &str = "<!-- mati:vector-c -->";
const END_MARKER: &str = "<!-- /mati:vector-c -->";
const VECTOR_C_BODY: &str = "\
## mati context store
This project uses mati. Before reading any file, call mem_get(\"file:<path>\").
High-confidence records (confidence >= 0.6, confirmed=true) replace file reads.
The PreToolUse hook enforces this at the environment level.
Run `mati status` to see current knowledge health.
## /mati-enrich
Run `/mati-enrich [path]` to enrich files into the store. The full
four-stage extraction pipeline lives in the `/mati-enrich` command
and loads only when you run it — never injected into every session.
";
fn vector_c_stub() -> String {
format!("{MARKER}\n{VECTOR_C_BODY}\n{END_MARKER}\n")
}
pub fn write_claude_md_stub(project_root: &Path) -> Result<WriteResult> {
let claude_dir = project_root.join(".claude");
if !claude_dir.is_dir() {
return Ok(WriteResult::NoClaude);
}
let path = claude_dir.join("CLAUDE.md");
let stub = vector_c_stub();
if path.exists() {
let content = std::fs::read_to_string(&path)
.with_context(|| format!("failed to read {}", path.display()))?;
if let Some(start) = content.find(MARKER) {
let updated = if let Some(end_rel) = content[start..].find(END_MARKER) {
let end = start + end_rel + END_MARKER.len();
let mut next = String::with_capacity(content.len() + stub.len());
next.push_str(&content[..start]);
next.push_str(&stub);
if content[end..].starts_with('\n') {
next.push_str(&content[end + 1..]);
} else {
next.push_str(&content[end..]);
}
next
} else {
let mut next = String::with_capacity(content.len() + stub.len());
next.push_str(&content[..start]);
next.push_str(&stub);
next
};
if updated == content {
return Ok(WriteResult::AlreadyPresent);
}
std::fs::write(&path, updated)
.with_context(|| format!("failed to write {}", path.display()))?;
return Ok(WriteResult::Updated);
}
let mut appended = content;
if !appended.ends_with('\n') {
appended.push('\n');
}
appended.push('\n');
appended.push_str(&stub);
std::fs::write(&path, appended)
.with_context(|| format!("failed to write {}", path.display()))?;
Ok(WriteResult::Appended)
} else {
std::fs::write(&path, &stub)
.with_context(|| format!("failed to write {}", path.display()))?;
Ok(WriteResult::Created)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WriteResult {
Created,
Appended,
Updated,
AlreadyPresent,
NoClaude,
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn creates_file_when_claude_dir_exists() {
let dir = TempDir::new().unwrap();
std::fs::create_dir_all(dir.path().join(".claude")).unwrap();
let result = write_claude_md_stub(dir.path()).unwrap();
assert_eq!(result, WriteResult::Created);
let content = std::fs::read_to_string(dir.path().join(".claude/CLAUDE.md")).unwrap();
assert!(content.contains(MARKER));
assert!(content.contains(END_MARKER));
assert!(content.contains("mem_get(\"file:<path>\")"));
assert!(content.contains("PreToolUse hook enforces this"));
assert!(!content.contains("## mati Policy Capture"));
assert!(!content.contains("## mati Knowledge Capture"));
assert!(!content.contains("### Stage 1 — Setup"));
assert!(content.contains("loads only when you run it"));
}
#[test]
fn skips_when_no_claude_dir() {
let dir = TempDir::new().unwrap();
assert!(!dir.path().join(".claude").exists());
let result = write_claude_md_stub(dir.path()).unwrap();
assert_eq!(result, WriteResult::NoClaude);
assert!(!dir.path().join(".claude").exists());
}
#[test]
fn appends_to_existing_file_without_marker() {
let dir = TempDir::new().unwrap();
let claude_dir = dir.path().join(".claude");
std::fs::create_dir_all(&claude_dir).unwrap();
let existing = "# My Project\n\nExisting instructions.\n";
std::fs::write(claude_dir.join("CLAUDE.md"), existing).unwrap();
let result = write_claude_md_stub(dir.path()).unwrap();
assert_eq!(result, WriteResult::Appended);
let content = std::fs::read_to_string(claude_dir.join("CLAUDE.md")).unwrap();
assert!(content.starts_with("# My Project"));
assert!(content.contains(MARKER));
assert!(content.contains("Existing instructions."));
}
#[test]
fn idempotent_on_rerun() {
let dir = TempDir::new().unwrap();
std::fs::create_dir_all(dir.path().join(".claude")).unwrap();
let first = write_claude_md_stub(dir.path()).unwrap();
assert_eq!(first, WriteResult::Created);
let second = write_claude_md_stub(dir.path()).unwrap();
assert_eq!(second, WriteResult::AlreadyPresent);
let content = std::fs::read_to_string(dir.path().join(".claude/CLAUDE.md")).unwrap();
let marker_count = content.matches(MARKER).count();
assert_eq!(marker_count, 1);
}
#[test]
fn appended_stub_has_blank_line_separator() {
let dir = TempDir::new().unwrap();
let claude_dir = dir.path().join(".claude");
std::fs::create_dir_all(&claude_dir).unwrap();
std::fs::write(claude_dir.join("CLAUDE.md"), "# Title\n").unwrap();
write_claude_md_stub(dir.path()).unwrap();
let content = std::fs::read_to_string(claude_dir.join("CLAUDE.md")).unwrap();
assert!(content.contains("# Title\n\n<!-- mati:vector-c -->"));
}
#[test]
fn updates_existing_legacy_stub_block() {
let dir = TempDir::new().unwrap();
let claude_dir = dir.path().join(".claude");
std::fs::create_dir_all(&claude_dir).unwrap();
let legacy = format!("{MARKER}\n## old mati block\nstale instructions\n");
std::fs::write(claude_dir.join("CLAUDE.md"), legacy).unwrap();
let result = write_claude_md_stub(dir.path()).unwrap();
assert_eq!(result, WriteResult::Updated);
let content = std::fs::read_to_string(claude_dir.join("CLAUDE.md")).unwrap();
assert!(content.contains("## mati context store"));
assert!(content.contains(END_MARKER));
assert!(!content.contains("## old mati block"));
}
}