Skip to main content

libgrite_cli/
init.rs

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/// Action taken for AGENTS.md
19#[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
40/// Initialize grite in the current repository.
41pub 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            // Initialize empty sled database with lock
58            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    // Handle AGENTS.md
72    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
86/// Return the existing default actor ID if one is already configured and its
87/// directory is present with a readable config.
88fn 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
96/// Handle AGENTS.md creation or update
97fn 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}