aboutsummaryrefslogtreecommitdiff
path: root/src/integration/mod.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-23 15:04:14 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-23 15:19:36 +0300
commited11779fcaebea7cc3223c4e221ea43674057891 (patch)
tree13f2962445f60252ef9bb77a44c2033f7f67b5bb /src/integration/mod.rs
parent5954ece9f7e13346a9ae6e7bcabda307280c26ea (diff)
feat: add omp integration
Diffstat (limited to 'src/integration/mod.rs')
-rw-r--r--src/integration/mod.rs244
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();