diff --git a/src/cli/hooks-cli.ts b/src/cli/hooks-cli.ts index 7f7b0e9eb0..42bb391e22 100644 --- a/src/cli/hooks-cli.ts +++ b/src/cli/hooks-cli.ts @@ -66,6 +66,50 @@ function buildHooksReport(config: OpenClawConfig): HookStatusReport { return buildWorkspaceHookStatus(workspaceDir, { config, entries }); } +function resolveHookForToggle( + report: HookStatusReport, + hookName: string, + opts?: { requireEligible?: boolean }, +): HookStatusEntry { + const hook = report.hooks.find((h) => h.name === hookName); + if (!hook) { + throw new Error(`Hook "${hookName}" not found`); + } + if (hook.managedByPlugin) { + throw new Error( + `Hook "${hookName}" is managed by plugin "${hook.pluginId ?? "unknown"}" and cannot be enabled/disabled.`, + ); + } + if (opts?.requireEligible && !hook.eligible) { + throw new Error(`Hook "${hookName}" is not eligible (missing requirements)`); + } + return hook; +} + +function buildConfigWithHookEnabled(params: { + config: OpenClawConfig; + hookName: string; + enabled: boolean; + ensureHooksEnabled?: boolean; +}): OpenClawConfig { + const entries = { ...params.config.hooks?.internal?.entries }; + entries[params.hookName] = { ...entries[params.hookName], enabled: params.enabled }; + + const internal = { + ...params.config.hooks?.internal, + ...(params.ensureHooksEnabled ? { enabled: true } : {}), + entries, + }; + + return { + ...params.config, + hooks: { + ...params.config.hooks, + internal, + }, + }; +} + function formatHookStatus(hook: HookStatusEntry): string { if (hook.eligible) { return theme.success("✓ ready"); @@ -384,38 +428,13 @@ export function formatHooksCheck(report: HookStatusReport, opts: HooksCheckOptio export async function enableHook(hookName: string): Promise { const config = loadConfig(); - const report = buildHooksReport(config); - const hook = report.hooks.find((h) => h.name === hookName); - - if (!hook) { - throw new Error(`Hook "${hookName}" not found`); - } - - if (hook.managedByPlugin) { - throw new Error( - `Hook "${hookName}" is managed by plugin "${hook.pluginId ?? "unknown"}" and cannot be enabled/disabled.`, - ); - } - - if (!hook.eligible) { - throw new Error(`Hook "${hookName}" is not eligible (missing requirements)`); - } - - // Update config - const entries = { ...config.hooks?.internal?.entries }; - entries[hookName] = { ...entries[hookName], enabled: true }; - - const nextConfig = { - ...config, - hooks: { - ...config.hooks, - internal: { - ...config.hooks?.internal, - enabled: true, - entries, - }, - }, - }; + const hook = resolveHookForToggle(buildHooksReport(config), hookName, { requireEligible: true }); + const nextConfig = buildConfigWithHookEnabled({ + config, + hookName, + enabled: true, + ensureHooksEnabled: true, + }); await writeConfigFile(nextConfig); defaultRuntime.log( @@ -425,33 +444,8 @@ export async function enableHook(hookName: string): Promise { export async function disableHook(hookName: string): Promise { const config = loadConfig(); - const report = buildHooksReport(config); - const hook = report.hooks.find((h) => h.name === hookName); - - if (!hook) { - throw new Error(`Hook "${hookName}" not found`); - } - - if (hook.managedByPlugin) { - throw new Error( - `Hook "${hookName}" is managed by plugin "${hook.pluginId ?? "unknown"}" and cannot be enabled/disabled.`, - ); - } - - // Update config - const entries = { ...config.hooks?.internal?.entries }; - entries[hookName] = { ...entries[hookName], enabled: false }; - - const nextConfig = { - ...config, - hooks: { - ...config.hooks, - internal: { - ...config.hooks?.internal, - entries, - }, - }, - }; + const hook = resolveHookForToggle(buildHooksReport(config), hookName); + const nextConfig = buildConfigWithHookEnabled({ config, hookName, enabled: false }); await writeConfigFile(nextConfig); defaultRuntime.log(