1use snafu::Snafu;
2use std::path::PathBuf;
3
4#[derive(Debug, Snafu)]
5#[snafu(visibility(pub), module(chronicle_error))]
6pub enum ChronicleError {
7 #[snafu(display("not a git repository: {}", path.display()))]
8 NotARepository {
9 path: PathBuf,
10 #[snafu(implicit)]
11 location: snafu::Location,
12 },
13
14 #[snafu(display("chronicle not initialized (run `git chronicle init` first)"))]
15 NotInitialized {
16 #[snafu(implicit)]
17 location: snafu::Location,
18 },
19
20 #[snafu(display("git error: {source}"))]
21 Git {
22 source: GitError,
23 #[snafu(implicit)]
24 location: snafu::Location,
25 },
26
27 #[snafu(display("provider error: {source}"))]
28 Provider {
29 source: ProviderError,
30 #[snafu(implicit)]
31 location: snafu::Location,
32 },
33
34 #[snafu(display("agent error: {source}"))]
35 Agent {
36 source: AgentError,
37 #[snafu(implicit)]
38 location: snafu::Location,
39 },
40
41 #[snafu(display("config error: {message}"))]
42 Config {
43 message: String,
44 #[snafu(implicit)]
45 location: snafu::Location,
46 },
47
48 #[snafu(display("IO error: {source}"))]
49 Io {
50 source: std::io::Error,
51 #[snafu(implicit)]
52 location: snafu::Location,
53 },
54
55 #[snafu(display("JSON error: {source}"))]
56 Json {
57 source: serde_json::Error,
58 #[snafu(implicit)]
59 location: snafu::Location,
60 },
61
62 #[snafu(display("annotation validation error: {message}"))]
63 Validation {
64 message: String,
65 #[snafu(implicit)]
66 location: snafu::Location,
67 },
68
69 #[snafu(display("setup error: {source}"))]
70 Setup {
71 source: SetupError,
72 #[snafu(implicit)]
73 location: snafu::Location,
74 },
75}
76
77#[derive(Debug, Snafu)]
78#[snafu(visibility(pub), module(git_error))]
79pub enum GitError {
80 #[snafu(display("git command failed: {message}"))]
81 CommandFailed {
82 message: String,
83 #[snafu(implicit)]
84 location: snafu::Location,
85 },
86
87 #[snafu(display("commit not found: {sha}"))]
88 CommitNotFound {
89 sha: String,
90 #[snafu(implicit)]
91 location: snafu::Location,
92 },
93
94 #[snafu(display("file not found: {path} at {commit}"))]
95 FileNotFound {
96 path: String,
97 commit: String,
98 #[snafu(implicit)]
99 location: snafu::Location,
100 },
101
102 #[snafu(display("notes ref missing: {refname}"))]
103 NotesRefMissing {
104 refname: String,
105 #[snafu(implicit)]
106 location: snafu::Location,
107 },
108
109 #[snafu(display("diff parse error: {message}"))]
110 DiffParse {
111 message: String,
112 #[snafu(implicit)]
113 location: snafu::Location,
114 },
115
116 #[snafu(display("IO error: {source}"))]
117 Io {
118 source: std::io::Error,
119 #[snafu(implicit)]
120 location: snafu::Location,
121 },
122}
123
124#[derive(Debug, Snafu)]
125#[snafu(visibility(pub), module(provider_error))]
126pub enum ProviderError {
127 #[snafu(display("no credentials found for any provider"))]
128 NoCredentials {
129 #[snafu(implicit)]
130 location: snafu::Location,
131 },
132
133 #[snafu(display("authentication failed: {message}"))]
134 AuthFailed {
135 message: String,
136 #[snafu(implicit)]
137 location: snafu::Location,
138 },
139
140 #[snafu(display("rate limited, retry after {retry_after_secs}s"))]
141 RateLimited {
142 retry_after_secs: u64,
143 #[snafu(implicit)]
144 location: snafu::Location,
145 },
146
147 #[snafu(display("request timeout"))]
148 Timeout {
149 #[snafu(implicit)]
150 location: snafu::Location,
151 },
152
153 #[snafu(display("API error: {message}"))]
154 Api {
155 message: String,
156 #[snafu(implicit)]
157 location: snafu::Location,
158 },
159
160 #[snafu(display("failed to parse response: {message}"))]
161 ParseResponse {
162 message: String,
163 #[snafu(implicit)]
164 location: snafu::Location,
165 },
166
167 #[snafu(display("HTTP error: {source}, at {location}"))]
168 Http {
169 source: Box<ureq::Error>,
170 #[snafu(implicit)]
171 location: snafu::Location,
172 },
173
174 #[snafu(display("retries exhausted after {attempts} attempts"))]
175 RetriesExhausted {
176 attempts: u32,
177 #[snafu(implicit)]
178 location: snafu::Location,
179 },
180}
181
182#[derive(Debug, Snafu)]
183#[snafu(visibility(pub), module(agent_error))]
184pub enum AgentError {
185 #[snafu(display("provider error: {source}"))]
186 Provider {
187 source: ProviderError,
188 #[snafu(implicit)]
189 location: snafu::Location,
190 },
191
192 #[snafu(display("no annotations emitted by agent"))]
193 NoAnnotations {
194 #[snafu(implicit)]
195 location: snafu::Location,
196 },
197
198 #[snafu(display("max turns exceeded ({turns})"))]
199 MaxTurnsExceeded {
200 turns: u32,
201 #[snafu(implicit)]
202 location: snafu::Location,
203 },
204
205 #[snafu(display("invalid annotation: {message}"))]
206 InvalidAnnotation {
207 message: String,
208 #[snafu(implicit)]
209 location: snafu::Location,
210 },
211
212 #[snafu(display("git error: {source}"))]
213 Git {
214 source: GitError,
215 #[snafu(implicit)]
216 location: snafu::Location,
217 },
218
219 #[snafu(display("JSON error: {source}"))]
220 Json {
221 source: serde_json::Error,
222 #[snafu(implicit)]
223 location: snafu::Location,
224 },
225}
226
227#[derive(Debug, Snafu)]
228#[snafu(visibility(pub), module(setup_error))]
229pub enum SetupError {
230 #[snafu(display("home directory not found, at {location}"))]
231 NoHomeDirectory {
232 #[snafu(implicit)]
233 location: snafu::Location,
234 },
235
236 #[snafu(display("git-chronicle binary not found on PATH, at {location}"))]
237 BinaryNotFound {
238 #[snafu(implicit)]
239 location: snafu::Location,
240 },
241
242 #[snafu(display("failed to write {path}: {source}, at {location}"))]
243 WriteFile {
244 path: String,
245 #[snafu(source)]
246 source: std::io::Error,
247 #[snafu(implicit)]
248 location: snafu::Location,
249 },
250
251 #[snafu(display("failed to read file {path}: {source}, at {location}"))]
252 ReadFile {
253 path: String,
254 #[snafu(source)]
255 source: std::io::Error,
256 #[snafu(implicit)]
257 location: snafu::Location,
258 },
259
260 #[snafu(display("failed to read user config: {source}, at {location}"))]
261 ReadConfig {
262 #[snafu(source)]
263 source: toml::de::Error,
264 #[snafu(implicit)]
265 location: snafu::Location,
266 },
267
268 #[snafu(display("failed to write user config: {source}, at {location}"))]
269 WriteConfig {
270 #[snafu(source)]
271 source: toml::ser::Error,
272 #[snafu(implicit)]
273 location: snafu::Location,
274 },
275
276 #[snafu(display(
277 "Claude CLI not found — install Claude Code or select a different provider, at {location}"
278 ))]
279 ClaudeCliNotFound {
280 #[snafu(implicit)]
281 location: snafu::Location,
282 },
283
284 #[snafu(display("ANTHROPIC_API_KEY environment variable not set, at {location}"))]
285 ApiKeyNotSet {
286 #[snafu(implicit)]
287 location: snafu::Location,
288 },
289
290 #[snafu(display("interactive input error: {source}, at {location}"))]
291 InteractiveInput {
292 #[snafu(source)]
293 source: std::io::Error,
294 #[snafu(implicit)]
295 location: snafu::Location,
296 },
297}
298
299pub type Result<T, E = ChronicleError> = std::result::Result<T, E>;