use std::path::PathBuf;
use thiserror::Error;
#[allow(dead_code)]
#[derive(Debug, Error)]
pub enum CliError {
#[error(".omne/ not found — not an omne volume")]
NotAVolume,
#[error(".omne/ already exists at {path}")]
VolumeAlreadyExists { path: PathBuf },
#[error("validation failed with {} issue(s)", issues.len())]
ValidationFailed { issues: Vec<String> },
#[error(transparent)]
Distro(#[from] crate::distro::Error),
#[error(transparent)]
Manifest(#[from] crate::manifest::Error),
#[error(transparent)]
Github(#[from] crate::github::Error),
#[error(transparent)]
Tarball(#[from] crate::tarball::Error),
#[error("tarball layout mismatch: expected '{expected}/' but found {found:?}")]
TarballLayoutMismatch {
expected: String,
found: Vec<String>,
},
#[error(transparent)]
Python(#[from] crate::python::Error),
#[error("unsafe target: {path} contains or is a symlink — refusing to remove")]
UnsafeTarget { path: PathBuf },
#[error(
"symlink privilege required on Windows\n\
\n\
Creating .claude/skills/ requires directory symlinks. Windows\n\
blocks this for non-elevated processes unless Developer Mode\n\
is enabled. Re-run `omne init` one of these ways:\n\
\n\
• Elevated PowerShell: right-click → Run as Administrator\n\
• Enable Developer Mode: Settings → Privacy & Security → For developers"
)]
SymlinkPrivilegeRequired,
#[error("ULID allocator lock {path} timed out")]
UlidLockTimeout { path: PathBuf },
#[error(transparent)]
Ulid(crate::ulid::Error),
#[error(transparent)]
Worktree(#[from] crate::worktree::Error),
#[error(transparent)]
EventLog(#[from] crate::event_log::Error),
#[error(transparent)]
ClaudeProc(#[from] crate::claude_proc::Error),
#[error(transparent)]
Dispatch(#[from] crate::executor::DispatchError),
#[error("{0}")]
Io(String),
#[error("run not found: {0}")]
RunNotFound(String),
#[error("pipe {run_id} aborted: {reason}")]
PipeAborted { run_id: String, reason: String },
}
impl From<crate::ulid::Error> for CliError {
fn from(err: crate::ulid::Error) -> Self {
match err {
crate::ulid::Error::LockTimeout { path } => CliError::UlidLockTimeout { path },
other => CliError::Ulid(other),
}
}
}
impl From<std::io::Error> for CliError {
fn from(err: std::io::Error) -> Self {
CliError::Io(err.to_string())
}
}
impl CliError {
pub fn exit_code(&self) -> i32 {
match self {
CliError::NotAVolume
| CliError::VolumeAlreadyExists { .. }
| CliError::ValidationFailed { .. }
| CliError::Distro(_)
| CliError::Manifest(_)
| CliError::Github(_)
| CliError::Tarball(_)
| CliError::TarballLayoutMismatch { .. }
| CliError::Python(_)
| CliError::UnsafeTarget { .. }
| CliError::UlidLockTimeout { .. }
| CliError::Ulid(_)
| CliError::Worktree(_)
| CliError::EventLog(_)
| CliError::ClaudeProc(_)
| CliError::Dispatch(_)
| CliError::Io(_)
| CliError::RunNotFound(_)
| CliError::PipeAborted { .. } => 1,
CliError::SymlinkPrivilegeRequired => 7,
}
}
}