diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-23 15:04:14 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-23 15:19:36 +0300 |
| commit | ed11779fcaebea7cc3223c4e221ea43674057891 (patch) | |
| tree | 13f2962445f60252ef9bb77a44c2033f7f67b5bb /src/integration/mod.rs | |
| parent | 5954ece9f7e13346a9ae6e7bcabda307280c26ea (diff) | |
feat: add omp integration
Diffstat (limited to 'src/integration/mod.rs')
| -rw-r--r-- | src/integration/mod.rs | 244 |
1 files changed, 243 insertions, 1 deletions
diff --git a/src/integration/mod.rs b/src/integration/mod.rs index 8b6b854c..d6ccaa0f 100644 --- a/src/integration/mod.rs +++ b/src/integration/mod.rs @@ -13,6 +13,9 @@ pub(crate) const HERDR_PANE_ID_ENV_VAR: &str = "HERDR_PANE_ID"; const PI_EXTENSION_INSTALL_NAME: &str = "herdr-agent-state.ts"; const PI_EXTENSION_ASSET: &str = include_str!("assets/pi/herdr-agent-state.ts"); const PI_INTEGRATION_VERSION: u32 = 1; +const OMP_EXTENSION_INSTALL_NAME: &str = "herdr-omp-agent-state.ts"; +const OMP_EXTENSION_ASSET: &str = include_str!("assets/omp/herdr-agent-state.ts"); +const OMP_INTEGRATION_VERSION: u32 = 1; const PI_CODING_AGENT_DIR_ENV_VAR: &str = "PI_CODING_AGENT_DIR"; const CLAUDE_HOOK_INSTALL_NAME: &str = "herdr-agent-state.sh"; const CLAUDE_HOOK_ASSET: &str = include_str!("assets/claude/herdr-agent-state.sh"); @@ -52,6 +55,12 @@ pub(crate) struct OpenCodeInstallPaths { } #[derive(Debug)] +pub(crate) struct OmpInstallPaths { + pub extension_path: PathBuf, + pub removed_legacy_pi_extension: bool, +} + +#[derive(Debug)] pub(crate) struct HermesInstallPaths { pub plugin_dir: PathBuf, pub config_path: PathBuf, @@ -106,6 +115,12 @@ pub(crate) struct PiUninstallResult { } #[derive(Debug)] +pub(crate) struct OmpUninstallResult { + pub extension_path: PathBuf, + pub removed_extension: bool, +} + +#[derive(Debug)] pub(crate) struct ClaudeUninstallResult { pub hook_path: PathBuf, pub settings_path: PathBuf, @@ -149,6 +164,24 @@ pub(crate) fn install_target( let path = install_pi()?; vec![format!("installed pi integration to {}", path.display())] } + crate::api::schema::IntegrationTarget::Omp => { + let installed = install_omp()?; + let mut messages = Vec::new(); + if installed.removed_legacy_pi_extension { + messages.push(format!( + "removed legacy pi integration from omp extension directory at {}", + installed + .extension_path + .with_file_name(PI_EXTENSION_INSTALL_NAME) + .display() + )); + } + messages.push(format!( + "installed omp integration to {}", + installed.extension_path.display() + )); + messages + } crate::api::schema::IntegrationTarget::Claude => { let installed = install_claude()?; vec![ @@ -220,6 +253,20 @@ pub(crate) fn uninstall_target( )] } } + crate::api::schema::IntegrationTarget::Omp => { + let result = uninstall_omp()?; + if result.removed_extension { + vec![format!( + "removed omp integration extension at {}", + result.extension_path.display() + )] + } else { + vec![format!( + "no omp integration extension found at {}", + result.extension_path.display() + )] + } + } crate::api::schema::IntegrationTarget::Claude => { let result = uninstall_claude()?; let mut messages = Vec::new(); @@ -330,6 +377,7 @@ pub(crate) fn integration_target_label( ) -> &'static str { match target { crate::api::schema::IntegrationTarget::Pi => "pi", + crate::api::schema::IntegrationTarget::Omp => "omp", crate::api::schema::IntegrationTarget::Claude => "claude", crate::api::schema::IntegrationTarget::Codex => "codex", crate::api::schema::IntegrationTarget::Opencode => "opencode", @@ -340,6 +388,7 @@ pub(crate) fn integration_target_label( fn integration_target_command(target: crate::api::schema::IntegrationTarget) -> &'static str { match target { crate::api::schema::IntegrationTarget::Pi => "pi", + crate::api::schema::IntegrationTarget::Omp => "omp", crate::api::schema::IntegrationTarget::Claude => "claude", crate::api::schema::IntegrationTarget::Codex => "codex", crate::api::schema::IntegrationTarget::Opencode => "opencode", @@ -417,7 +466,7 @@ fn integration_specs() -> [( crate::api::schema::IntegrationTarget, io::Result<PathBuf>, u32, -); 5] { +); 6] { [ ( crate::api::schema::IntegrationTarget::Pi, @@ -425,6 +474,11 @@ fn integration_specs() -> [( PI_INTEGRATION_VERSION, ), ( + crate::api::schema::IntegrationTarget::Omp, + omp_extension_dir().map(|dir| dir.join(OMP_EXTENSION_INSTALL_NAME)), + OMP_INTEGRATION_VERSION, + ), + ( crate::api::schema::IntegrationTarget::Claude, claude_dir().map(|dir| dir.join("hooks").join(CLAUDE_HOOK_INSTALL_NAME)), CLAUDE_INTEGRATION_VERSION, @@ -546,6 +600,39 @@ pub(crate) fn install_pi() -> io::Result<PathBuf> { Ok(path) } +pub(crate) fn install_omp() -> io::Result<OmpInstallPaths> { + let dir = omp_extension_dir()?; + if !dir.is_dir() { + return Err(io::Error::other(format!( + "omp extension directory not found at {}. install omp and create the extensions directory first", + dir.display() + ))); + } + + let removed_legacy_pi_extension = remove_legacy_pi_extension_from_omp_dir(&dir)?; + let extension_path = dir.join(OMP_EXTENSION_INSTALL_NAME); + fs::write(&extension_path, OMP_EXTENSION_ASSET)?; + Ok(OmpInstallPaths { + extension_path, + removed_legacy_pi_extension, + }) +} + +fn remove_legacy_pi_extension_from_omp_dir(dir: &Path) -> io::Result<bool> { + let legacy_path = dir.join(PI_EXTENSION_INSTALL_NAME); + if !legacy_path.is_file() { + return Ok(false); + } + + let content = fs::read_to_string(&legacy_path)?; + if content.contains("HERDR_INTEGRATION_ID=pi") { + fs::remove_file(legacy_path)?; + return Ok(true); + } + + Ok(false) +} + pub(crate) fn install_claude() -> io::Result<ClaudeInstallPaths> { let dir = claude_dir()?; if !dir.is_dir() { @@ -790,6 +877,16 @@ pub(crate) fn uninstall_pi() -> io::Result<PiUninstallResult> { }) } +pub(crate) fn uninstall_omp() -> io::Result<OmpUninstallResult> { + let extension_path = omp_extension_dir()?.join(OMP_EXTENSION_INSTALL_NAME); + let removed_extension = remove_file_if_exists(&extension_path)?; + + Ok(OmpUninstallResult { + extension_path, + removed_extension, + }) +} + pub(crate) fn uninstall_claude() -> io::Result<ClaudeUninstallResult> { let hook_path = claude_dir()?.join("hooks").join(CLAUDE_HOOK_INSTALL_NAME); let settings_path = claude_dir()?.join("settings.json"); @@ -1355,6 +1452,13 @@ fn pi_extension_dir() -> io::Result<PathBuf> { ) } +fn omp_extension_dir() -> io::Result<PathBuf> { + Ok( + config_dir_from_env_or_home(PI_CODING_AGENT_DIR_ENV_VAR, &[".omp", "agent"])? + .join("extensions"), + ) +} + fn claude_dir() -> io::Result<PathBuf> { config_dir_from_env_or_home(CLAUDE_CONFIG_DIR_ENV_VAR, &[".claude"]) } @@ -1554,6 +1658,144 @@ mod tests { } #[test] + fn install_omp_writes_embedded_asset_to_omp_extensions_dir() { + let _lock = integration_env_lock(); + let base = unique_base(); + let home = base.join("home"); + let ext_dir = home.join(".omp/agent/extensions"); + fs::create_dir_all(&ext_dir).unwrap(); + std::env::set_var("HOME", &home); + + let installed = install_omp().unwrap(); + let content = fs::read_to_string(&installed.extension_path).unwrap(); + + assert_eq!( + installed.extension_path, + ext_dir.join(OMP_EXTENSION_INSTALL_NAME) + ); + assert!(!installed.removed_legacy_pi_extension); + assert_eq!(content, OMP_EXTENSION_ASSET); + + std::env::remove_var("HOME"); + let _ = fs::remove_dir_all(base); + } + + #[test] + fn install_omp_removes_legacy_pi_integration_from_omp_extensions_dir() { + let _lock = integration_env_lock(); + let base = unique_base(); + let home = base.join("home"); + let ext_dir = home.join(".omp/agent/extensions"); + fs::create_dir_all(&ext_dir).unwrap(); + let legacy_path = ext_dir.join(PI_EXTENSION_INSTALL_NAME); + fs::write(&legacy_path, PI_EXTENSION_ASSET).unwrap(); + std::env::set_var("HOME", &home); + + let installed = install_omp().unwrap(); + + assert_eq!( + installed.extension_path, + ext_dir.join(OMP_EXTENSION_INSTALL_NAME) + ); + assert!(installed.removed_legacy_pi_extension); + assert!(!legacy_path.exists()); + + std::env::remove_var("HOME"); + let _ = fs::remove_dir_all(base); + } + + #[test] + fn install_omp_preserves_non_herdr_file_with_pi_install_name() { + let _lock = integration_env_lock(); + let base = unique_base(); + let home = base.join("home"); + let ext_dir = home.join(".omp/agent/extensions"); + fs::create_dir_all(&ext_dir).unwrap(); + let user_path = ext_dir.join(PI_EXTENSION_INSTALL_NAME); + fs::write(&user_path, "// user extension\n").unwrap(); + std::env::set_var("HOME", &home); + + let installed = install_omp().unwrap(); + + assert_eq!( + installed.extension_path, + ext_dir.join(OMP_EXTENSION_INSTALL_NAME) + ); + assert!(!installed.removed_legacy_pi_extension); + assert_eq!( + fs::read_to_string(user_path).unwrap(), + "// user extension\n" + ); + + std::env::remove_var("HOME"); + let _ = fs::remove_dir_all(base); + } + + #[test] + fn install_omp_uses_pi_coding_agent_dir_env() { + let _lock = integration_env_lock(); + let base = unique_base(); + let agent_dir = base.join("custom-omp-agent"); + let ext_dir = agent_dir.join("extensions"); + fs::create_dir_all(&ext_dir).unwrap(); + std::env::set_var(PI_CODING_AGENT_DIR_ENV_VAR, &agent_dir); + + let installed = install_omp().unwrap(); + + assert_eq!( + installed.extension_path, + ext_dir.join(OMP_EXTENSION_INSTALL_NAME) + ); + assert!(!installed.removed_legacy_pi_extension); + + clear_integration_path_env(); + let _ = fs::remove_dir_all(base); + } + + #[test] + fn uninstall_omp_removes_embedded_extension_when_present() { + let _lock = integration_env_lock(); + let base = unique_base(); + let home = base.join("home"); + let ext_dir = home.join(".omp/agent/extensions"); + fs::create_dir_all(&ext_dir).unwrap(); + fs::write( + ext_dir.join(OMP_EXTENSION_INSTALL_NAME), + OMP_EXTENSION_ASSET, + ) + .unwrap(); + std::env::set_var("HOME", &home); + + let result = uninstall_omp().unwrap(); + + assert_eq!( + result.extension_path, + ext_dir.join(OMP_EXTENSION_INSTALL_NAME) + ); + assert!(result.removed_extension); + assert!(!result.extension_path.exists()); + + std::env::remove_var("HOME"); + let _ = fs::remove_dir_all(base); + } + + #[test] + fn install_omp_errors_when_extension_dir_missing() { + let _lock = integration_env_lock(); + let base = unique_base(); + let home = base.join("home"); + fs::create_dir_all(&home).unwrap(); + std::env::set_var("HOME", &home); + + let err = install_omp().unwrap_err().to_string(); + + assert!(err.contains("omp extension directory not found")); + + std::env::remove_var("HOME"); + let _ = fs::remove_dir_all(base); + } + + #[test] fn uninstall_pi_removes_embedded_extension_when_present() { let _lock = integration_env_lock(); let base = unique_base(); |
