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
// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
// SPDX-License-Identifier: MIT OR Apache-2.0
/// All errors that can originate from the `zeph-skills` crate.
///
/// Use `thiserror` source chaining to preserve the original cause where available.
///
/// # Examples
///
/// ```rust
/// use zeph_skills::SkillError;
///
/// fn check(name: &str) -> Result<(), SkillError> {
/// if name.is_empty() {
/// return Err(SkillError::Invalid("skill name must not be empty".into()));
/// }
/// Ok(())
/// }
///
/// assert!(check("").is_err());
/// assert!(check("my-skill").is_ok());
/// ```
#[derive(Debug, thiserror::Error)]
pub enum SkillError {
/// Filesystem or IO failure.
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
/// Qdrant client error (boxed to keep the variant size small).
#[cfg(feature = "qdrant")]
#[error("Qdrant error: {0}")]
Qdrant(#[from] Box<qdrant_client::QdrantError>),
/// JSON serialization or deserialization failure.
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
/// Integer type-conversion overflow, e.g. when converting `usize` to `u64`.
#[error("integer conversion: {0}")]
IntConversion(#[from] std::num::TryFromIntError),
/// `notify`/debouncer watcher initialization or watch failure.
#[error("watcher error: {0}")]
Watcher(#[from] notify::Error),
/// Skill frontmatter or content failed validation (name format, description length, etc.).
#[error("invalid skill: {0}")]
Invalid(String),
/// A skill with the given name was not found in the registry.
#[error("skill not found: {0}")]
NotFound(String),
/// A skill with the given name already exists at the target location.
#[error("skill already exists: {0}")]
AlreadyExists(String),
/// `git clone` subprocess exited non-zero or could not be spawned.
#[error("git clone failed: {0}")]
GitCloneFailed(String),
/// Filesystem copy of skill files failed.
#[error("copy failed: {0}")]
CopyFailed(String),
/// An LLM call exceeded its configured timeout.
#[error("skill generation timed out after {0}ms")]
Timeout(u64),
/// Embedding vector length does not match the expected model dimension.
///
/// Raised by [`crate::embedding::SkillEmbedding::new`] when the provided vector length
/// differs from `expected_dim`. Prevents silent zero-similarity results caused by
/// mismatched dimensions in `cosine_similarity`.
#[error("embedding dimension mismatch: expected {expected}, got {actual}")]
EmbeddingDimMismatch {
/// Dimension that was expected.
expected: usize,
/// Dimension of the provided vector.
actual: usize,
},
/// Catch-all for errors that do not fit the above categories.
#[error("{0}")]
Other(String),
}