1use std::fs;
2use std::path::{Path, PathBuf};
3
4use libgrite_core::{
5 config::{
6 actor_dir, load_actor_config, load_repo_config, repo_sled_path, save_actor_config,
7 save_repo_config, RepoConfig,
8 },
9 types::actor::ActorConfig,
10 types::ids::{generate_actor_id, id_to_hex},
11 GriteError, GriteStore,
12};
13
14use crate::agents_md::GRITE_AGENTS_SECTION;
15use crate::context::GriteContext;
16use crate::types::{InitOptions, InitResult};
17
18#[allow(dead_code)]
20#[derive(Clone, Copy)]
21enum AgentsMdAction {
22 Created,
23 Updated,
24 Skipped,
25 Disabled,
26}
27
28#[allow(dead_code)]
29impl AgentsMdAction {
30 fn as_str(&self) -> &'static str {
31 match self {
32 AgentsMdAction::Created => "created",
33 AgentsMdAction::Updated => "updated",
34 AgentsMdAction::Skipped => "skipped",
35 AgentsMdAction::Disabled => "disabled",
36 }
37 }
38}
39
40pub fn init(opts: &InitOptions) -> Result<InitResult, GriteError> {
42 let git_dir = GriteContext::find_git_dir()?;
43
44 let (actor_id_hex, data_dir, _is_new) = match find_existing_actor(&git_dir) {
45 Some(existing_id) => {
46 let data_dir = actor_dir(&git_dir, &existing_id);
47 (existing_id, data_dir, false)
48 }
49 None => {
50 let actor_id = generate_actor_id();
51 let actor_id_hex = id_to_hex(&actor_id);
52 let data_dir = actor_dir(&git_dir, &actor_id_hex);
53
54 let actor_config = ActorConfig::new(actor_id, None);
55 save_actor_config(&data_dir, &actor_config)?;
56
57 let sled_path = repo_sled_path(&git_dir);
59 let _store = GriteStore::open_locked(&sled_path)?;
60
61 let repo_config = RepoConfig {
62 default_actor: Some(actor_id_hex.clone()),
63 ..Default::default()
64 };
65 save_repo_config(&git_dir, &repo_config)?;
66
67 (actor_id_hex, data_dir, true)
68 }
69 };
70
71 let created_agents_md = if opts.no_agents_md {
73 false
74 } else {
75 let (_agents_md_path, action) = handle_agents_md(&git_dir)?;
76 matches!(action, AgentsMdAction::Created | AgentsMdAction::Updated)
77 };
78
79 Ok(InitResult {
80 actor_id: actor_id_hex,
81 data_dir,
82 created_agents_md,
83 })
84}
85
86fn find_existing_actor(git_dir: &Path) -> Option<String> {
89 let repo_config = load_repo_config(git_dir).ok()??;
90 let actor_id = repo_config.default_actor?;
91 let data_dir = actor_dir(git_dir, &actor_id);
92 load_actor_config(&data_dir).ok()?;
93 Some(actor_id)
94}
95
96fn handle_agents_md(git_dir: &Path) -> Result<(Option<PathBuf>, AgentsMdAction), GriteError> {
98 let repo_root = git_dir
99 .parent()
100 .ok_or_else(|| GriteError::Internal("Could not determine repository root".to_string()))?;
101
102 let agents_md_path = repo_root.join("AGENTS.md");
103
104 if agents_md_path.exists() {
105 let content = fs::read_to_string(&agents_md_path)
106 .map_err(|e| GriteError::Internal(format!("Failed to read AGENTS.md: {}", e)))?;
107
108 if content.contains("## Grite") {
109 return Ok((Some(agents_md_path), AgentsMdAction::Skipped));
110 }
111
112 let new_content = if content.ends_with('\n') {
113 format!("{}\n{}", content, GRITE_AGENTS_SECTION)
114 } else {
115 format!("{}\n\n{}", content, GRITE_AGENTS_SECTION)
116 };
117
118 fs::write(&agents_md_path, new_content)
119 .map_err(|e| GriteError::Internal(format!("Failed to update AGENTS.md: {}", e)))?;
120
121 Ok((Some(agents_md_path), AgentsMdAction::Updated))
122 } else {
123 fs::write(&agents_md_path, GRITE_AGENTS_SECTION)
124 .map_err(|e| GriteError::Internal(format!("Failed to create AGENTS.md: {}", e)))?;
125
126 Ok((Some(agents_md_path), AgentsMdAction::Created))
127 }
128}