pub trait AgentAdapter {
Show 37 methods
// Required methods
fn id(&self) -> &'static str;
fn display_name(&self) -> &'static str;
fn dir_prefix(&self) -> &'static str;
fn mcp_config_relpath(&self) -> &'static str;
fn supported_agents(&self) -> &[&'static str];
fn hook_compatibility(&self) -> &'static CompatibilityMatrix;
fn hook_settings_relpath(&self) -> &'static str;
fn hook_settings_shape(&self) -> HookSettingsShape;
fn scaffold_hook_event(&self) -> &'static str;
fn detect(&self, repo_root: &Path) -> bool;
fn kinds_supported(&self) -> &[CapabilityType];
// Provided methods
fn hook_filename(&self) -> &'static str { ... }
fn hook_file_content(&self, hook_cfg: &HookConfig) -> Result<Vec<u8>> { ... }
fn render_standard_hook(
&self,
context: HookRenderContext<'_>,
) -> Result<HookRenderPlan> { ... }
fn command_hook_fragment(&self, native_event: &str, command: &str) -> Value { ... }
fn merge_hook_fragment(
&self,
existing: Option<&[u8]>,
fragment: &Value,
) -> Result<Vec<u8>> { ... }
fn remove_hook_settings(
&self,
repo_root: &Path,
managed_hooks: &[ManagedHook],
) -> Result<()> { ... }
fn policy_compatibility(&self) -> Vec<PolicyCoverageEntry> { ... }
fn permissions_settings_relpath(&self) -> Option<&'static str> { ... }
fn native_permission_rules(
&self,
_rule: &PolicyRule,
) -> Result<Option<Vec<String>>> { ... }
fn supports(&self, capability_type: CapabilityType) -> bool { ... }
fn native_hook_event(&self, raw_event: &str) -> Result<&'static str> { ... }
fn canonical_hook_event(&self, raw_event: &str) -> Result<&'static str> { ... }
fn ensure_project_dir(&self, repo_root: &Path) -> Result<()> { ... }
fn mcp_env_reference(&self, var: &str) -> String { ... }
fn mcp_server_entry(&self, server: &McpServerConfig) -> Value { ... }
fn mcp_http_declares_type(&self) -> bool { ... }
fn plan(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>> { ... }
fn remove(
&self,
primitive_id: &str,
repo_root: &Path,
managed_hooks: &[ManagedHook],
) -> Result<()> { ... }
fn plan_skill(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>> { ... }
fn plan_tool(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>> { ... }
fn plan_hook(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>> { ... }
fn plan_native_hook(
&self,
capability: &ResolvedCapability,
native: &NativeHookConfig,
repo_root: &Path,
) -> Result<Vec<PlannedFile>> { ... }
fn plan_workflow(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>> { ... }
fn plan_mcp_server(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>> { ... }
fn plan_policy(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>> { ... }
fn remove_dir(
&self,
repo_root: &Path,
base: &str,
kind: &str,
primitive_id: &str,
) -> Result<()> { ... }
}Expand description
What a harness adapter declares, and what Tuff does with it.
An adapter is a declaration: where the harness keeps its files, which
hook events it has and how they map onto Tuff’s, the shape of its hook
settings file, and how to recognise a project that uses it. Everything
else, planning files, rendering hooks, merging into and removing from
the settings file, is a default method here, so there is one
implementation of each and an adapter overrides one only when its
harness genuinely differs (Cursor’s ${env:VAR} spelling, say).
Required Methods§
fn id(&self) -> &'static str
fn display_name(&self) -> &'static str
fn dir_prefix(&self) -> &'static str
fn mcp_config_relpath(&self) -> &'static str
fn supported_agents(&self) -> &[&'static str]
fn hook_compatibility(&self) -> &'static CompatibilityMatrix
fn hook_settings_relpath(&self) -> &'static str
Sourcefn hook_settings_shape(&self) -> HookSettingsShape
fn hook_settings_shape(&self) -> HookSettingsShape
How the settings file at hook_settings_relpath lays out its
registrations. This is the whole of what differs between harnesses
in hook handling; the merge and removal follow from it.
Sourcefn scaffold_hook_event(&self) -> &'static str
fn scaffold_hook_event(&self) -> &'static str
The native event tuff create registers a scaffolded hook under.
fn kinds_supported(&self) -> &[CapabilityType]
Provided Methods§
fn hook_filename(&self) -> &'static str
fn hook_file_content(&self, hook_cfg: &HookConfig) -> Result<Vec<u8>>
fn render_standard_hook( &self, context: HookRenderContext<'_>, ) -> Result<HookRenderPlan>
Sourcefn command_hook_fragment(&self, native_event: &str, command: &str) -> Value
fn command_hook_fragment(&self, native_event: &str, command: &str) -> Value
The hooks-only fragment that registers command under native_event.
Sourcefn merge_hook_fragment(
&self,
existing: Option<&[u8]>,
fragment: &Value,
) -> Result<Vec<u8>>
fn merge_hook_fragment( &self, existing: Option<&[u8]>, fragment: &Value, ) -> Result<Vec<u8>>
Merge a hooks-only fragment into the settings file’s current bytes.
Sourcefn remove_hook_settings(
&self,
repo_root: &Path,
managed_hooks: &[ManagedHook],
) -> Result<()>
fn remove_hook_settings( &self, repo_root: &Path, managed_hooks: &[ManagedHook], ) -> Result<()>
Take Tuff’s registrations out of the settings file, leaving the user’s own alone.
Sourcefn policy_compatibility(&self) -> Vec<PolicyCoverageEntry>
fn policy_compatibility(&self) -> Vec<PolicyCoverageEntry>
How this harness enforces each kind of policy rule, one row per effect and subject. The default says, for every row, that Tuff does not compile policies for this harness, so a policy is refused for it rather than reported as installed.
Sourcefn permissions_settings_relpath(&self) -> Option<&'static str>
fn permissions_settings_relpath(&self) -> Option<&'static str>
The settings file this harness reads native permission rules from, when it has one a repository can carry.
Sourcefn native_permission_rules(
&self,
_rule: &PolicyRule,
) -> Result<Option<Vec<String>>>
fn native_permission_rules( &self, _rule: &PolicyRule, ) -> Result<Option<Vec<String>>>
The native permission rules one policy rule compiles to on this
harness, or None when the harness has no such rules. The list the
rules go in follows the policy rule’s effect.
fn supports(&self, capability_type: CapabilityType) -> bool
fn native_hook_event(&self, raw_event: &str) -> Result<&'static str>
fn canonical_hook_event(&self, raw_event: &str) -> Result<&'static str>
fn ensure_project_dir(&self, repo_root: &Path) -> Result<()>
Sourcefn mcp_env_reference(&self, var: &str) -> String
fn mcp_env_reference(&self, var: &str) -> String
How this harness spells a reference to an environment variable inside
its MCP config. Claude Code and most stdio clients expand ${VAR}.
Sourcefn mcp_server_entry(&self, server: &McpServerConfig) -> Value
fn mcp_server_entry(&self, server: &McpServerConfig) -> Value
The mcpServers.<id> entry this harness needs for an external MCP
server. Secrets are emitted as env references, never values.
Sourcefn mcp_http_declares_type(&self) -> bool
fn mcp_http_declares_type(&self) -> bool
Whether this harness wants an explicit "type": "http" on a remote
server entry. Claude Code and Codex do; Cursor infers the transport
from url and has no type key for remote servers.
fn plan( &self, capability: &ResolvedCapability, repo_root: &Path, ) -> Result<Vec<PlannedFile>>
fn remove( &self, primitive_id: &str, repo_root: &Path, managed_hooks: &[ManagedHook], ) -> Result<()>
fn plan_skill( &self, capability: &ResolvedCapability, repo_root: &Path, ) -> Result<Vec<PlannedFile>>
fn plan_tool( &self, capability: &ResolvedCapability, repo_root: &Path, ) -> Result<Vec<PlannedFile>>
fn plan_hook( &self, capability: &ResolvedCapability, repo_root: &Path, ) -> Result<Vec<PlannedFile>>
fn plan_native_hook( &self, capability: &ResolvedCapability, native: &NativeHookConfig, repo_root: &Path, ) -> Result<Vec<PlannedFile>>
fn plan_workflow( &self, capability: &ResolvedCapability, repo_root: &Path, ) -> Result<Vec<PlannedFile>>
Sourcefn plan_mcp_server(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>>
fn plan_mcp_server( &self, capability: &ResolvedCapability, repo_root: &Path, ) -> Result<Vec<PlannedFile>>
Emit the canonical server.toml record. The JSON entry in the
harness’s MCP config is the artifact the harness reads; this file is
what gives the capability a tree to hash, so check/diff/delete
work exactly as they do for every other kind.
Sourcefn plan_policy(
&self,
capability: &ResolvedCapability,
repo_root: &Path,
) -> Result<Vec<PlannedFile>>
fn plan_policy( &self, capability: &ResolvedCapability, repo_root: &Path, ) -> Result<Vec<PlannedFile>>
Emit the canonical policy.toml record. The rules the harness reads
live in its settings file; this record gives the capability a tree
to hash, so check, diff, and delete treat it like every other
kind, and update can tell when the policy’s rules changed.
fn remove_dir( &self, repo_root: &Path, base: &str, kind: &str, primitive_id: &str, ) -> Result<()>
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".