feat(hooks): add a Cline file-based hooks adapter - #2628
Merged
Conversation
Cline resolves one executable per lifecycle event from .clinerules/hooks (project) or ~/Documents/Cline/Hooks (global), named exactly after the event and answering with a JSON result on stdout. Emit a wrapper script per configured event in both the POSIX and PowerShell spellings, plus a manifest of what rulesync owns, and add the file-mode plumbing the executable bit needs.
Wire the adapter into the processor registry, the e2e generate matrix at both scopes, the derived gitignore entry and the generated support tables, and document the surface in file-formats.md.
Restrict the manifest's event list to the names this adapter emits, so a committed manifest cannot make generate write an executable anywhere else. Pass each command to bash -c as a quoted argument instead of splicing it into the wrapper, check its syntax so an unparseable command reports an error rather than cancelling, and strip control characters from the JSON result, which a tab or carriage return in a command's stderr used to make unparseable. Delete the marked scripts when the target is dropped, warn on a hand-authored collision, gitignore the generated script names, and only restore a missing executable bit rather than resetting a mode the user tightened.
Reusing getAuxiliaryFiles for the deletion pass made every adapter's extra files deletion candidates, and Codex CLI returns the user's shared .codex/config.toml there — a --delete run with no hooks source would have removed it. Give the sweep its own getDeletableAuxiliaryFiles hook, implemented only by the Cline adapter, and filter it through isDeletable.
This was referenced Aug 7, 2026
Merged
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
clinehooks target for Cline's file-based hooks, following the maintainer comment that reversed the earlier deferral.Upstream re-verified from source before implementing (the docs page at docs.cline.bot/features/hooks now redirects to the SDK plugins page, which describes a different surface):
VALID_HOOK_TYPESand the hooks directories: https://github.com/cline/cline/blob/main/apps/vscode/src/core/hooks/utils.ts —<workspace>/.clinerules/hooks/and~/Documents/Cline/Hooks/, resolved as the extensionless<Event>on Unix and<Event>.ps1on Windows.{ cancel, contextModification, errorMessage }is read from stdout (shouldContinueis explicitly rejected upstream).Deviation from the mapping in the issue comment:
TaskErrorandSessionShutdowndo not exist.VALID_HOOK_TYPESis exactlyTaskStart,TaskResume,TaskCancel,TaskComplete,PreToolUse,PostToolUse,UserPromptSubmit,Notification,PreCompact, soafterErrorandsessionEndare left unmapped rather than pointed at names Cline would reject. The other seven mappings from the comment match upstream and are implemented.TaskResume/TaskCancelhave no canonical counterpart and stay unmapped.Ownership. The hooks directory is where users hand-author their own hooks and the filenames are fixed by the contract, so every generated script carries a
rulesync-owned: cline-hooksmarker and a script without it is never overwritten. Because the hooks feature reconciles only its single settable path, a script whose event is removed is rewritten as a no-op instead of being stranded with live behavior; the manifestrulesync-hooks.jsonrecords which events rulesync generated.New shared infrastructure:
AiFile.getFileMode()(defaultundefined) plus a chmod inwriteAiFiles, applied even when the content write is skipped so an interrupted run cannot leave a hook without its executable bit. No-op on Windows.Matchers are not supported (the wrapper is a plain shell script with no payload parser) and import is unsupported, both declared in the factory meta. Unit tests cover the mapping, the override block, multi-command ordering, marker-guarded collision, no-op retraction and the executable bit;
e2e-hookscovers the target at both scopes.The issue's other gaps — global modular rules, the required agent description, and the deprecated
.clineruleshandling — are not addressed here.Part of #2405
🤖 Generated with Claude Code