1use crate::settings::ProjectTrustDefault;
5use serde::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7use std::path::{Path, PathBuf};
8
9#[derive(Debug, Default, Serialize, Deserialize)]
10struct TrustFile {
11 #[serde(flatten)]
12 decisions: BTreeMap<String, bool>,
13}
14
15pub fn trust_file_path() -> Option<PathBuf> {
16 dirs::home_dir().map(|h| h.join(".kiss/agent/trust.json"))
17}
18
19fn read_trust_file() -> TrustFile {
20 trust_file_path()
21 .and_then(|p| std::fs::read_to_string(p).ok())
22 .and_then(|t| serde_json::from_str(&t).ok())
23 .unwrap_or_default()
24}
25
26pub fn has_project_resources(cwd: &Path) -> bool {
29 cwd.join(".kiss").is_dir() || cwd.join(".agents/skills").is_dir()
30}
31
32pub fn saved_decision(cwd: &Path) -> Option<bool> {
34 let file = read_trust_file();
35 for dir in cwd.ancestors() {
36 if let Some(&decision) = file.decisions.get(&dir.display().to_string()) {
37 return Some(decision);
38 }
39 }
40 None
41}
42
43pub fn save_decision(path: &Path, trusted: bool) -> anyhow::Result<()> {
44 let file_path = trust_file_path().ok_or_else(|| anyhow::anyhow!("no home directory"))?;
45 if let Some(parent) = file_path.parent() {
46 std::fs::create_dir_all(parent)?;
47 }
48 let mut file = read_trust_file();
49 file.decisions.insert(path.display().to_string(), trusted);
50 std::fs::write(&file_path, serde_json::to_string_pretty(&file)?)?;
51 Ok(())
52}
53
54pub fn resolve_non_interactive(
56 cwd: &Path,
57 cli_override: Option<bool>,
58 default: ProjectTrustDefault,
59) -> bool {
60 if let Some(explicit) = cli_override {
61 return explicit;
62 }
63 if !has_project_resources(cwd) {
64 return true; }
66 if let Some(saved) = saved_decision(cwd) {
67 return saved;
68 }
69 matches!(default, ProjectTrustDefault::Always)
70}