feat: add Cline (cline.bot VS Code extension) preset and hook installer#1301
Open
Krishnachaitanyakc wants to merge 3 commits into
Open
feat: add Cline (cline.bot VS Code extension) preset and hook installer#1301Krishnachaitanyakc wants to merge 3 commits into
Krishnachaitanyakc wants to merge 3 commits into
Conversation
Adds first-class support for Cline's hook protocol against the
post-rewrite agent-preset framework.
Preset (`presets/cline.rs`):
- Pure parser implementing `AgentPreset::parse`. Handles
`PreToolUse` / `PostToolUse` for `write_to_file`,
`replace_in_file`, and `execute_command`.
- Strict event handling: lifecycle events (`TaskStart`,
`TaskResume`, `TaskCancel`, `TaskComplete`, `UserPromptSubmit`,
`PreCompact`) return `PresetError` rather than fabricating a
phantom file-edit checkpoint.
- Workspace-relative path resolution: `parameters.path` is
resolved against `workspaceRoots[0]`; absolute paths pass
through unchanged.
- Model extraction tolerates both shapes: prefer the documented
`model.slug`, fall back to a bare-string `model`, default to
`"unknown"` when missing.
- `transcript_source: None`. Cline holds conversation state in
the VS Code webview, not in a documented on-disk format; a
dedicated reader can land in a follow-up.
Installer (`mdm/agents/cline.rs`):
- Drops extensionless executable shell scripts at
`~/Documents/Cline/Hooks/PreToolUse` and `PostToolUse` (or
`.ps1` files on Windows). Cline's hook system uses the
filename itself as the registration — no JSON/TOML/YAML
config to merge.
- Sets the executable bit on Unix (mode | 0o755).
- Each script carries a marker comment plus the embedded
`git-ai checkpoint cline` invocation; recognition for
idempotency and uninstall requires both signals.
- Refuses to overwrite a user-defined script at the PreToolUse
or PostToolUse path, surfacing a warning and continuing with
the other event.
- Idempotent. Uninstall removes only managed scripts and
preserves user-owned files in the same directory.
- `process_names()` returns an empty vec because Cline ships
only as a VS Code extension (`saoudrizwan.claude-dev`); we
use Hooks-dir existence as the install signal.
Tool classification (`bash_tool.rs`):
- New `Agent::Cline` variant. `write_to_file` /
`replace_in_file` map to `FileEdit`, `execute_command` maps
to `Bash`, all else `Skip`.
Wiring:
- `presets/mod.rs::resolve_preset` accepts `cline`.
- `mdm/agents/mod.rs::get_all_installers` includes
`ClineInstaller`.
- `git_ai_handlers` help text lists `cline`.
- `tests/integration/repos/test_file.rs::AI_AUTHOR_NAMES` and
`test_repo.rs::is_known_checkpoint_preset` learn `cline`.
Tests: 13 preset unit tests, 15 installer unit tests (including a
Unix-gated executable-bit check and a marker-rejection test for
sibling presets), 9 integration tests including 3 full E2E flows
asserting `assert_lines_and_blame`. `cargo fmt --check`,
`cargo clippy --all-targets -- -D warnings`,
`RUSTDOCFLAGS=\"-D warnings\" cargo doc --no-deps`, and the
relevant test suites all pass locally.
Closes git-ai-project#538
CodeQL flags the previous `panic!("...", &events[0])` patterns as
cleartext logging of sensitive information because the Debug
serialization of `ParsedHookEvent` includes `external_session_id`.
Drop the `{:?}` argument; the panic message itself already names
the expected variant, so this matches the existing convention used
by gemini, amp, opencode, etc.
Devin Review (PR git-ai-project#1301) flagged that `install_hooks_at` was calling `fs::create_dir_all(hooks_dir)?` unconditionally, even under `dry_run = true`. That created `~/Documents/Cline/Hooks/` on the user's filesystem during what is supposed to be a no-op preview, and left a side-effect that a subsequent `check_hooks` call would interpret as "Cline is installed." Move the `create_dir_all` inside `if !dry_run`, matching the existing convention in `mdm/agents/opencode.rs`, `mdm/agents/pi.rs`, `mdm/agents/amp.rs`, and `mdm/agents/github_copilot.rs`. Strengthen the s4 dry-run test to assert the hooks dir itself is also not created — that's the property Devin's report is really about.
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.
Summary
Adds first-class support for the Cline VS Code extension —
saoudrizwan.claude-dev— against the post-rewrite agent-preset framework. Closes #538.Cline's hook system is unusual: hooks are not declared in a config file, but as extensionless executable scripts dropped into a global hooks directory (upstream docs). The script's filename IS the registration. Cline ships only as a VS Code extension (no standalone CLI).
Preset (
presets/cline.rs)AgentPreset::parse. HandlesPreToolUse/PostToolUseforwrite_to_file,replace_in_file, andexecute_command.TaskStart,TaskResume,TaskCancel,TaskComplete,UserPromptSubmit,PreCompact) returnPresetErrorrather than fabricating a phantom file-edit checkpoint.parameters.pathis resolved againstworkspaceRoots[0]; absolute paths pass through unchanged.model.slug, fall back to a bare-stringmodel, default to"unknown"when missing.transcript_source: Nonefor v1 — Cline holds conversation state in the VS Code webview, not in a documented on-disk format. A dedicated reader can land in a follow-up.Installer (
mdm/agents/cline.rs)~/Documents/Cline/Hooks/PreToolUseandPostToolUse(or.ps1files on Windows). Cline's hook system uses the filename itself as registration — no JSON/TOML/YAML config to merge.git-ai checkpoint clineinvocation; recognition for idempotency and uninstall requires both signals — so a sibling preset's marker-tagged file (e.g. accidentally invokingcheckpoint claude) is not matched as ours.process_names()returns an empty vec because Cline ships only as a VS Code extension; we use Hooks-dir existence as the install signal.Tool classification (
bash_tool.rs)Agent::Clinevariant:write_to_file/replace_in_file→FileEdit,execute_command→Bash, all elseSkip.Wiring
presets/mod.rs::resolve_presetacceptscline.mdm/agents/mod.rs::get_all_installersincludesClineInstaller.git_ai_handlershelp text listscline.AI_AUTHOR_NAMESandis_known_checkpoint_presetlearncline.Note on the upstream Cline discussion
The issue references cline/cline#9351, which is the issue-author's request for Cline to integrate WITH git-ai. That discussion has not been picked up by Cline's maintainers and is unrelated to our integration direction (we install hooks INTO Cline's documented hook system, no upstream changes required).
Test plan
cargo fmt --checkcleancargo clippy --all-targets -- -D warningsclean (no#[allow(...)]annotations added)RUSTDOCFLAGS="-D warnings" cargo doc --no-depscleanpresets::cline::tests::*) — pre/post for each of the 3 supported tools, lifecycle event rejection (all 6), unsupported tool rejection (all 10 documented read-only/non-mutating tools), missing field errors (taskId,workspaceRoots), invalid JSON, absolute path passthrough, model extraction (object form, bare string form, default-to-unknown)mdm::agents::cline::tests::*) — fresh install (s1), executable-bit set on Unix (s1b), idempotency (s2), updates outdated path (s3), dry-run (s4), creates Hooks dir (s5), refuses to overwrite user-defined scripts (s6); uninstall variants u1-u4; marker-recognition tests including a sibling-preset rejection (test_is_git_ai_cline_script_rejects_other_preset)tests/integration/cline.rs) — preset routing for all 3 tool classes, lifecycle/tool rejection sweeps, 3 E2E flows assertingassert_lines_and_blameand session metadata