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