mati_core/scaffold/
mod.rs1use std::path::Path;
5
6use anyhow::Result;
7
8pub mod claude_md;
9pub mod codex;
10pub mod commands;
11pub mod settings;
12pub mod skills;
13
14pub use claude_md::write_claude_md_stub;
15pub use codex::install_codex;
16pub use commands::write_mati_enrich_command;
17pub use settings::install_hooks;
18pub use skills::write_capture_skills;
19
20pub fn mati_binary_path() -> String {
25 std::env::current_exe()
26 .ok()
27 .and_then(|p| p.canonicalize().ok())
28 .map(|p| p.to_string_lossy().into_owned())
29 .unwrap_or_else(|| "mati".to_owned())
30}
31
32pub fn write_mati_wrapper(hooks_dir: &Path) -> Result<()> {
38 let bin = mati_binary_path();
39 let content = format!(
40 "#!/usr/bin/env bash\n\
41 # mati binary wrapper — written by mati init.\n\
42 # Ensures hooks use the same binary as the MCP server.\n\
43 # DO NOT EDIT — regenerated on each mati init.\n\
44 [ -x \"{bin}\" ] || exit 0\n\
45 exec \"{bin}\" \"$@\"\n"
46 );
47 let path = hooks_dir.join("mati");
48 write_if_changed(&path, &content)?;
49 make_executable(&path)?;
50 Ok(())
51}
52
53pub(crate) fn write_if_changed(path: &Path, content: &str) -> Result<()> {
54 if path.exists() {
55 if let Ok(existing) = std::fs::read_to_string(path) {
56 if existing == content {
57 return Ok(());
58 }
59 }
60 }
61 std::fs::write(path, content)?;
62 Ok(())
63}
64
65#[cfg(unix)]
66pub(crate) fn make_executable(path: &Path) -> Result<()> {
67 use std::os::unix::fs::PermissionsExt;
68 let mut perms = std::fs::metadata(path)?.permissions();
69 perms.set_mode(0o755);
70 std::fs::set_permissions(path, perms)?;
71 Ok(())
72}
73
74#[cfg(not(unix))]
75pub(crate) fn make_executable(_path: &Path) -> Result<()> {
76 Ok(())
77}