use crate::IntegrationMode;
use crate::host_assets::HostAdapter;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum SourceFileAdapter {
Claude,
Codex,
Gemini,
Hermes,
OpenClaw,
}
impl SourceFileAdapter {
pub const ALL: &'static [Self] = &[
Self::Claude,
Self::Codex,
Self::Gemini,
Self::Hermes,
Self::OpenClaw,
];
pub fn as_str(self) -> &'static str {
match self {
Self::Claude => "claude",
Self::Codex => "codex",
Self::Gemini => "gemini",
Self::Hermes => "hermes",
Self::OpenClaw => "openclaw",
}
}
pub fn from_id(name: &str) -> Option<Self> {
match name {
"claude" | "claude-code" => Some(Self::Claude),
"codex" => Some(Self::Codex),
"gemini" => Some(Self::Gemini),
"hermes" => Some(Self::Hermes),
"openclaw" => Some(Self::OpenClaw),
_ => None,
}
}
pub fn relative_path(self) -> &'static str {
match self {
Self::Claude => "CLAUDE.md",
Self::Codex => "AGENTS.md",
Self::Gemini => "GEMINI.md",
Self::Hermes => "HERMES.md",
Self::OpenClaw => "OPENCLAW.md",
}
}
pub fn from_host_adapter(host: HostAdapter) -> Self {
match host {
HostAdapter::Claude => Self::Claude,
HostAdapter::Codex => Self::Codex,
HostAdapter::Hermes => Self::Hermes,
HostAdapter::OpenClaw => Self::OpenClaw,
}
}
}
pub const TEMPLATE_VERSION: u32 = 1;
pub(super) fn describe_integration_mode(mode: IntegrationMode) -> &'static str {
match mode {
IntegrationMode::ManualSkill => {
"manual_skill — operator-typed entry point; no native hook fires"
}
IntegrationMode::LauncherWrapper => {
"launcher_wrapper — wrapper script invokes lifecycle hooks before exec"
}
IntegrationMode::NativeHook => {
"native_hook — harness-native hooks fire lifecycle events directly"
}
IntegrationMode::ReferenceAdapter => {
"reference_adapter — adapter implements lifecycle calls explicitly"
}
IntegrationMode::TelemetryOnly => {
"telemetry_only — no installable assets; lifecycle is read from telemetry"
}
}
}
pub(super) fn lifecycle_timing_summary(
adapter: SourceFileAdapter,
mode: IntegrationMode,
) -> Vec<&'static str> {
if matches!(mode, IntegrationMode::TelemetryOnly) {
return vec!["lifecycle events derived from telemetry; no hook timing applies"];
}
match adapter {
SourceFileAdapter::Claude => vec![
"SessionStart -> session.starting",
"UserPromptSubmit -> frame.opening",
"PreCompact -> context.pressure",
"Stop -> frame.ended",
"SessionEnd -> session.ended",
],
SourceFileAdapter::Codex => vec![
"SessionStart -> session.starting",
"UserPromptSubmit -> frame.opening",
"PreCompact -> context.pressure",
"PostCompact -> context.compacted",
"Stop -> frame.ended",
],
SourceFileAdapter::Gemini => vec![
"session boot -> session.starting (telemetry-derived in Synthesized mode)",
"prompt submit -> frame.opening",
"session end -> session.ended",
],
SourceFileAdapter::Hermes => vec![
"on-session-start -> session.starting",
"before-prompt-build -> frame.opening",
"on-compaction-notice -> context.pressure",
"on-agent-end -> frame.ended",
"on-session-end -> session.ended",
"supervisor-tick -> lease.refreshed",
],
SourceFileAdapter::OpenClaw => vec![
"on-session-start -> session.starting",
"before-prompt-build -> frame.opening",
"on-compaction-notice -> context.pressure",
"on-agent-end -> frame.ended",
"on-session-end -> session.ended",
],
}
}
pub(super) fn host_asset_paths(
adapter: SourceFileAdapter,
mode: IntegrationMode,
) -> Vec<&'static str> {
use crate::host_assets as ha;
match (adapter, mode) {
(SourceFileAdapter::Claude, IntegrationMode::NativeHook) => {
vec![ha::CLAUDE_TARGET_SETTINGS]
}
(SourceFileAdapter::Codex, IntegrationMode::NativeHook) => {
vec![ha::CODEX_TARGET_CONFIG, ha::CODEX_TARGET_HOOKS]
}
(SourceFileAdapter::Codex, IntegrationMode::LauncherWrapper) => {
vec![ha::CODEX_TARGET_LAUNCHER]
}
(SourceFileAdapter::Hermes, IntegrationMode::ReferenceAdapter) => {
vec![ha::HERMES_TARGET_ADAPTER]
}
(SourceFileAdapter::OpenClaw, IntegrationMode::ReferenceAdapter) => {
vec![ha::OPENCLAW_TARGET_ADAPTER]
}
_ => Vec::new(),
}
}