1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
//! Configuration management for AGPM
//!
//! This module provides comprehensive configuration management for the `AGent` Package Manager (AGPM).
//! It handles project manifests, global user configuration, and resource metadata with a focus on
//! security, cross-platform compatibility, and reproducible builds.
//!
//! # Architecture Overview
//!
//! AGPM uses a multi-layered configuration architecture:
//!
//! 1. **Global Configuration** (`~/.agpm/config.toml`) - User-wide settings including authentication
//! 2. **Project Manifest** (`agpm.toml`) - Project dependencies and sources
//! 3. **Lockfile** (`agpm.lock`) - Resolved versions for reproducible builds
//! 4. **Resource Metadata** - Agent and snippet configurations embedded in `.md` files
//!
//! # Modules
//!
//! - `agent` - Agent and snippet manifest structures for resource metadata
//! - `global` - Global configuration management with authentication token support
//! - `parser` - Generic TOML parsing utilities with error context
//!
//! # Configuration Files
//!
//! ## Global Configuration (`~/.agpm/config.toml`)
//!
//! **Location:**
//! - Unix/macOS: `~/.agpm/config.toml`
//! - Windows: `%LOCALAPPDATA%\agpm\config.toml`
//!
//! **Purpose:** Store user-wide settings including private repository access tokens.
//! This file is never committed to version control.
//!
//! ```toml
//! # Global sources with authentication tokens
//! [sources]
//! private = "https://oauth2:ghp_xxxxxxxxxxxx@github.com/company/private-agpm.git"
//! enterprise = "https://token:abc123@gitlab.company.com/ai/resources.git"
//! ```
//!
//! ## Project Manifest (`agpm.toml`)
//!
//! **Purpose:** Define project dependencies and public sources. Safe for version control.
//!
//! ```toml
//! [sources]
//! community = "https://github.com/aig787/agpm-community.git"
//!
//! [agents]
//! code-reviewer = { source = "community", path = "agents/code-reviewer.md", version = "v1.2.0" }
//! local-helper = { path = "../local-agents/helper.md" }
//!
//! [snippets]
//! rust-patterns = { source = "community", path = "snippets/rust.md", version = "^2.0" }
//! ```
//!
//! ## Lockfile (`agpm.lock`)
//!
//! **Purpose:** Pin exact versions for reproducible installations. Auto-generated.
//!
//! ```toml
//! # Auto-generated lockfile - DO NOT EDIT
//! version = 1
//!
//! [[sources]]
//! name = "community"
//! url = "https://github.com/aig787/agpm-community.git"
//! commit = "abc123..."
//!
//! [[agents]]
//! name = "code-reviewer"
//! source = "community"
//! version = "v1.2.0"
//! resolved_commit = "def456..."
//! checksum = "sha256:..."
//! installed_at = "agents/code-reviewer.md"
//! ```
//!
//! # Security Model
//!
//! ## Credential Isolation
//!
//! - **Global Config**: Contains authentication tokens, never committed
//! - **Project Manifest**: Public sources only, safe for version control
//! - **Source Merging**: Global sources loaded first, project sources can override
//!
//! ## Configuration Priority
//!
//! 1. Environment variables (`AGPM_CONFIG_PATH`, `AGPM_CACHE_DIR`)
//! 2. Global configuration (`~/.agpm/config.toml`)
//! 3. Project manifest (`agpm.toml`)
//! 4. Default values
//!
//! # Resource Metadata
//!
//! Agent and snippet files can include TOML frontmatter for metadata:
//!
//! ```markdown
//! +++
//! [metadata]
//! name = "rust-expert"
//! description = "Expert Rust development agent"
//! author = "AGPM Community"
//! license = "MIT"
//! keywords = ["rust", "programming", "expert"]
//!
//! [requirements]
//! agpm_version = ">=0.1.0"
//! claude_version = "latest"
//! platforms = ["windows", "macos", "linux"]
//!
//! [[requirements.dependencies]]
//! name = "code-formatter"
//! version = "^1.0"
//! type = "snippet"
//! +++
//!
//! # Rust Expert Agent
//!
//! You are an expert Rust developer...
//! ```
//!
//! # Platform Support
//!
//! This module handles cross-platform configuration paths:
//!
//! - **Windows**: Uses `%LOCALAPPDATA%` for configuration
//! - **macOS/Linux**: Uses `$HOME/.agpm` directory
//! - **Path Separators**: Normalized automatically
//! - **File Permissions**: Handles Windows vs Unix differences
//!
//! # Examples
//!
//! ## Loading Global Configuration
//!
//! ```rust,no_run
//! use agpm_cli::config::{GlobalConfig, GlobalConfigManager};
//!
//! # async fn example() -> anyhow::Result<()> {
//! // Simple load
//! let global = GlobalConfig::load().await?;
//! println!("Found {} global sources", global.sources.len());
//!
//! // Using manager for caching
//! let mut manager = GlobalConfigManager::new()?;
//! let config = manager.get().await?;
//!
//! // Add authenticated source
//! let config = manager.get_mut().await?;
//! config.add_source(
//! "private".to_string(),
//! "https://oauth2:token@github.com/company/repo.git".to_string()
//! );
//! manager.save().await?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Parsing Resource Metadata
//!
//! ```rust,no_run
//! use agpm_cli::config::{parse_config, AgentManifest};
//! use std::path::Path;
//!
//! # fn example() -> anyhow::Result<()> {
//! // Parse agent manifest from TOML file
//! let agent: AgentManifest = parse_config(Path::new("agent.toml"))?;
//!
//! println!("Agent: {} by {}",
//! agent.metadata.name,
//! agent.metadata.author);
//!
//! if let Some(requirements) = &agent.requirements {
//! println!("Requires AGPM: {:?}", requirements.agpm_version);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## Source Resolution with Authentication
//!
//! ```rust,no_run
//! use agpm_cli::config::GlobalConfig;
//! use std::collections::HashMap;
//!
//! # async fn example() -> anyhow::Result<()> {
//! let global = GlobalConfig::load().await?;
//!
//! // Project manifest sources (public)
//! let mut local_sources = HashMap::new();
//! local_sources.insert(
//! "community".to_string(),
//! "https://github.com/aig787/agpm-community.git".to_string()
//! );
//!
//! // Merge with global sources (may include auth tokens)
//! let merged = global.merge_sources(&local_sources);
//!
//! // Use merged sources for git operations
//! for (name, url) in &merged {
//! println!("Source {}: {}", name,
//! if url.contains("@") { "[authenticated]" } else { url });
//! }
//! # Ok(())
//! # }
//! ```
pub use ;
pub use ;
pub use parse_config;
// Type aliases for cleaner code
/// Type alias for agent configuration using the `AgentManifest` structure
pub type AgentConfig = AgentManifest;
/// Type alias for snippet configuration using the `SnippetManifest` structure
pub type SnippetConfig = SnippetManifest;
use Result;
use PathBuf;
/// Get the cache directory for AGPM.
///
/// Returns the directory where AGPM stores cached Git repositories and temporary files.
/// The location follows platform conventions and can be overridden with environment variables.
///
/// # Location Priority
///
/// 1. `AGPM_CACHE_DIR` environment variable (if set)
/// 2. Platform-specific cache directory:
/// - Windows: `%LOCALAPPDATA%\agpm\cache`
/// - macOS/Linux: `~/.agpm/cache`
///
/// # Directory Creation
///
/// The directory is automatically created if it doesn't exist.
///
/// # Examples
///
/// ```rust,no_run
/// use agpm_cli::config::get_cache_dir;
///
/// # fn example() -> anyhow::Result<()> {
/// let cache = get_cache_dir()?;
/// println!("Cache directory: {}", cache.display());
/// # Ok(())
/// # }
/// ```
///
/// # Errors
///
/// Returns an error if:
/// - The system cache directory cannot be determined
/// - The cache directory cannot be created
/// - Insufficient permissions for directory creation