pub trait Integration: Send + Sync {
// Required methods
fn id(&self) -> &'static str;
fn display_name(&self) -> &'static str;
fn supported_scopes(&self) -> &'static [ScopeKind];
fn status(
&self,
scope: &Scope,
tag: &str,
) -> Result<StatusReport, AgentConfigError>;
fn plan_install(
&self,
scope: &Scope,
spec: &HookSpec,
) -> Result<InstallPlan, AgentConfigError>;
fn plan_uninstall(
&self,
scope: &Scope,
tag: &str,
) -> Result<UninstallPlan, AgentConfigError>;
fn install(
&self,
scope: &Scope,
spec: &HookSpec,
) -> Result<InstallReport, AgentConfigError>;
fn uninstall(
&self,
scope: &Scope,
tag: &str,
) -> Result<UninstallReport, AgentConfigError>;
// Provided methods
fn is_installed(
&self,
scope: &Scope,
tag: &str,
) -> Result<bool, AgentConfigError> { ... }
fn validate(
&self,
scope: &Scope,
tag: &str,
) -> Result<ValidationReport, AgentConfigError> { ... }
fn migrate(
&self,
_scope: &Scope,
_tag: &str,
) -> Result<MigrationReport, AgentConfigError> { ... }
}Expand description
One AI harness’s hook installer.
The trait is intentionally narrow so adding a new harness is a small,
mechanical exercise: implement Integration, then register it in
crate::registry::all.
All operations must be idempotent: calling install twice with the same
spec, or uninstall twice with the same tag, must produce the same end
state as calling once.
Required Methods§
Sourcefn display_name(&self) -> &'static str
fn display_name(&self) -> &'static str
Human-readable name (e.g., "Claude Code").
Sourcefn supported_scopes(&self) -> &'static [ScopeKind]
fn supported_scopes(&self) -> &'static [ScopeKind]
Which scopes this integration accepts.
Sourcefn status(
&self,
scope: &Scope,
tag: &str,
) -> Result<StatusReport, AgentConfigError>
fn status( &self, scope: &Scope, tag: &str, ) -> Result<StatusReport, AgentConfigError>
Detailed installation state for the hook identified by tag.
Distinguishes installed-by-us from installed-by-someone-else, surfaces
drift (parse failures, duplicate entries), and reports any pending
.bak files. See StatusReport for the full shape.
§Errors
AgentConfigError::PathResolutionwhen the harness config path cannot be resolved (e.g.$HOMEmissing).AgentConfigError::Iofor unreadable files other than the “missing” case (which becomesInstallStatus::Absent).AgentConfigError::ConfigTooLargewhen the config file exceeds the 8 MiB read cap.
Parse failures are intentionally folded into a StatusReport with
crate::status::DriftIssue::InvalidConfig rather than surfaced as
errors.
Sourcefn plan_install(
&self,
scope: &Scope,
spec: &HookSpec,
) -> Result<InstallPlan, AgentConfigError>
fn plan_install( &self, scope: &Scope, spec: &HookSpec, ) -> Result<InstallPlan, AgentConfigError>
Plan a hook install without mutating user files.
§Errors
AgentConfigError::PathResolutionwhen the target config path cannot be resolved or escapes the scope root.AgentConfigError::UnsupportedScopeis encoded ascrate::plan::PlanStatus::Refusedrather than returned.AgentConfigError::Io/AgentConfigError::ConfigTooLargewhen the existing config cannot be read.AgentConfigError::InvalidTagfrom spec re-validation.
Sourcefn plan_uninstall(
&self,
scope: &Scope,
tag: &str,
) -> Result<UninstallPlan, AgentConfigError>
fn plan_uninstall( &self, scope: &Scope, tag: &str, ) -> Result<UninstallPlan, AgentConfigError>
Plan a hook uninstall without mutating user files.
§Errors
Same envelope as plan_install.
Predictable refusals (unsupported scope, owner mismatch) are encoded
as crate::plan::PlanStatus::Refused.
Sourcefn install(
&self,
scope: &Scope,
spec: &HookSpec,
) -> Result<InstallReport, AgentConfigError>
fn install( &self, scope: &Scope, spec: &HookSpec, ) -> Result<InstallReport, AgentConfigError>
Install the hook. Repeated calls with the same spec.tag are a no-op
after the first (the on-disk state is reached, then preserved).
§Errors
AgentConfigError::PathResolutionwhen the target path escapes the scope root or contains a symlink component.AgentConfigError::UnsupportedScopewhen the scope is not insupported_scopes.AgentConfigError::Iofor filesystem failures, including permission denials and atomic-rename collisions.AgentConfigError::JsonInvalid/AgentConfigError::TomlInvalidwhen the existing config is unparseable and cannot be merged.AgentConfigError::ConfigTooLargewhen the existing config exceeds the 8 MiB read cap.AgentConfigError::BackupExistswhen a sibling.bakalready exists for a file we would otherwise back up.AgentConfigError::MissingSpecField/AgentConfigError::InvalidTagfrom spec re-validation.
Sourcefn uninstall(
&self,
scope: &Scope,
tag: &str,
) -> Result<UninstallReport, AgentConfigError>
fn uninstall( &self, scope: &Scope, tag: &str, ) -> Result<UninstallReport, AgentConfigError>
Uninstall the hook identified by tag. Restores .bak files when
removing our content leaves the target file empty or pristine.
§Errors
Same envelope as install. Hooks have no
separate ownership ledger (the tag is the owner), so there is no
AgentConfigError::NotOwnedByCaller arm here.
Provided Methods§
Sourcefn is_installed(
&self,
scope: &Scope,
tag: &str,
) -> Result<bool, AgentConfigError>
fn is_installed( &self, scope: &Scope, tag: &str, ) -> Result<bool, AgentConfigError>
Returns true if a hook with this tag is currently installed in this
scope. Used by CLI consumers to render install/uninstall state.
Default impl matches on the richer status
result and treats InstallStatus::InstalledOwned and
InstallStatus::InstalledOtherOwner as installed; agents that have
already implemented status get this for free.
§Errors
Propagates whatever status returns: typically
AgentConfigError::PathResolution, AgentConfigError::Io,
AgentConfigError::JsonInvalid, or
AgentConfigError::ConfigTooLarge.
Sourcefn validate(
&self,
scope: &Scope,
tag: &str,
) -> Result<ValidationReport, AgentConfigError>
fn validate( &self, scope: &Scope, tag: &str, ) -> Result<ValidationReport, AgentConfigError>
Validate hook state without mutating user files.
Unlike status, this reports whether the
discovered state is internally consistent and safe to repair.
§Errors
AgentConfigError::InvalidTagwhentagis not a legal hook tag.- Any error from
statusother thanAgentConfigError::JsonInvalid, which is folded into the returnedValidationReportas malformed-ledger output.
Sourcefn migrate(
&self,
_scope: &Scope,
_tag: &str,
) -> Result<MigrationReport, AgentConfigError>
fn migrate( &self, _scope: &Scope, _tag: &str, ) -> Result<MigrationReport, AgentConfigError>
Migrate any prior layout produced by an earlier version of the consumer (e.g., remove a legacy shell-script wrapper that has since been superseded by a native binary). Default impl is a no-op.
§Errors
Implementation-defined; the default returns MigrationReport::NoOp
unconditionally. Concrete impls typically return
AgentConfigError::Io or AgentConfigError::PathResolution.