Compare commits

..

4 Commits

Author SHA1 Message Date
celestial-vault a07554f191 rework the docker:shell script to reuse an existing container 2025-11-14 21:39:59 -08:00
celestial-vault 474c655240 update script documentation for next steps after docker build 2025-11-14 11:11:25 -08:00
celestial-vault b5157a2376 code comment 2025-11-14 11:02:53 -08:00
celestial-vault b14db72140 add docker setup for cli development 2025-11-13 21:31:54 -08:00
385 changed files with 7807 additions and 23711 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Add DeepSeek 3.2 to native tool calling allow list
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Prevent simultaneuos refreshes when restoring auth info
-90
View File
@@ -1,90 +0,0 @@
# Networking & Proxy Support
To ensure Cline works correctly in all environments (VSCode, JetBrains, CLI) and with various network configurations (especially corporate proxies), strictly follow these guidelines for all network activity.
In extension code, do NOT use the global `fetch` or a default `axios` instance. (Note, `shared/net.ts` is exempt from these rules because it sets up the fetch wrappers.) In Webview code, you SHOULD use global `fetch`.
Global `fetch` and default `axios` do not automatically pick up proxy configurations in all environments (specifically JetBrains and CLI). You MUST use the provided utilities in `@/shared/net` which handle proxy agent configuration. In the webview, the browser/embedder handles proxies.
## Guidelines
### 1. Using `fetch`
Instead of `fetch(...)`, import the proxy-aware wrapper:
```typescript
import { fetch } from '@/shared/net'
// Usage is identical to global fetch
const response = await fetch('https://api.example.com/data')
```
### 2. Using `axios`
When using `axios`, you must apply the settings from `getAxiosSettings()`:
```typescript
import axios from 'axios'
import { getAxiosSettings } from '@/shared/net'
const response = await axios.get('https://api.example.com/data', {
headers: { 'Authorization': '...' },
...getAxiosSettings() // <--- CRITICAL: Injects the proxy agent if needed
})
```
### 3. Third-Party Clients (OpenAI, Ollama, etc.)
Most API client libraries allow you to customize the `fetch` implementation. You **MUST** pass the proxy-aware `fetch` to these clients.
**Example (OpenAI):**
```typescript
import OpenAI from "openai"
import { fetch } from "@/shared/net"
this.client = new OpenAI({
apiKey: '...',
fetch, // <--- CRITICAL: Pass our fetch wrapper
})
```
### 4. Tests
Use `mockFetchForTesting` to mock the underlying fetch implementation.
**Example (callback):**
```
import { mockFetchForTesting } from "@/shared/net"
...
let mockFetch = ...
mockFetchForTesting(mockFetch, () => {
// This calls mockFetch
fetch('https://foo.example').then(...)
})
// Original fetch is restored immediately when the call returns.
```
**Example (Promise):**
```
import { mockFetchForTesting } from "@/shared/net"
...
let mockFetch = ...
await mockFetchForTesting(mockFetch, async () => {
await ...
// This calls mockFetch
await fetch('https://foo.example')
...
})
// Original fetch is restored when the Promise from the callback settles
```
## Verification
If you are adding a new network call or integration:
1. Check `@/shared/net.ts` is imported.
2. Ensure `fetch` or `getAxiosSettings` is being used.
3. Verify that third-party clients are configured to use the custom fetch.
@@ -1,29 +0,0 @@
# Address PR Comments
Review and address all comments on the current branch's PR.
## Steps
1. Get the current branch name and find the associated PR:
```bash
gh pr view --json number,title,body
```
2. Understand the PR context:
- Get the full diff: `git diff origin/main...HEAD`
- Read the changed files to understand what the PR is doing
- Read related files if needed to understand the broader context
- Understand the intent and spirit of the changes, not just the code
3. Fetch all PR comments:
- Inline comments: `gh api repos/{owner}/{repo}/pulls/{pr_number}/comments`
- General comments: `gh pr view {pr_number} --json comments,reviews`
4. Present a summary of all comments with your recommendation for each (apply, skip, or respond). Ignore bot noise (changeset-bot, CI status, etc.).
5. **Wait for my approval** before proceeding.
6. After approval:
- Apply code changes and commit
- Reply to comments that were addressed or intentionally skipped
- Push commits
@@ -1,49 +0,0 @@
# Find Best Reviewers for Current Branch
Analyze my current branch to find the best people to review my PR based on **domain expertise** and git history.
## Steps
1. Get the current branch name and verify it's not `main`
2. Get the diff between the current branch and `origin/main`:
- Use `git diff origin/main...HEAD --name-only` to get changed files
- Use `git diff origin/main...HEAD` to understand the nature/spirit of the changes
3. **Identify the domain/feature area** being changed:
- Read the diff carefully to understand WHAT is being changed conceptually (e.g., "slash commands", "authentication", "API client", "UI components")
- This semantic understanding is crucial for finding the right reviewers
4. Find domain experts by searching for related files and their contributors:
- Identify all files related to the feature/domain (not just the ones changed)
- Example: if changing slash commands, find ALL slash-command related files across the codebase
- Use `git log --format="%an <%ae>" -- <related-files-pattern>` to find who has expertise in that domain
5. For additional context, also gather:
- `git blame -L <start>,<end> origin/main -- <file-path>` for exact lines changed
- Recent commit activity on related files
6. Score and rank contributors by:
- **Highest weight: Domain expertise** - who has the most commits to files in this feature area (even files not touched by this PR)
- **Medium weight: Direct file expertise** - commits to the specific files being changed
- **Lower weight: Line-level ownership** - authored the exact lines being modified
7. Exclude myself (check against my git config user.email)
8. Present the top 5 reviewers as an ordered list
## Output Format
Output an ordered list:
1. **Name** - Domain expert: 15 commits to slash-command related files, authored core parsing logic
2. **Name** - 8 commits to affected files, recently added the feature being modified
3. ...
## Commands Reference
```bash
git config user.email
git diff origin/main...HEAD --name-only
git diff origin/main...HEAD
# Find related files for a domain (adjust pattern based on what you learn from the diff)
find . -type f \( -name "*slash-command*" -o -name "*SlashCommand*" \) | head -20
# Get contributors for related files
find . -type f \( -name "*slash-command*" -o -name "*SlashCommand*" \) -print0 | xargs -0 git log --format="%an <%ae>" -- | sort | uniq -c | sort -rn
git log --format="%an <%ae>" -- <file> | sort | uniq -c | sort -rn
git blame -L 10,20 origin/main -- <file>
```
Do NOT ask questions - analyze the changes, identify the domain, and output the reviewer list.
+1 -1
View File
@@ -58,7 +58,7 @@ jobs:
cache: "npm"
- name: Install Dependencies
run: npm ci
run: npm install changeset
# Check if there are any new changesets to process
- name: Check for changesets
+2 -2
View File
@@ -74,8 +74,8 @@ jobs:
CLINE_ENVIRONMENT: production
# OpenTelemetry production defaults (can be overridden at runtime)
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_LOGS_EXPORTER: console,otlp
OTEL_METRICS_EXPORTER: console,otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
+4 -4
View File
@@ -60,11 +60,11 @@ jobs:
- name: Install root dependencies
if: steps.root-cache.outputs.cache-hit != 'true'
run: npm install --include=optional
run: npm ci --include=optional
- name: Install webview-ui dependencies
if: steps.webview-cache.outputs.cache-hit != 'true'
run: cd webview-ui && npm install --include=optional
run: cd webview-ui && npm ci --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
@@ -99,8 +99,8 @@ jobs:
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
# OpenTelemetry production defaults (can be overridden at runtime)
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_LOGS_EXPORTER: console,otlp
OTEL_METRICS_EXPORTER: console,otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
-21
View File
@@ -165,27 +165,6 @@
},
"console": "integratedTerminal",
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "Open Storybook",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run",
"storybook"
],
"cwd": "${workspaceFolder}/webview-ui",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"serverReadyAction": {
"pattern": "Local:.*http://localhost:([0-9]+)",
"uriFormat": "http://localhost:%s",
"action": "openExternally"
},
"env": {
"IS_DEV": "true"
}
}
]
}
-20
View File
@@ -263,26 +263,6 @@
"watch"
],
"command": "rm -rf ${workspaceFolder}/dist/tmp/user && mkdir -p ${workspaceFolder}/dist/tmp/user"
},
{
"type": "npm",
"script": "storybook",
"group": "build",
"problemMatcher": [],
"isBackground": false,
"label": "npm: storybook",
"dependsOn": [
"npm: protos",
"npm: build:webview"
],
"presentation": {
"reveal": "always"
},
"options": {
"env": {
"IS_DEV": "true"
}
}
}
],
"inputs": [
+6 -70
View File
@@ -1,78 +1,14 @@
# Changelog
## [3.40.0]
## 3.37.1
- Fix highlighted text flashing when task header is collapsed
- Add X-Cerebras-3rd-Party-Integration header to Cerebras API requests
- Add microwave family system prompt configuration
- Remove tooltips from auto approve menu
- Fix Standalone, ensure cwd is the install dir to find resources reliably
- Fix a bug where terminal commands with double quotes are broken when "Terminal Execution Mode" is set to "Background Exec"
- Add support for slash commands anywhere in a message, not just at the beginning. This matches the behavior of @ mentions for a more flexible input experience.
- Add bottom padding to the last message to fix last response text getting cut off by auto approve settings bar.
- Add default thinking level for Gemini 3 Pro models in Gemini provider
## [3.39.2]
- Fix for microwave model and thinking settings
## [3.39.1]
- Fix Openrouter and Cline Provider model info
## [3.39.0]
- Add Explain Changes feature
- Add microwave Stealth model
- Add Tabbed Model Picker with Recommended and Free tabs
- Add support to View remote rules and workflows in the editor
- Enable NTC (Native Tool Calling) by default
- Bug fixes and improvements for LiteLLM provider
## [3.38.3]
- Task export feature now opens the task directory, allowing easy access to the full task files
- Add Grok 4.1 and Grok Code to XAI provider
- Enabled native tool calling for Baseten and Kimi K2 models
- Add thinking level to Gemini 3.0 Pro preview
- Expanded Hooks functionality
- Removed Task Timeline from Task Header
- Bug fix for slash commands
- Bug fixes for Vertex provider
- Bug fixes for thinking/reasoning issues across multiple providers when using native tool calling
- Bug fixes for terminal usage on Windows devices
## [3.38.2]
- Add Claude Opus 4.5
## [3.38.1]
### Fixed
- Fixed handling of 'signature' field in sanitizeAnthropicContentBlock to properly preserve it when thinking is enabled, as required by Anthropic's API.
## [3.38.0]
### Added
- Gemini 3 Pro Preview model
- AquaVoice Avalon model for voice-to-text dictation
### Fixed
- Automatic context truncation when AWS Bedrock token usage rate limits are exceeded
- Removed new_task tool from system prompts, updated slash command prompts, and added helper function for native tool calling validation
## [3.37.1]
- Comprehensive changes to better support GPT 5.1 - System prompt, tools, deep-planning, focus chain, etc.
- Add AGENTS.md support
- feat(models): Add free minimax/mimax-m2 model to the model picker
- cf8dd1c: Comprehensive changes to better support GPT 5.1 - System prompt, tools, deep-planning, focus chain, etc.
- 02abbcf: Add AGENTS.md support
- 855db7d: feat(models): Add free minimax/mimax-m2 model to the model picker
## [3.37.0]
### Added
## Added
- GPT-5.1 with model-specific prompting: tailored system prompts, tool usage, focus chain, and deep-planning optimizations
- Nous Research provider with Hermes 4 model family and custom system prompts
@@ -82,7 +18,7 @@
- Expanded HTTP proxy support throughout the codebase
- Improved focus chain prompting for frontier models (Anthropic, OpenAI, Gemini, xAI)
### Fixed
## Fixed
- Duplicate tool results prevention through existence checking
- XML entity escaping in model content processor
-125
View File
@@ -1,125 +0,0 @@
# CLAUDE.md
This file is the secret sauce for working effectively in this codebase. It captures tribal knowledge—the nuanced, non-obvious patterns that make the difference between a quick fix and hours of back-and-forth & human intervention.
**When to add to this file:**
- User had to intervene, correct, or hand-hold
- Multiple back-and-forth attempts were needed to get something working
- You discovered something that required reading many files to understand
- A change touched files you wouldn't have guessed
- Something worked differently than you expected
- User explicitly asks to "add this to CLAUDE.md"
**Proactively suggest additions** when any of the above happen—don't wait to be asked.
**What NOT to add:** Stuff you can figure out from reading a few files, obvious patterns, or standard practices. This file should be high-signal, not comprehensive.
## gRPC/Protobuf Communication
The extension and webview communicate via gRPC-like protocol over VS Code message passing.
**Proto files live in `proto/`** (e.g., `proto/cline/task.proto`, `proto/cline/ui.proto`)
- Each feature domain has its own `.proto` file
- For simple data, use shared types in `proto/cline/common.proto` (`StringRequest`, `Empty`, `Int64Request`)
- For complex data, define custom messages in the feature's `.proto` file
- Naming: Services `PascalCaseService`, RPCs `camelCase`, Messages `PascalCase`
- For streaming responses, use `stream` keyword (see `subscribeToAuthCallback` in `account.proto`)
**Run `npm run protos`** after any proto changes—generates types in:
- `src/shared/proto/` - Shared type definitions
- `src/generated/grpc-js/` - Service implementations
- `src/generated/nice-grpc/` - Promise-based clients
- `src/generated/hosts/` - Generated handlers
**Adding new enum values** (like a new `ClineSay` type) requires updating conversion mappings in `src/shared/proto-conversions/cline-message.ts`
**Adding new RPC methods** requires:
- Handler in `src/core/controller/<domain>/`
- Call from webview via generated client: `UiServiceClient.scrollToSettings(StringRequest.create({ value: "browser" }))`
**Example—the `explain-changes` feature touched:**
- `proto/cline/task.proto` - Added `ExplainChangesRequest` message and `explainChanges` RPC
- `proto/cline/ui.proto` - Added `GENERATE_EXPLANATION = 29` to `ClineSay` enum
- `src/shared/ExtensionMessage.ts` - Added `ClineSayGenerateExplanation` type
- `src/shared/proto-conversions/cline-message.ts` - Added mapping for new say type
- `src/core/controller/task/explainChanges.ts` - Handler implementation
- `webview-ui/src/components/chat/ChatRow.tsx` - UI rendering
## Adding Tools to System Prompt
This is tricky—multiple prompt variants and configs. **Always search for existing similar tools first and follow their pattern.** Look at the full chain from prompt definition → variant configs → handler → UI before implementing.
1. **Add to `ClineDefaultTool` enum** in `src/shared/tools.ts`
2. **Tool definition** in `src/core/prompts/system-prompt/tools/` (create file like `generate_explanation.ts`)
- Define variants for each `ModelFamily` (generic, next-gen, xs, etc.)
- Export variants array (e.g., `export const my_tool_variants = [GENERIC, NATIVE_NEXT_GEN, XS]`)
- **Fallback behavior**: If a variant isn't defined for a model family, `ClineToolSet.getToolByNameWithFallback()` automatically falls back to GENERIC. So you only need to export `[GENERIC]` unless the tool needs model-specific behavior.
3. **Register in `src/core/prompts/system-prompt/tools/init.ts`** - Import and spread into `allToolVariants`
4. **Add to variant configs** - Each model family has its own config in `src/core/prompts/system-prompt/variants/*/config.ts`. Add your tool's enum to the `.tools()` list:
- `generic/config.ts`, `next-gen/config.ts`, `gpt-5/config.ts`, `native-gpt-5/config.ts`, `native-gpt-5-1/config.ts`, `native-next-gen/config.ts`, `gemini-3/config.ts`, `glm/config.ts`, `hermes/config.ts`, `xs/config.ts`
- **Important**: If you add to a variant's config, make sure the tool spec exports a variant for that ModelFamily (or relies on GENERIC fallback)
5. **Create handler** in `src/core/task/tools/handlers/`
6. **Wire up in `ToolExecutor.ts`** if needed for execution flow
7. **Add to tool parsing** in `src/core/assistant-message/index.ts` if needed
8. **If tool has UI feedback**: add `ClineSay` enum in proto, update `src/shared/ExtensionMessage.ts`, update `src/shared/proto-conversions/cline-message.ts`, update `webview-ui/src/components/chat/ChatRow.tsx`
## Modifying System Prompt
**Read these first:** `src/core/prompts/system-prompt/README.md`, `tools/README.md`, `__tests__/README.md`
System prompt is modular: **components** (reusable sections) + **variants** (model-specific configs) + **templates** (with `{{PLACEHOLDER}}` resolution).
**Key directories:**
- `components/` - Shared sections: `rules.ts`, `capabilities.ts`, `editing_files.ts`, etc.
- `variants/` - Model-specific: `generic/`, `next-gen/`, `xs/`, `gpt-5/`, `gemini-3/`, `hermes/`, `glm/`, etc.
- `templates/` - Template engine and placeholder definitions
**Variant tiers (ask user which to modify):**
- **Next-gen** (Claude 4, GPT-5, Gemini 2.5): `next-gen/`, `native-next-gen/`, `native-gpt-5/`, `native-gpt-5-1/`, `gemini-3/`, `gpt-5/`
- **Standard** (default fallback): `generic/`
- **Local/small models**: `xs/`, `hermes/`, `glm/`
**How overrides work:** Variants can override components via `componentOverrides` in their `config.ts`, or provide a custom template in `template.ts` (e.g., `next-gen/template.ts` exports `rules_template`). If no override, the shared component from `components/` is used.
**Example: Adding a rule to RULES section**
1. Check if variant overrides rules: look for `rules_template` in `variants/*/template.ts` or `componentOverrides.RULES` in `config.ts`
2. If shared: modify `components/rules.ts`
3. If overridden: modify that variant's template
4. XS variant is special—has heavily condensed inline content in `template.ts`
**After any changes, regenerate snapshots:**
```bash
UPDATE_SNAPSHOTS=true npm run test:unit
```
Snapshots live in `__tests__/__snapshots__/`. Tests validate across model families and context variations (browser, MCP, focus chain).
## Modifying Default Slash Commands
Three places need updates:
- `src/core/slash-commands/index.ts` - Command definitions
- `src/core/prompts/commands.ts` - System prompt integration
- `webview-ui/src/utils/slash-commands.ts` - Webview autocomplete
## ChatRow Cancelled/Interrupted States
When a ChatRow displays a loading/in-progress state (spinner), you must handle what happens when the task is cancelled. This is non-obvious because cancellation doesn't update the message content—you have to infer it from context.
**The pattern:**
1. A message has a `status` field (e.g., `"generating"`, `"complete"`, `"error"`) stored in `message.text` as JSON
2. When cancelled mid-operation, the status stays `"generating"` forever—no one updates it
3. To detect cancellation, check TWO conditions:
- `!isLast` — if this message is no longer the last message, something else happened after it (interrupted)
- `lastModifiedMessage?.ask === "resume_task" || "resume_completed_task"` — task was just cancelled and is waiting to resume
**Example from `generate_explanation`:**
```tsx
const wasCancelled =
explanationInfo.status === "generating" &&
(!isLast ||
lastModifiedMessage?.ask === "resume_task" ||
lastModifiedMessage?.ask === "resume_completed_task")
const isGenerating = explanationInfo.status === "generating" && !wasCancelled
```
**Why both checks?**
- `!isLast` catches: cancelled → resumed → did other stuff → this old message is stale
- `lastModifiedMessage?.ask === "resume_task"` catches: just cancelled, hasn't resumed yet, this message is still technically "last"
**See also:** `BrowserSessionRow.tsx` uses similar pattern with `isLastApiReqInterrupted` and `isLastMessageResume`.
**Backend side:** When streaming is cancelled, clean up properly (close tabs, clear comments, etc.) by checking `taskState.abort` after the streaming function returns.
-5
View File
@@ -141,11 +141,6 @@ For example, when working with a local web server, you can use 'Restore Workspac
To contribute to the project, start with our [Contributing Guide](CONTRIBUTING.md) to learn the basics. You can also join our [Discord](https://discord.gg/cline) to chat with other contributors in the `#contributors` channel. If you're looking for full-time work, check out our open positions on our [careers page](https://cline.bot/join-us)!
## Enterprise
Get the same Cline experience with enterprise-grade controls: SSO (SAML/OIDC), global policies and configuration, observability with audit trails, private networking (VPC/private link), and self-hosted or on-prem deployments, and enterprise support. Learn more at our [enterprise page](https://cline.bot/enterprise) or [talk to us](https://cline.bot/contact-sales).
## License
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
-30
View File
@@ -1,30 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>
<json>
<![CDATA[
{
"fontFamily": "cline-bot",
"majorVersion": 1,
"minorVersion": 0,
"fontURL": "https://cline.bot",
"designerURL": "https://cline.bot",
"licenseURL": "https://cline.bot",
"version": "Version 1.0",
"fontId": "cline-bot",
"psName": "cline-bot",
"subFamily": "Regular",
"fullName": "cline-bot",
"description": "Font generated by IcoMoon."
}
]]>
</json>
</metadata>
<defs>
<font id="cline-bot" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="cline" data-tags="cline" horiz-adv-x="977" d="M964.553 383.11l-60.285 121.406v69.495c0 115.545-92.939 209.321-207.647 209.321h-102.986c7.536 15.071 11.722 32.654 11.722 51.074 0 64.471-51.912 116.383-115.545 116.383s-115.545-51.912-115.545-116.383 4.186-35.166 11.722-51.074h-102.986c-114.708 0-207.647-93.776-207.647-209.321v-69.495l-61.959-121.406c-5.861-11.722-5.861-26.793 0-38.515l61.959-119.732v-69.495c0-115.545 92.939-209.321 207.647-209.321h415.294c114.708 0 207.647 93.776 207.647 209.321v69.495l60.285 119.732c5.861 11.722 5.861 25.956 0 38.515v0zM426.178 284.311c0-52.749-42.702-95.451-94.613-95.451s-94.613 42.702-94.613 95.451v169.132c0 52.749 42.702 95.451 94.613 95.451s94.613-42.702 94.613-95.451v-169.132zM731.787 284.311c0-52.749-42.702-95.451-94.613-95.451s-94.613 42.702-94.613 95.451v169.132c0 52.749 42.702 95.451 94.613 95.451s94.613-42.702 94.613-95.451v-169.132z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -517,7 +517,7 @@ func (pw *ProviderWizard) applyModelChange(provider cline.ApiProvider, modelID s
ModelInfo: modelInfo,
}
return UpdateProviderPartial(pw.ctx, pw.manager, provider, updates, true)
return UpdateProviderPartial(pw.ctx, pw.manager, provider, updates, false)
}
// SwitchToBYOProvider switches to a BYO provider that's already configured.
+77 -1
View File
@@ -221,7 +221,83 @@ func (p *ToolResultParser) ParseCodeDefinitions(content string) string {
// ParseWebFetch formats webFetch tool results with content preview
func (p *ToolResultParser) ParseWebFetch(content, url string) string {
return ""
if content == "" {
return fmt.Sprintf("*Fetched content from %s (empty response)*", url)
}
lines := strings.Split(content, "\n")
var result strings.Builder
// Try to extract title
var title string
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, "#") && !strings.HasPrefix(trimmed, "##") {
title = strings.TrimSpace(strings.TrimPrefix(trimmed, "#"))
break
}
}
if title != "" {
result.WriteString(fmt.Sprintf("**Title:** %s\n\n", title))
}
// Show preview of content
result.WriteString("**Preview:**\n")
charCount := 0
maxChars := 500
previewLines := []string{}
for _, line := range lines {
// Skip markdown headers
if strings.HasPrefix(strings.TrimSpace(line), "#") {
continue
}
trimmed := strings.TrimSpace(line)
if trimmed == "" {
continue
}
if charCount+len(trimmed) > maxChars {
break
}
previewLines = append(previewLines, trimmed)
charCount += len(trimmed)
}
result.WriteString(strings.Join(previewLines, " "))
result.WriteString("...\n\n")
// Extract sections
sections := []string{}
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, "##") {
section := strings.TrimSpace(strings.TrimPrefix(trimmed, "##"))
sections = append(sections, section)
if len(sections) >= 5 {
break
}
}
}
if len(sections) > 0 {
result.WriteString("**Sections Found:**\n")
for _, section := range sections {
result.WriteString(fmt.Sprintf("- %s\n", section))
}
result.WriteString("\n")
}
// Word count estimate
wordCount := len(strings.Fields(content))
result.WriteString(fmt.Sprintf("*[Full content: ~%s]*", p.formatWordCount(wordCount)))
return result.String()
}
// detectLanguage returns syntax highlighting language based on file extension
+2 -5
View File
@@ -394,7 +394,7 @@ func newTaskViewCommand() *cobra.Command {
return taskManager.FollowConversation(ctx, taskManager.GetCurrentInstance(), false)
} else if followComplete {
// Follow until completion
return taskManager.FollowConversationUntilCompletion(ctx, task.DefaultFollowOptions())
return taskManager.FollowConversationUntilCompletion(ctx)
} else {
// Default: show snapshot
return taskManager.ShowConversation(ctx)
@@ -668,10 +668,7 @@ func CreateAndFollowTask(ctx context.Context, prompt string, opts TaskOptions) e
// If yolo mode is enabled, follow until completion (non-interactive)
// Otherwise, follow in interactive mode
if opts.Yolo {
// Skip active task check since we just created the task
return taskManager.FollowConversationUntilCompletion(ctx, task.FollowOptions{
SkipActiveTaskCheck: true,
})
return taskManager.FollowConversationUntilCompletion(ctx)
} else {
return taskManager.FollowConversation(ctx, taskManager.GetCurrentInstance(), true)
}
-15
View File
@@ -1,15 +0,0 @@
package task
// FollowOptions contains options for following a conversation
type FollowOptions struct {
// SkipActiveTaskCheck skips the check for an active task
// This is useful when following a task that was just created to avoid race conditions
SkipActiveTaskCheck bool
}
// DefaultFollowOptions returns the default options for following a conversation
func DefaultFollowOptions() FollowOptions {
return FollowOptions{
SkipActiveTaskCheck: false,
}
}
+5 -19
View File
@@ -280,8 +280,8 @@ func (m *Manager) CheckSendEnabled(ctx context.Context) error {
// Error types which we allow sending on
errorTypes := []string{
string(types.AskTypeAPIReqFailed), // "api_req_failed"
string(types.AskTypeMistakeLimitReached), // "mistake_limit_reached"
string(types.AskTypeAPIReqFailed), // "api_req_failed"
string(types.AskTypeMistakeLimitReached), // "mistake_limit_reached"
}
isError := false
@@ -753,21 +753,7 @@ func (m *Manager) FollowConversation(ctx context.Context, instanceAddress string
}
// FollowConversationUntilCompletion streams conversation updates until task completion
func (m *Manager) FollowConversationUntilCompletion(ctx context.Context, opts FollowOptions) error {
// Check if there's an active task before entering follow mode
// Skip this check if we just created a task (to avoid race condition where task isn't active yet)
if !opts.SkipActiveTaskCheck {
err := m.CheckSendEnabled(ctx)
if err != nil {
if errors.Is(err, ErrNoActiveTask) {
fmt.Println("No task is currently running.")
return nil
}
// For other errors (like task busy), we can still enter follow mode
// as the user may want to observe the task
}
}
func (m *Manager) FollowConversationUntilCompletion(ctx context.Context) error {
// Enable streaming mode
m.mu.Lock()
m.isStreamingMode = true
@@ -1253,7 +1239,7 @@ func (m *Manager) updateMode(stateJson string) {
// UpdateTaskAutoApprovalAction enables a specific auto-approval action for the current task
func (m *Manager) UpdateTaskAutoApprovalAction(ctx context.Context, actionKey string) error {
boolPtr := func(b bool) *bool { return &b }
settings := &cline.Settings{
AutoApprovalSettings: &cline.AutoApprovalSettings{
Actions: &cline.AutoApprovalActions{},
@@ -1262,7 +1248,7 @@ func (m *Manager) UpdateTaskAutoApprovalAction(ctx context.Context, actionKey st
// Set the specific action to true based on actionKey
truePtr := boolPtr(true)
switch actionKey {
case "read_files":
settings.AutoApprovalSettings.Actions.ReadFiles = truePtr
+1 -1
View File
@@ -77,7 +77,7 @@ func (s *EnvService) GetHostVersion(ctx context.Context, req *cline.EmptyRequest
return &host.GetHostVersionResponse{
Platform: proto.String("Cline CLI"),
Version: proto.String(global.CliVersion),
Version: proto.String(""),
ClineType: proto.String("CLI"),
ClineVersion: proto.String(global.CliVersion),
}, nil
+48
View File
@@ -0,0 +1,48 @@
# Git
.git
.gitignore
.gitattributes
# Node modules
node_modules
npm-debug.log
# Build artifacts
dist
dist-standalone
build
*.log
# Generated code
src/generated
# CLI build artifacts
cli/bin
cli/dist
# Webview build artifacts
webview-ui/dist
webview-ui/build
# IDE
.vscode
.idea
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Documentation
*.md
!README.md
# Tests
tests
*.test.js
*.spec.js
# CI/CD
.github
.gitlab-ci.yml
+49
View File
@@ -0,0 +1,49 @@
FROM node:22-slim
# TARGETARCH enables multi-architecture support without emulation warnings:
# - Docker automatically sets TARGETARCH to the build platform's architecture
# - On arm64 machines (Apple Silicon): TARGETARCH=arm64, uses linux-arm64 binaries
# - On amd64 machines (Intel/AMD): TARGETARCH=amd64, uses linux-x64 binaries
# The corresponding platform-specific binaries and native modules (better-sqlite3)
# are pre-built by scripts/package-standalone.mjs during the build process.
ARG TARGETARCH
# Install only runtime dependencies
RUN apt-get update && apt-get install -y \
git curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/cline
# Copy the entire pre-built distribution
COPY dist-standalone/ ./
# Create symlink for Linux native modules
# Map Docker's TARGETARCH (arm64/amd64) to Node's platform naming (x64 for amd64)
RUN if [ "$TARGETARCH" = "amd64" ]; then \
ln -sf /opt/cline/binaries/linux-x64/node_modules/better-sqlite3 /opt/cline/node_modules/better-sqlite3; \
else \
ln -sf /opt/cline/binaries/linux-$TARGETARCH/node_modules/better-sqlite3 /opt/cline/node_modules/better-sqlite3; \
fi
# Set up CLI binaries
# The Linux binaries are already in /opt/cline/bin/ from dist-standalone
# Just need to create symlinks to the platform-specific ones
RUN cd /opt/cline/bin && \
ln -sf cline-linux-$TARGETARCH cline && \
ln -sf cline-host-linux-$TARGETARCH cline-host && \
chmod +x cline-linux-$TARGETARCH cline-host-linux-$TARGETARCH cline cline-host
# Add binaries to PATH
ENV PATH="/opt/cline/bin:${PATH}"
ENV NODE_ENV=production
ENV CLINE_HOME=/root/.cline
RUN mkdir -p $CLINE_HOME
WORKDIR /workspace
EXPOSE 8000
ENTRYPOINT ["/opt/cline/bin/cline"]
CMD ["--help"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

-13
View File
@@ -254,19 +254,6 @@ COMMANDS
cline c l
List all configuration variables and their values.
Context Window Configuration
For local model providers, you can configure the context window size:
Ollama
cline config s ollama-api-options-ctx-num=32768
LM Studio
cline config s lm-studio-max-tokens=32768
For other providers (Anthropic, OpenRouter, etc.), the context window
is defined per model in the model metadata and is not user-settable.
Cline uses each model's built-in context limits automatically.
TASK SETTINGS
Task settings are persisted in the ~/.cline/x/tasks directory. When
resuming a task with cline task open, task settings are automatically
@@ -9,7 +9,7 @@ Automate GitHub issue analysis with AI. Mention `@cline` in any issue comment to
<Note>
**New to Cline CLI?** This sample assumes you understand Cline CLI basics and have completed the [Installation Guide](https://docs.cline.bot/cline-cli/installation). If you're new to Cline CLI, we recommend starting with the [GitHub RCA sample](./github-issue-rca) first, as it's simpler and will help you understand the fundamentals before setting up GitHub Actions.
**New to Cline CLI?** This sample assumes you understand Cline CLI basics and have completed the [Installation Guide](https://docs.cline.bot/cline-cli/installation). If you're new to Cline CLI, we recommend starting with the [GitHub RCA sample](../github-issue-rca) first, as it's simpler and will help you understand the fundamentals before setting up GitHub Actions.
</Note>
## The Workflow
+1 -15
View File
@@ -113,20 +113,6 @@ cline instances kill -a
Keep track of instance addresses returned by `cline instance new`. When scripting multiple agents, store these IDs and direct your tasks to the appropriate instance.
</Tip>
## Configuring context window for local providers
For Ollama and LM Studio, you can configure the model context window via CLI:
```bash
# For Ollama
cline config s ollama-api-options-ctx-num=32768
# For LM Studio
cline config s lm-studio-max-tokens=32768
```
For other providers (Anthropic, OpenRouter, etc.), the context window is defined per model in the model metadata and is not user-configurable—Cline uses each model's built-in context limits automatically.
## Choosing the right flow
- **Interactive mode**: Best for exploring new problems, learning how Cline works, or when you want to review plans before execution
@@ -152,7 +138,7 @@ For in-depth commands and flags, check out the [CLI reference](/cline-cli/cli-re
Understand how YOLO mode works and when to use full automation versus manual approval.
</Card>
<Card title="Task management" icon="clipboard-check" href="/features/tasks/task-management">
<Card title="Task management" icon="clipboard-check" href="/getting-started/task-management">
Learn how Cline tracks and manages tasks, including saving and restoring state from checkpoints.
</Card>
</Columns>
+19 -71
View File
@@ -137,16 +137,8 @@
"features/dictation",
"features/drag-and-drop",
"features/editing-messages",
"features/explain-changes",
"features/focus-chain",
{
"group": "Hooks",
"pages": [
"features/hooks/index",
"features/hooks/hook-reference",
"features/hooks/samples"
]
},
"features/hooks",
"features/multiroot-workspace",
"features/plan-and-act",
{
@@ -154,20 +146,12 @@
"pages": [
"features/slash-commands/new-task",
"features/slash-commands/new-rule",
"features/slash-commands/explain-changes",
"features/slash-commands/smol",
"features/slash-commands/report-bug",
"features/slash-commands/deep-planning"
]
},
{
"group": "Workflows",
"pages": [
"features/slash-commands/workflows/index",
"features/slash-commands/workflows/quickstart",
"features/slash-commands/workflows/best-practices"
]
},
"features/slash-commands/workflows",
{
"group": "Task Management",
"pages": [
@@ -205,7 +189,6 @@
"provider-config/fireworks",
"provider-config/zai",
"provider-config/gcp-vertex-ai",
"provider-config/baseten",
{
"group": "AWS Bedrock",
"pages": [
@@ -232,7 +215,8 @@
"provider-config/vscode-language-model-api",
"provider-config/sap-aicore",
"provider-config/vercel-ai-gateway",
"provider-config/requesty"
"provider-config/requesty",
"provider-config/baseten"
]
}
]
@@ -258,39 +242,18 @@
]
},
{
"group": "Reference",
"pages": [
"troubleshooting/networking-and-proxies",
"troubleshooting/terminal-quick-fixes",
"troubleshooting/terminal-integration-guide",
"troubleshooting/task-history-recovery",
"more-info/telemetry"
]
}
]
},
{
"tab": "Enterprise",
"icon": "building",
"groups": [
{
"group": "Enterprise Solutions",
"group": "Enterprise",
"pages": [
"enterprise-solutions/overview",
"enterprise-solutions/onboarding",
"enterprise-solutions/members/roles-and-permissions",
{
"group": "Provider Remote Configuration",
"pages": [
{
"group": "AWS Bedrock",
"pages": [
"enterprise-solutions/provider-remote-config/aws-bedrock/admin-configuration",
"enterprise-solutions/provider-remote-config/aws-bedrock/member-configuration"
]
}
]
}
"enterprise-solutions/security-concerns"
]
},
{
"group": "Reference",
"pages": [
"troubleshooting/terminal-quick-fixes",
"troubleshooting/terminal-integration-guide",
"more-info/telemetry"
]
}
]
@@ -299,6 +262,11 @@
"tab": "Learn",
"icon": "graduation-cap",
"href": "https://cline.bot/learn"
},
{
"tab": "Blog",
"icon": "newspaper",
"href": "https://cline.bot/blog"
}
]
},
@@ -360,26 +328,6 @@
{
"source": "/cline-cli/samples",
"destination": "/cline-cli/samples/overview"
},
{
"source": "/features/hooks/real-world-examples",
"destination": "/features/hooks/samples"
},
{
"source": "/enterprise-solutions/configure-AWS-Bedrock-Admin",
"destination": "/enterprise-solutions/provider-remote-config/aws-bedrock/admin-configuration"
},
{
"source": "/enterprise-solutions/configure-AWS-Bedrock-Member",
"destination": "/enterprise-solutions/provider-remote-config/aws-bedrock/member-configuration"
},
{
"source": "/enterprise-solutions/configure-workOS-authkit",
"destination": "/enterprise-solutions/onboarding"
},
{
"source": "/enterprise-solutions/Onboarding your Organization",
"destination": "/enterprise-solutions/onboarding"
}
],
"search": {
@@ -1,63 +0,0 @@
---
title: "Managing Members"
sidebarTitle: "Managing Members"
description: "A guide to adding, removing, and editing members in your enterprise organization."
---
This guide covers the practical steps for adding, editing, and removing members from your enterprise dashboard. For a conceptual overview of roles and permissions, see the [Roles and Permissions](/enterprise-solutions/members/roles-and-permissions).
<Frame caption="The Members Dashboard provides a central place to manage your team.">
<img src="https://storage.googleapis.com/cline_public_images/members-dash.png" alt="Members Dashboard" />
</Frame>
## Adding Members
To invite someone to your organization, you must have an open seat available on your organization.
1. Navigate to the **Members** tab in your dashboard.
2. Click the **Add Members** button.
3. Enter one or more email addresses, separated by commas.
4. Select a role for the new member(s). It's best practice to start with the "Member" role unless you know they need admin privileges.
5. Click **Send Invitation**.
Invited users will receive an email with a link to join. You can cancel a pending invitation at any time by clicking the trash icon next to the user's email in the 'Pending Invites' section.
<Tip>
**Managing Users at Scale**
When inviting a large number of users, you can paste a comma-separated list of emails directly into the invitation field. While role changes and removals are performed individually, this bulk invitation feature helps streamline the onboarding process for entire teams.
</Tip>
<Frame caption="Adding members to your organization">
<img src="https://storage.googleapis.com/cline_public_images/adding-members.png" alt="Confirm Member Removal" />
</Frame>
## Editing Member Roles
As your team's needs change, you can adjust member roles directly from the dashboard.
- Find the member in your list.
- Under the "Role" column, click the dropdown menu.
- Select their new role. The change takes effect immediately.
Refer to the [Roles and Permissions](/enterprise-solutions/members/roles-and-permissions) for a detailed breakdown of what each role can do.
## Removing Members
Removing a member immediately revokes their access to all organization-specific resources, including shared API keys and configurations.
1. Go to the **Members Dashboard**.
2. Find the member in the list and click the red trash icon (<Icon icon="trash" iconType="solid" />).
3. Confirm the removal when prompted.
<Frame caption="You will be asked to confirm before a member is permanently removed.">
<img src="https://storage.googleapis.com/cline_public_images/remove-user.png" alt="Confirm Member Removal" />
</Frame>
## Troubleshooting Invitations
If an invited user is having trouble joining, check these common issues:
- **Invitation Not Received**: Ask the user to check their spam or junk mail folder. If it's not there, cancel the pending invitation and try sending it again, verifying the email address is correct.
- **"Invalid Domain" Error**: The user's email address must belong to a domain that has been verified for your organization. Work with your IT administrator to ensure the necessary domains are configured.
@@ -1,63 +0,0 @@
---
title: "Managing Members"
sidebarTitle: "Managing Members"
description: "A guide to adding, removing, and editing members in your enterprise organization."
---
This guide covers the practical steps for adding, editing, and removing members from your enterprise dashboard. For a conceptual overview of roles and permissions, see the [Roles and Permissions](/enterprise-solutions/members/roles-and-permissions).
<Frame caption="The Members Dashboard provides a central place to manage your team.">
<img src="https://storage.googleapis.com/cline_public_images/members-dash.png" alt="Members Dashboard" />
</Frame>
## Adding Members
To invite someone to your organization, you must have an open seat available on your organization.
1. Navigate to the **Members** tab in your dashboard.
2. Click the **Add Members** button.
3. Enter one or more email addresses, separated by commas.
4. Select a role for the new member(s). It's best practice to start with the "Member" role unless you know they need admin privileges.
5. Click **Send Invitation**.
Invited users will receive an email with a link to join. You can cancel a pending invitation at any time by clicking the trash icon next to the user's email in the 'Pending Invites' section.
<Tip>
**Managing Users at Scale**
When inviting a large number of users, you can paste a comma-separated list of emails directly into the invitation field. While role changes and removals are performed individually, this bulk invitation feature helps streamline the onboarding process for entire teams.
</Tip>
<Frame caption="Adding members to your organization">
<img src="https://storage.googleapis.com/cline_public_images/adding-members.png" alt="Confirm Member Removal" />
</Frame>
## Editing Member Roles
As your team's needs change, you can adjust member roles directly from the dashboard.
- Find the member in your list.
- Under the "Role" column, click the dropdown menu.
- Select their new role. The change takes effect immediately.
Refer to the [Roles and Permissions](/enterprise-solutions/members/roles-and-permissions) for a detailed breakdown of what each role can do.
## Removing Members
Removing a member immediately revokes their access to all organization-specific resources, including shared API keys and configurations.
1. Go to the **Members Dashboard**.
2. Find the member in the list and click the red trash icon (<Icon icon="trash" iconType="solid" />).
3. Confirm the removal when prompted.
<Frame caption="You will be asked to confirm before a member is permanently removed.">
<img src="https://storage.googleapis.com/cline_public_images/remove-user.png" alt="Confirm Member Removal" />
</Frame>
## Troubleshooting Invitations
If an invited user is having trouble joining, check these common issues:
- **Invitation Not Received**: Ask the user to check their spam or junk mail folder. If it's not there, cancel the pending invitation and try sending it again, verifying the email address is correct.
- **"Invalid Domain" Error**: The user's email address must belong to a domain that has been verified for your organization. Work with your IT administrator to ensure the necessary domains are configured.
@@ -1,27 +0,0 @@
---
title: "Members Overview"
sidebarTitle: "Overview"
description: "An overview of member management in your enterprise organization."
---
This section provides a comprehensive guide to managing members in your enterprise organization. Here, you'll find everything you need to know about roles, permissions, and the practical steps for adding, editing, and removing members from your dashboard.
## Key Topics
<CardGroup cols={2}>
<Card
title="Roles and Permissions"
icon="user-shield"
href="/enterprise-solutions/members/roles-and-permissions"
>
A detailed breakdown of the available roles and their specific permissions.
</Card>
<Card
title="Managing Members"
icon="users-gear"
href="/enterprise-solutions/members/managing-members"
>
A practical guide to adding, editing, and removing members from your
dashboard.
</Card>
</CardGroup>
@@ -1,83 +0,0 @@
---
title: "Roles and Permissions"
sidebarTitle: "Roles and Permissions"
description: "An overview of member roles, permissions, and best practices for your enterprise organization."
---
Choosing the right role for each member is crucial for maintaining security and ensuring your team can work effectively. This guide provides a detailed breakdown of the available roles, their specific permissions, and best practices for managing your organization.
## Role Definitions
Heres a summary of the available roles and their intended use cases.
<CardGroup cols={1}>
<Card title="Owner" icon="user-crown">
**Best for:** The primary account holder or a small number of designated leaders.
Owners have unrestricted access to all settings, including billing, member management, and security configurations. To maintain tight control over the organization, the number of Owners should be kept to a minimum.
</Card>
<Card title="Admin" icon="user-gear">
**Best for:** Team leads or IT administrators who need to manage users and configurations.
Admins can invite, edit, and remove members, as well as manage provider configurations. They have broad access but cannot manage billing or change the Owner. This is a suitable role for trusted team managers.
</Card>
<Card title="Member" icon="user">
**Best for:** Most developers and individual contributors.
Members can use Cline with the organization's shared resources but cannot change any settings or view other users' activity. This is the safest default role for new users.
</Card>
</CardGroup>
## Permissions Matrix
For a detailed comparison, this matrix outlines the specific capabilities of each role.
| Permission | Member | Admin | Owner |
| --------------------------- | :----: | :----: | :----: |
| **General Usage** | | | |
| Use Cline | ✅ | ✅ | ✅ |
| Access Shared API Providers | ✅ | ✅ | ✅ |
| | | | |
| **Member Management** | | | |
| View Members | ❌ | ✅ | ✅ |
| Invite New Members | ❌ | ✅ | ✅ |
| Edit Member Roles | ❌ | ✅ | ✅ |
| Remove Members | ❌ | ✅ | ✅ |
| Remove Admins | ❌ | ❌ | ✅ |
| | | | |
| **Configuration** | | | |
| Configure API Providers | ❌ | ✅ | ✅ |
| Manage Security Settings | ❌ | ❌ | ✅ |
| | | | |
| **Billing & Ownership** | | | |
| View Billing Information | ❌ | ❌ | ✅ |
| Manage Subscription | ❌ | ❌ | ✅ |
| Transfer Ownership | ❌ | ❌ | ✅ |
## Role Management Best Practices
Effective role management is fundamental to securing your organization.
- **Apply the Principle of Least Privilege**: Always assign the role with the minimum necessary permissions. Most users should be **Members**. Grant **Admin** rights only to those who are responsible for user management or technical configuration.
- **Limit the Number of Owners**: The **Owner** role should be reserved for one or two key individuals who control the account and billing. This centralization of power prevents accidental or malicious changes to critical settings.
- **Regularly Audit Roles**: Periodically review the list of Admins and Owners to ensure the assigned roles are still appropriate. When a team member's responsibilities change, adjust their role accordingly.
## Identity Providers and Domain Verification
For a user to successfully join and sign in to your organization, two conditions must be met:
1. Their email must be managed by your organization's verified **Identity Provider (IDP)**, such as Microsoft Entra ID, Okta, or AWS.
2. Your organization must have a **verified domain** with a provider like Google or Microsoft.
This ensures that only authenticated users from your company can access your Cline organization.
## Seat Management and Invitations
Each user in your organization, regardless of role, consumes one seat from your license.
- When an invitation is sent, a seat is considered "pending."
- If an invited user does not accept, the invitation can be revoked to free up the seat.
- Removing a member from the organization immediately frees up a seat.
Now that you understand the different roles and how to manage them, you can proceed to [configuring provider remote access](/enterprise-solutions/provider-remote-config/aws-bedrock/admin-configuration) for your organization.
-128
View File
@@ -1,128 +0,0 @@
---
title: "Onboarding"
description: "This guide explains how administrators configure SSO provisioning and user management in Cline Enterprise."
---
## Overview
Cline Enterprise integrates with your existing identity provider (IdP) via WorkOS to deliver secure SSO and zero-touch user lifecycle management. In this guide, you'll connect your IdP (Okta, Azure AD, Google Workspace, or any SAML/OIDC provider), enable just-in-time (JIT) provisioning so new users are created automatically on first sign-in, and configure role mapping so permissions stay aligned with your directory—no manual invites or seat reconciliations required.
## Prerequisites
- [Cline Enterprise License](https://cline.bot/enterprise)
- Access to your identity provider (IdP) configuration (e.g., Okta, Azure AD, Google Workspace)
- Knowledge of your organization's SSO requirements
## Configuration Steps
### Step 1: Onboard to Cline Enterprise license
Your IdP administrator will receive an email with a link to register their organization with WorkOS during onboarding.
### Step 2: Configure Your Identity Provider
Connect your identity provider (IdP) to WorkOS:
1. In the WorkOS dashboard, go to **AuthKit → Connections**
2. Click **Add Connection**
3. Select your identity provider (e.g., Okta, Azure AD, Google Workspace, Generic SAML/OIDC)
4. Follow the provider-specific setup instructions
Each identity provider (IdP) will have its own setup process and required fields. Be sure to follow the specific instructions in the WorkOS dashboard for your chosen provider.
For more explicit instruction on connecting your IdP, refer to the [WorkOS SSO documentation](https://workos.com/docs/authkit/sso)
### Step 3: Configure User Provisioning
Cline Enterprise uses **just-in-time provisioning** that works automatically:
- **Organizations are created automatically**
- **Users gain access automatically** on their first SSO sign-in, once their credentials have been configured by the IdP administrator.
- **Roles sync automatically** from your IdP (Admin/Owner → Admin, Member → Member)
- **No manual user invites or seat management** required
No additional configuration is needed. Users are provisioned automatically when they sign in through SSO.
### Step 4: Configure User Attributes Mapping
User roles are mapped automatically from your IdP:
- **Admin** in IdP → **Admin** role in Cline (Note: The first Owner of the org is created manually during onboarding)
- **Member** in IdP → **Member** role in Cline
<Info>
For what each role can access, see the [Roles and Permissions](./members/roles-and-permissions) page.
</Info>
If needed, you can configure additional user attributes in the Cline Admin console:
1. Go to **Settings → Authentication → User Attributes**
2. Map attributes such as email and name based on your IdP configuration
For information about available user attributes, see the [WorkOS User Object Documentation](https://workos.com/docs/authkit/user-management).
### Step 5: Test SSO Connection
Before allowing users to sign in, test the SSO flow to ensure everything is configured correctly.
**To test the connection:**
1. In the WorkOS dashboard (or Cline Admin console if available), locate and click **Test SSO Connection**
2. You'll be redirected to your IdP's login page
3. Enter valid credentials for a test user
4. After successful authentication, you should be redirected back
5. Confirm that the user's information (name, email, role) displays correctly
**Expected outcome:** The test user is authenticated, their account details are visible, and their role matches what's configured in your IdP.
**If the test fails:** Double-check your IdP configuration (redirect URIs, SAML certificates, attribute mappings). See the [WorkOS SSO documentation](https://workos.com/docs/authkit/sso) for troubleshooting guidance.
### User Access
Once SSO is configured, users in your IdP can access Cline automatically without manual invites or account setup.
**First-time sign-in flow:**
1. User navigates to Cline and clicks **Sign in with SSO**
2. User authenticates via your organization's IdP
3. Cline automatically creates their account in your Organization
4. Role is assigned based on their IdP role (see [Step 4](#step-4-configure-user-attributes-mapping))
5. User is redirected to Cline and can begin working
**What happens automatically:**
- Account creation with correct organization assignment
- Role and permission assignment
- Basic profile information (name, email) populated from IdP
**No action required:** Users don't need to request access or wait for approval. Access is granted immediately upon successful IdP authentication.
### Managing Access
All access management and revocation of users is currently handled by your IdP:
- Add users → access granted automatically on first login
- Change roles → updated on next login
- Remove users → access revoked automatically
<Info>
Role changes sync automatically on the user's next sign-in.
</Info>
### Changing your IdP
In order to change to a different IdP, please contact support and we will guide you through this process.
---
## Verification
Steps to verify successful configuration:
1. **Test User Sign-In**: Have a test user sign in through the SSO flow (access is granted automatically on first login)
2. **Verify User Provisioning**: Confirm that the user is automatically created and has appropriate role permissions
3. **Check User Attributes**: Verify that user information (name, email, organization) is correctly populated
4. **Test Role Changes**: Update a user's role in your IdP and verify it syncs on their next login
5. **Test User Deprovisioning**: Remove a user from your IdP and verify they lose access to Cline on their next login attempt
6. **Review Audit Logs**: Check WorkOS audit logs to ensure authentication events are being recorded
---
@@ -1,127 +0,0 @@
---
title: "Configure AWS Bedrock Provider (Admin)"
sidebarTitle: "Configure AWS Bedrock (Admin)"
description: "This guide explains how administrators configure AWS Bedrock as the organization-wide LLM provider for Cline."
---
As an administrator, you can add AWS Bedrock as the organization-wide LLM provider for all Cline users. This centralized approach ensures consistent access to Amazon's AI models while maintaining your organization's security and compliance requirements through VPC endpoints, region controls, and prompt caching optimizations.
## Before You Begin
To get started with setting up AWS Bedrock as your organization's LLM provider, you'll need a few items in place.
**Administrator access to the Cline Admin console**
You need admin privileges to enforce provider settings across your organization. If you can navigate to **Settings → Cline Settings** in the admin console at [app.cline.bot](https://app.cline.bot), you have the right access level.
<Info>
**Quick Check**: Try accessing the settings page now. If you can see the provider configuration options, you're good to go.
</Info>
**AWS Bedrock account with the right permissions**
Your AWS account needs specific Bedrock permissions to work with Cline.
<Note>
If you don't have direct AWS access, coordinate with your cloud team to get these permissions set up before proceeding.
</Note>
**Your preferred AWS region**
Choose your primary AWS region carefully since this will be enforced for all users.
<Tip>
Check which models are available in your region first. Some newer models might not be available in all regions yet.
</Tip>
<Frame>
<img
src="https://storage.googleapis.com/cline-static-assets-prod/assets/AWS%20Remote%20Config.gif"
/>
</Frame>
## Configuration Steps
<Steps>
<Step title="Access Cline Settings">
Navigate to [app.cline.bot](https://app.cline.bot) and sign in with your administrator account. Go to **Settings → Cline Settings**.
<Info>
You should see the provider configuration options if you have the correct admin access level.
</Info>
</Step>
<Step title="Enable Remote Provider Configuration">
Toggle on **Enable settings** to reveal the remote provider configuration options. This allows you to enforce provider settings across your organization.
</Step>
<Step title="Select AWS Bedrock as the API Provider">
Open the **API Provider** dropdown menu and select **Amazon Bedrock**. This will open the Bedrock configuration panel where you'll configure all your organization-wide settings.
</Step>
<Step title="Configure Bedrock Settings">
The configuration panel includes several settings that control how Bedrock works for your organization. Configure what you need:
<AccordionGroup>
<Accordion title="Region (required)">
Enter your preferred AWS region like `us-west-2` or `us-east-1`. This region will be enforced for all organization members.
[View AWS Global Infrastructure](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/)
<Tip>
For most organizations, `us-east-1` or `us-west-2` are recommended as they have the best model availability.
</Tip>
</Accordion>
<Accordion title="Custom VPC Endpoint (optional)">
If your organization uses a private VPC endpoint for Bedrock, specify it here to ensure all API calls go through your network infrastructure.
[Learn more about AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-overview.html)
</Accordion>
<Accordion title="Cross-region Inference (optional)">
Enable this to let Bedrock automatically route requests to other regions when your primary region has capacity constraints. Useful for maintaining availability during high-demand periods.
[Learn more about Inference Profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html)
</Accordion>
<Accordion title="Global Inference Profile (optional)">
Turn this on to use AWS's global inference routing, which automatically directs requests to the optimal region based on availability and latency.
</Accordion>
<Accordion title="Prompt Caching (optional)">
Enable prompt caching to reduce costs and latency. Bedrock caches portions of prompts that remain consistent across requests, making repeated interactions faster and cheaper.
[Learn more about Prompt Caching](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html)
</Accordion>
</AccordionGroup>
</Step>
<Step title="Save Configuration">
After configuring your settings, close the provider configuration panel and click **Save** on the settings page to persist your changes.
Once saved, all organization members signed into the Cline extension will automatically use AWS Bedrock with your configured settings. They won't be able to select other providers or switch to their personal Cline accounts.
<Warning>
Members can't switch to personal Cline accounts or join other organizations once remote configuration is enabled. This ensures consistent provider usage across your team.
</Warning>
</Step>
</Steps>
## Verification
To verify the configuration:
1. Check that the provider shows as "Amazon Bedrock" in the Enabled provider field
2. Confirm the settings persist after refreshing the page
3. Test with a member account to ensure they see only Bedrock as a provider
## Troubleshooting
**Members don't see the configured provider**
Ensure you clicked Save after closing the configuration panel. Verify the member account belongs to the correct organization.
**Configuration changes don't persist**
Make sure to click the Save button on the main settings page, not just close the configuration panel.
**Need to change regions later**
You can update the region at any time. Members will need to ensure their local AWS credentials have access to the new region. For more information, refer to the [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html).
For further details, consult the [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) and coordinate with your internal cloud team.
@@ -1,131 +0,0 @@
---
title: "Configure AWS Bedrock in VS Code (Members)"
sidebarTitle: "Configure AWS Bedrock (Member)"
description: "Guide for engineers configuring AWS Bedrock credentials in VS Code after admin setup"
---
As a team member, you can connect your local development environment to your organization's AWS Bedrock setup. This guide walks you through configuring your AWS credentials in VS Code so you can start using models through your organization's Bedrock infrastructure. Your administrator has already configured the provider settings—you just need to add your credentials to get started.
## Before You Begin
To successfully connect to your organization's AWS Bedrock setup, you'll need a few things ready.
**Cline extension installed and configured**
The Cline extension must be installed in VS Code and you need to be signed into your organization account. If you haven't installed Cline yet, follow our [installation guide](/getting-started/installing-cline).
<Info>
**Quick Check**: Open the Cline panel in VS Code. If you see your organization name in the bottom left, you're signed in correctly.
</Info>
**AWS credentials with Bedrock access**
You need AWS credentials that have permission to access Bedrock in your organization's configured region.
<Note>
If you don't have AWS credentials yet, reach out to your IT or cloud team to get access keys or AWS CLI profiles configured with the necessary Bedrock permissions.
</Note>
<Frame>
<img
src="https://storage.googleapis.com/cline-static-assets-prod/assets/VS%20Code%20Bedrock%20API%20Key.gif"
/>
</Frame>
## Configuration Steps
<Steps>
<Step title="Open Cline Settings">
Open VS Code and access the Cline settings panel using either of these methods:
- Click the settings icon (⚙️) in the Cline panel
- Click on the API Provider dropdown located directly below the chat area (it will display as `bedrock.anthropic.claude-sonnet-4-20250514-v1:0` or similar)
</Step>
<Step title="Select Your Authentication Method">
Choose one of the following credential methods to authenticate with AWS Bedrock:
<AccordionGroup>
<Accordion title="AWS Bedrock API Key">
Use dedicated AWS access keys specifically for Bedrock access.
[Learn more about AWS Bedrock API Keys](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html)
1. Select the **API Key** radio button
2. Enter your AWS Access Key ID and Secret Access Key
3. These credentials are stored locally and used only by the VS Code extension
</Accordion>
<Accordion title="AWS Profile">
Use an existing AWS CLI profile configured on your machine.
[Learn more about AWS CLI Profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html)
1. Select the **AWS Profile** radio button
2. Choose or enter the profile name from your `~/.aws/credentials` file
3. Cline will use the credentials associated with that profile
</Accordion>
<Accordion title="AWS Credentials">
Use your default AWS credential chain (environment variables, EC2 instance roles, etc.).
1. Select the **AWS Credentials** radio button
2. Cline will automatically detect credentials from your environment using the standard AWS credential provider chain
</Accordion>
</AccordionGroup>
<Note>
The AWS Region is preconfigured by your administrator and does not need to be set in the extension.
</Note>
</Step>
<Step title="Verify Configuration">
After selecting your authentication method, the extension will display checkmarks for enabled features:
- ✓ Supports images
- ✓ Supports browser use
- ✓ Supports prompt caching
Additional settings like cross-region inference and global inference profile will be locked (shown with a lock icon 🔒) as they're controlled by your administrator.
</Step>
<Step title="Test the Connection">
Send a test message in Cline to verify your credentials work correctly with the configured Bedrock region.
<Tip>
**Testing Recommendation**
It is recommended to test the connection in plan mode to verify everything works correctly before using it for actual tasks.
</Tip>
</Step>
</Steps>
## Troubleshooting
**Authentication errors ("Access Denied" or "Invalid Credentials")**
Verify your chosen credential method has the necessary IAM permissions to call Bedrock in the configured region. Required permissions include `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream`. For more information, refer to [AWS Bedrock IAM Permissions](https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html).
**Region-related errors or "model not available"**
Ask your administrator to confirm which region is configured for your organization. Ensure your AWS credentials have access to Bedrock in that specific region. [View AWS Global Infrastructure](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/)
**Don't see AWS Bedrock as an option**
Confirm you're signed into the correct Cline organization. Verify your administrator has saved the Bedrock configuration. Try signing out and back into the extension.
**AWS Credentials option not finding credentials**
Verify AWS CLI is installed and configured with `aws configure` ([AWS CLI Installation Guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)). Check that credentials are present in `~/.aws/credentials`. For EC2/ECS environments, ensure IAM roles are properly attached. If using environment variables, set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
## Security Best Practices
When configuring your AWS credentials, follow these security guidelines:
- Use IAM roles with minimum required permissions ([AWS IAM Best Practices](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html))
- Rotate access keys regularly if using the API Key method
- Never store credentials in code or version control
- Prefer AWS Profile method for better credential management
- Consider using AWS SSO/federated roles for enhanced security
Your organization administrator controls which models are available. The extension will automatically display available models based on your region's Bedrock configuration. For more information about available models, refer to the [AWS Bedrock Model Access documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html).
For further assistance, consult the [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) and coordinate with your organization's cloud administrator.
@@ -0,0 +1,61 @@
---
title: "Security Concerns"
---
## Enterprise Security with Cline
Cline addresses enterprise security concerns through its unique client-side architecture that prioritizes data privacy, secure cloud integration, and transparent operations. Below is a comprehensive overview of how Cline maintains robust security measures for enterprise environments.
### Client-Side Architecture
Cline operates exclusively as a client-side VSCode extension with zero server-side components. This fundamental design choice ensures that your code and data remain within your secure environment at all times. Unlike traditional AI assistants that send data to external servers for processing, Cline connects directly to your chosen cloud provider's AI endpoints, keeping all sensitive information within your infrastructure boundaries.
<Frame>
<img
src="https://storage.googleapis.com/cline_public_images/docs/assets/cline-arch.png"
alt="Cline's relationship to local and remote assets"
/>
</Frame>
### Data Privacy Commitment
Cline implements a strict zero data retention policy, meaning your intellectual property never leaves your secure environment. The extension does not collect, store, or transmit your code to any central servers. This approach significantly reduces potential attack vectors that might otherwise be introduced through data transmission to third-party systems. Telemetry collection is optional and requires explicit consent.
### Cloud Provider Integration
Enterprise teams can access cutting-edge AI models through their existing cloud deployments. Cline supports seamless integration with:
- AWS Bedrock
- Google Cloud Vertex AI
- Microsoft Azure
These integrations utilize your organization's existing security credentials, including native IAM role assumption for AWS. This ensures that all AI processing occurs within your corporate cloud environment, maintaining compliance with your established security protocols.
### Open-Source Transparency
Cline's codebase is completely open-source, allowing for comprehensive security auditing by your internal teams. This transparency enables security professionals to verify exactly how the extension functions and confirm that it adheres to your organization's security requirements. Organizations can review the code to ensure it aligns with their security policies before deployment.
### Controlled Modifications
The extension implements safeguards against unauthorized changes to your codebase. Cline requires explicit user approval for all file modifications and terminal commands, preventing accidental or unwanted alterations. This approval-based workflow maintains the integrity of your projects while still providing AI assistance.
### Enterprise Deployment Support
For organizations with strict security review processes, Cline provides comprehensive documentation including detailed deployment diagrams, sequence diagrams illustrating all data flows, and complete security posture documentation. These materials facilitate thorough security reviews and help demonstrate compliance with enterprise data handling standards and regulations.
### Access Control
Enterprise editions of Cline (planned for Q2 2025) will include centralized administration features that allow organizations to:
- Manage user access with customizable permission levels
- Provision accounts with corporate credentials
- Immediately revoke access when needed
- Control which AI providers and LLM endpoints can be used
- Deploy standardized settings across the organization
- Prevent unauthorized use of personal API keys
### Compliance and Governance
Cline's architecture supports compliance with data sovereignty requirements and enterprise data handling regulations. The planned Enterprise Complete edition will further enhance governance with detailed audit logging, compliance reporting, and automated policy enforcement mechanisms.
By combining client-side processing, direct cloud provider integration, and transparent operations, Cline offers enterprise teams a secure way to leverage AI assistance while maintaining strict control over their sensitive code and data.
-24
View File
@@ -15,30 +15,6 @@ Cline creates a checkpoint after each tool use (file edits, commands, etc.). The
For example, if you're working on a feature and Cline makes multiple file changes, each change creates a checkpoint. This means you can review each modification and, if needed, roll back to any point without affecting your main Git repository.
## Enabling or Disabling Checkpoints
Checkpoints are enabled by default in Cline. To toggle this feature:
1. Open the Cline settings by clicking the gear icon in the Cline panel
2. Go to "Feature Settings"
3. Toggle the **"Enable Checkpoints"** checkbox on or off
<Frame>
<img
src="https://storage.googleapis.com/cline_public_images/checkpoints.gif"
alt="Checkpoints toggle in settings"
/>
</Frame>
### When to Disable Checkpoints
While checkpoints provide valuable safety nets, you might want to disable them in certain situations:
- **Large repositories**: If you're working with very large codebases, checkpoints may use additional storage space
- **Performance concerns**: On systems with limited resources, disabling checkpoints can slightly improve performance
- **Simple tasks**: For quick, low-risk operations where rollback isn't needed
## Viewing Changes & Restoring
After each tool use, you can:
-90
View File
@@ -1,90 +0,0 @@
---
title: "Explain Changes"
sidebarTitle: "Explain Changes"
---
Explain Changes is an AI-powered code review feature that adds inline explanations to your code changes. When Cline makes modifications to your codebase, you can click a button to get streaming, contextual explanations that appear directly in VS Code's diff view.
<Note>
Explain Changes requires **[Checkpoints](/features/checkpoints)** to be enabled. Make sure to enable checkpoints in your Cline settings before using this feature.
</Note>
<Frame>
<video
autoPlay
loop
muted
playsInline
src="https://storage.googleapis.com/cline_public_images/explain-code-button.mp4"
/>
</Frame>
## How It Works
After Cline completes a task that involves file changes, you'll see an "Explain Changes" button alongside the "View Changes" button in the completion message. Clicking this button:
1. Opens a multi-file diff view showing all changed files
2. Streams AI-generated explanations as inline comments
3. Places comments at relevant code locations to explain what changed and why
The AI uses the full conversation context to provide meaningful explanations, not just describing what code does, but explaining the reasoning behind the changes.
## Interactive Comment Threads
One of the most powerful aspects of Explain Changes is that the comments are fully interactive. You can have conversations directly within each comment thread.
### Asking Follow-up Questions
Each explanation comment has a reply input where you can ask questions about that specific piece of code:
- "Why did you use this approach instead of X?"
- "Can you explain this pattern in more detail?"
- "What would happen if we changed this to Y?"
The AI will respond with context-aware answers, understanding both the code being discussed and the original task context.
### Moving to Main Chat
If a conversation in a comment thread becomes complex or you want to continue working on that code, click the title area of the comment thread to move the entire conversation into Cline's main chat input. This lets you:
- Continue the discussion with full Cline capabilities
- Have Cline make additional changes based on the discussion
- Keep the context from your review conversation
## When to Use Explain Changes
### Learning and Onboarding
When you're new to a codebase or working with unfamiliar patterns, Explain Changes helps you understand not just what Cline did, but why. The explanations cover:
- Design decisions and trade-offs
- Technical concepts and patterns used
- Relationships between different changes
### Code Review
Use Explain Changes as part of your review process:
- Understand complex changes before committing
- Verify the AI's reasoning matches your expectations
- Catch potential issues by understanding the full context
### Knowledge Transfer
The explanations serve as documentation for your changes. When other team members review your code, they can see the reasoning behind each modification.
## Best Practices
1. **Ask specific questions**: The more specific your follow-up questions, the more useful the AI's responses will be.
2. **Use for complex changes**: Explain Changes is most valuable for multi-file changes or complex logic. For simple changes, the diff view alone may be sufficient.
3. **Move important discussions to chat**: If a comment thread reveals something that needs more work, move it to main chat to take action.
4. **Review before committing**: Use Explain Changes as a final check before committing changes to ensure you understand everything Cline did.
## Related Features
- [Checkpoints](/features/checkpoints) - Required for Explain Changes to work
- [/explain-changes](/features/slash-commands/explain-changes) - Slash command to explain any git diff
+419
View File
@@ -0,0 +1,419 @@
---
title: "Hooks"
sidebarTitle: "Hooks"
description: "Inject custom logic into Cline's workflow to validate operations, monitor tool usage, and shape AI decisions"
---
Hooks let you inject custom logic into Cline's workflow at key moments. Think of them as automated checkpoints where you can validate operations before they execute, monitor tool usage as it happens, and shape how Cline makes decisions.
Hooks run automatically when specific events happen during development. They receive detailed information about each operation, can block problematic actions before they cause issues, and can inject context that guides future AI decisions.
The real power comes from combining these capabilities. You can:
- Stop operations before they cause problems (like creating `.js` files in a TypeScript project)
- Learn from what's happening and build up project knowledge over time
- Monitor performance and catch issues as they emerge
- Track everything for analytics or compliance
- Trigger external tools or services at the right moments
<Warning>
Hooks are currently supported on macOS and Linux only. Windows support is not available.
</Warning>
## Getting Started
<Frame>
<img src="https://storage.googleapis.com/cline_public_images/hooks.gif" alt="Hooks in action" />
</Frame>
Enabling hooks in Cline is straightforward. Here's what you need to do:
<Steps>
<Step title="Enable Hooks in Settings">
Open Cline settings and check the **"Enable Hooks"** checkbox.
You can find this setting by:
1. Opening Cline
2. Click the "Settings" button on the top right corner
3. Click the "Feature" section in the left side navigation menu.
4. Scroll down until you see the "Enable Hooks" checkbox and check it.
</Step>
<Step title="Choose Your Hook Location">
Decide where to place your hooks:
**For personal or organization-wide hooks:**
- Create hooks in `~/Documents/Cline/Rules/Hooks/`
- These apply to all workspaces automatically
**For project-specific hooks:**
- Create hooks in `.clinerules/hooks/` in your project root
- These only apply to the specific workspace
- Commit them to version control so your team can use them too
</Step>
<Step title="Create Your First Hook">
Hook files must have exact names with no file extensions. For example, to create a TaskStart hook:
```bash
# Create the hook file
vim .clinerules/hooks/TaskStart
```
Add your script (must start with shebang)
``` bash
#!/usr/bin/env bash
# Store piped input into a variable
input=$(cat)
# Dump the entire JSON payload
echo "$input" | jq .
# Get the type of a field
echo "$input" | jq -r '.timestamp | type'
```
This example script demonstrates the key mechanics of hook input/output: reading the JSON payload from stdin with `input=$(cat)`, and using `jq` to inspect the data structure and field types that your hook receives. This helps you understand what data is available before building more complex hook logic.
**Make it executable**
```bash
chmod +x .clinerules/hooks/TaskStart
```
</Step>
<Step title="Test Your Hook">
Start a task in Cline and verify your hook executes.
</Step>
</Steps>
<Tip>
Start with a simple hook that just logs information before building complex validation logic. This helps you understand the data structure and timing.
</Tip>
## What You Can Build
Once you understand the basics, hooks open up creative possibilities:
<CardGroup cols={2}>
<Card title="Intelligent Code Review" icon="code-branch">
Run linters or custom validators before files get saved. Block commits that don't pass checks. Track code quality metrics over time.
</Card>
<Card title="Security Enforcement" icon="shield-halved">
Prevent operations that violate security policies. Detect when sensitive data might be exposed. Audit all file access for compliance.
</Card>
<Card title="Development Analytics" icon="chart-line">
Measure how long different operations take. Identify patterns in how the AI works. Generate productivity reports from hook data.
</Card>
<Card title="Integration Hub" icon="plug">
Connect to issue trackers when certain keywords appear. Update project management tools. Sync with external APIs at the right moments.
</Card>
</CardGroup>
The key is combining hooks with external tools. A hook can be the glue between Cline's workflow and the rest of your development ecosystem.
## Hook Types
Cline provides multiple hook types that let you tap into different stages of the AI workflow. They're organized into categories based on their trigger points and use cases.
<Note>
The hook names below are the exact file names you need to create. For example, to use the TaskStart hook, create a file named `TaskStart` (no file extension) in your hooks directory.
</Note>
Each hook receives base fields in addition to its specific data: `clineVersion`, `hookName`, `timestamp`, `taskId`, `workspaceRoots`, `userId`.
### Tool Execution
These hooks intercept and validate tool operations before and after they execute. Use them to enforce policies, track changes, and learn from operations.
#### PreToolUse
Runs before any tool executes. Use it to block invalid operations, validate parameters, and enforce project policies before changes happen.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "PreToolUse",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"preToolUse": {
"toolName": "string",
"parameters": {}
}
}
```
#### PostToolUse
Runs after a tool completes. Use it to learn from results, track performance metrics, and build project knowledge based on operations performed.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "PostToolUse",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"postToolUse": {
"toolName": "string",
"parameters": {},
"result": "string",
"success": boolean,
"executionTimeMs": number
}
}
```
### User Interaction
These hooks monitor and enhance user communication with Cline. Use them to validate input, inject context, and track interaction patterns.
#### UserPromptSubmit
Runs when a user sends a message to Cline. Use it to validate input, inject context based on the prompt, and track interaction patterns.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "UserPromptSubmit",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"userPromptSubmit": {
"prompt": "string",
"attachments": ["string"]
}
}
```
### Task Lifecycle
These hooks monitor and respond to task state changes from start to finish. Use them to track progress, restore state, and trigger workflows.
#### TaskStart
Runs when a new task begins. Use it to detect project type, initialize tracking, and inject initial context that shapes how Cline approaches the work.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "TaskStart",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskStart": {
"taskMetadata": {
"taskId": "string",
"ulid": "string",
"initialTask": "string"
}
}
}
```
#### TaskResume
Runs when a task resumes after interruption. Use it to restore state, refresh context, and log resumption for analytics or external system notifications.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "TaskResume",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskResume": {
"taskMetadata": {
"taskId": "string",
"ulid": "string"
},
"previousState": {
"lastMessageTs": "string",
"messageCount": "string",
"conversationHistoryDeleted": "string"
}
}
}
```
#### TaskCancel
Runs when a task is cancelled. Use it to cleanup resources, log cancellation details, and notify external systems about interrupted work.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "TaskCancel",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskCancel": {
"taskMetadata": {
"taskId": "string",
"ulid": "string",
"completionStatus": "string"
}
}
}
```
{/*
#### TaskComplete
Runs when a task finishes successfully. Use it for final cleanup, tracking metrics, generating reports, and triggering post-task workflows.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "TaskComplete",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskComplete": {
"taskMetadata": {
"taskId": "string",
"ulid": "string"
}
}
}
```
*/}
### System Events
These hooks monitor internal Cline operations and system-level events. Use them to track context usage, log system behavior, and analyze performance patterns.
{/*
#### PreCompact
Runs before conversation context is truncated to fit token limits. Use it to monitor compaction frequency, log events, and track context usage patterns.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "PreCompact",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"preCompact": {
"contextSize": number,
"messagesToCompact": number,
"compactionStrategy": "string"
}
}
```
*/}
### JSON Communication
Hooks receive JSON via stdin and return JSON via stdout.
**Output structure:**
```json
{
"cancel": false,
"contextModification": "WORKSPACE_RULES: Use TypeScript",
"errorMessage": "Error details if blocking"
}
```
Your hook script can output logging or diagnostic information to stdout during execution, as long as the JSON response is the last thing written. Cline will parse only the final JSON object from stdout.
For example:
```bash
#!/usr/bin/env bash
echo "Processing hook..." # This is fine
echo "Tool: $tool_name" # This is also fine
# The JSON must be last:
echo '{"cancel": false}'
```
The `cancel` field controls whether execution continues. Set it to `true` to block an action, `false` to allow it.
The `contextModification` field injects text into the conversation. This affects future AI decisions, not the current one. Use prefixes like `WORKSPACE_RULES:` or `PERFORMANCE:` to help categorize the context.
### Understanding Context Timing
Context injection affects future decisions, not current ones. When a hook runs:
1. The AI has already decided what to do
2. The hook can block or allow it
3. Any context gets added to the conversation
4. The next AI request sees that context
This means PreToolUse hooks are for blocking bad actions, while PostToolUse hooks are for learning from completed ones.
## Troubleshooting
### Hook Not Running
- Ensure the "Enable Hooks" setting is checked
- Verify the hook file is executable (`chmod +x hookname`)
- Check the hook file has no syntax errors
- Look for errors in VSCode's Output panel (Cline channel)
### Hook Timing Out
- Reduce complexity of the hook script
- Avoid expensive operations (network calls, heavy computations)
- Consider moving complex logic to a background process
### Context Not Affecting Behavior
Remember that context modifications affect future AI decisions, not the current operation. The AI's current behavior is based on the previous "API Request..." block, and your `contextModification` gets injected into the next "API Request..." block. This means if you need immediate effect, you should use PreToolUse hooks for validation and return `cancel: true` in your hook's JSON response to block Cline from continuing.
When adding context, ensure your modifications are clear and actionable so the AI can understand and apply them effectively. Also check that your context isn't being truncated due to the 50KB limit, as this could prevent important information from reaching the AI.
### Handling Strings with Quotes in JSON Payloads
When your hook needs to include strings containing unescaped quote characters (`"`) in JSON output, use jq's `--arg` flag for proper escaping:
```bash
#!/usr/bin/env bash
# When $output contains unescaped quote characters (")...
output='{"foo":"bar"}'
# Use the --arg flag for automatic string escaping
jq -n --arg ctx "$output" '{cancel: false, contextModification: $ctx}'
# This will result in:
# {
# "cancel": false,
# "contextModification": "{\"foo\":\"bar\"}"
# }
```
The `--arg` flag automatically escapes special characters, preventing JSON parsing errors when your context modification includes complex strings or nested JSON structures.
<Warning>
Hooks run with the same permissions as VS Code. They can access all workspace files and environment variables. Review hooks from untrusted sources before enabling them.
</Warning>
## Related Features
Hooks complement other Cline features:
- [Cline Rules](/features/cline-rules) define high-level guidance that hooks can enforce
- [Checkpoints](/features/checkpoints) let you roll back changes if a hook didn't catch an issue
- [Auto-Approve](/features/auto-approve) works well with hooks as safety nets for automated operations
-437
View File
@@ -1,437 +0,0 @@
---
title: "Hook Reference"
sidebarTitle: "Hook Reference"
description: "Complete API reference for all Cline hook types, JSON schemas, and field documentation"
---
This reference provides complete technical documentation for all hook types, their JSON schemas, input/output formats, and communication protocols.
## Hook Types
Cline provides multiple hook types that let you tap into different stages of the AI workflow. They're organized into categories based on their trigger points and use cases.
<Note>
The hook names below are the exact file names you need to create. For example, to use the TaskStart hook, create a file named `TaskStart` (no file extension) in your hooks directory.
</Note>
Each hook receives base fields in addition to its specific data: `clineVersion`, `hookName`, `timestamp`, `taskId`, `workspaceRoots`, `userId`.
### Tool Execution Hooks
These hooks intercept and validate tool operations before and after they execute. Use them to enforce policies, track changes, and learn from operations.
#### `PreToolUse`
Triggered immediately before Cline uses any tool (see the [Cline Tools Reference Guide](/cline-tools) for all available tools). Use it to block invalid operations, validate parameters, and enforce project policies before changes happen.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "PreToolUse",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"preToolUse": {
"toolName": "string",
"parameters": {}
}
}
```
**Example Usage:**
```bash
#!/usr/bin/env bash
input=$(cat)
# Block creating .js files in TypeScript projects
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
if [[ "$tool_name" == "write_to_file" ]]; then
file_path=$(echo "$input" | jq -r '.preToolUse.parameters.path')
if [[ "$file_path" == *.js ]] && [[ -f "tsconfig.json" ]]; then
echo '{"cancel": true, "errorMessage": "JavaScript files not allowed in TypeScript project"}'
exit 0
fi
fi
echo '{"cancel": false}'
```
#### `PostToolUse`
Triggered immediately after Cline uses any tool (see the [Cline Tools Reference Guide](/cline-tools) for all available tools). Use it to learn from results, track performance metrics, and build project knowledge based on operations performed.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "PostToolUse",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"postToolUse": {
"toolName": "string",
"parameters": {},
"result": "string",
"success": boolean,
"executionTimeMs": number
}
}
```
**Example Usage:**
```bash
#!/usr/bin/env bash
input=$(cat)
# Log slow operations for performance monitoring
execution_time=$(echo "$input" | jq -r '.postToolUse.executionTimeMs')
tool_name=$(echo "$input" | jq -r '.postToolUse.toolName')
if (( execution_time > 5000 )); then
context="PERFORMANCE: Slow operation detected - $tool_name took ${execution_time}ms"
echo "{\"cancel\": false, \"contextModification\": \"$context\"}"
else
echo '{"cancel": false}'
fi
```
### User Interaction Hooks
These hooks monitor and enhance user communication with Cline. Use them to validate input, inject context, and track interaction patterns.
#### `UserPromptSubmit`
Triggered when the user enters text into the prompt box and presses enter to start a new task, continue a completed task, or resume a cancelled task. Use it to validate input, inject context based on the prompt, and track interaction patterns.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "UserPromptSubmit",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"userPromptSubmit": {
"prompt": "string",
"attachments": ["string"]
}
}
```
**Example Usage:**
```bash
#!/usr/bin/env bash
input=$(cat)
# Inject coding standards context for certain keywords
prompt=$(echo "$input" | jq -r '.userPromptSubmit.prompt')
context=""
if echo "$prompt" | grep -qi "component\|react"; then
context="CODING_STANDARDS: Follow React functional component patterns with proper TypeScript types"
elif echo "$prompt" | grep -qi "api\|endpoint"; then
context="CODING_STANDARDS: Use consistent REST API patterns with proper error handling"
fi
if [[ -n "$context" ]]; then
jq -n --arg ctx "$context" '{"cancel": false, "contextModification": $ctx}'
else
echo '{"cancel": false}'
fi
```
### Task Lifecycle Hooks
These hooks monitor and respond to task state changes from start to finish. Use them to track progress, restore state, and trigger workflows.
#### `TaskStart`
Triggered once at the beginning of a new task. Use it to detect project type, initialize tracking, and inject initial context that shapes how Cline approaches the work.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "TaskStart",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskStart": {
"taskMetadata": {
"taskId": "string",
"ulid": "string",
"initialTask": "string"
}
}
}
```
**Example Usage:**
```bash
#!/usr/bin/env bash
input=$(cat)
# Detect project type and inject relevant context
context=""
if [[ -f "package.json" ]]; then
if grep -q "react" package.json; then
context="PROJECT_TYPE: React application detected. Follow component-based architecture."
elif grep -q "express" package.json; then
context="PROJECT_TYPE: Express.js API detected. Follow RESTful patterns."
else
context="PROJECT_TYPE: Node.js project detected."
fi
elif [[ -f "requirements.txt" ]] || [[ -f "pyproject.toml" ]]; then
context="PROJECT_TYPE: Python project detected. Follow PEP 8 standards."
elif [[ -f "Cargo.toml" ]]; then
context="PROJECT_TYPE: Rust project detected. Follow Rust conventions."
fi
if [[ -n "$context" ]]; then
jq -n --arg ctx "$context" '{"cancel": false, "contextModification": $ctx}'
else
echo '{"cancel": false}'
fi
```
#### `TaskResume`
Triggered when the user resumes a task that has been cancelled or aborted. Use it to restore state, refresh context, and log resumption for analytics or external system notifications.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "TaskResume",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskResume": {
"taskMetadata": {
"taskId": "string",
"ulid": "string"
},
"previousState": {
"lastMessageTs": "string",
"messageCount": "string",
"conversationHistoryDeleted": "string"
}
}
}
```
#### `TaskCancel`
Triggered when the user cancels a task or aborts a hook execution. Use it to cleanup resources, log cancellation details, and notify external systems about interrupted work.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "TaskCancel",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskCancel": {
"taskMetadata": {
"taskId": "string",
"ulid": "string"
}
}
}
```
#### `TaskComplete`
Triggered when Cline finishes its work and successfully executes the `attempt_completion` tool to finalize the task output. Use it to track completion metrics, generate reports, log task outcomes, and trigger completion workflows.
**Input Fields:**
```json
{
"clineVersion": "string",
"hookName": "TaskComplete",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"taskComplete": {
"taskMetadata": {
"taskId": "string",
"ulid": "string"
}
}
}
```
**Example Usage:**
```bash
#!/usr/bin/env bash
input=$(cat)
# Extract task metadata
task_id=$(echo "$input" | jq -r '.taskComplete.taskMetadata.taskId // "unknown"')
ulid=$(echo "$input" | jq -r '.taskComplete.taskMetadata.ulid // "unknown"')
# Log completion
completion_log="$HOME/.cline_completions/$(date +%Y-%m-%d).log"
mkdir -p "$(dirname "$completion_log")"
echo "$(date -Iseconds): Task $task_id completed (ULID: $ulid)" >> "$completion_log"
# Provide context about completion
context="TASK_COMPLETED: Task $task_id finished successfully. Completion logged."
jq -n --arg ctx "$context" '{"cancel": false, "contextModification": $ctx}'
```
### System Events Hooks
These hooks monitor internal Cline operations and system-level events. Use them to track context usage, log system behavior, and analyze performance patterns.
## JSON Communication Protocol
Hooks receive JSON via stdin and return JSON via stdout.
### Input Format
All hooks receive a JSON object through stdin with this base structure:
```json
{
"clineVersion": "string",
"hookName": "string",
"timestamp": "string",
"taskId": "string",
"workspaceRoots": ["string"],
"userId": "string",
"[hookSpecificField]": {
// Hook-specific data structure
}
}
```
### Output Format
Your hook script must output a JSON response as the final stdout content:
```json
{
"cancel": false,
"contextModification": "WORKSPACE_RULES: Use TypeScript",
"errorMessage": "Error details if blocking"
}
```
**Field Descriptions:**
- **`cancel`** (required): Boolean controlling whether execution continues
- `true`: Block the current action
- `false`: Allow the action to proceed
- **`contextModification`** (optional): String that gets injected into the conversation
- Affects future AI decisions, not the current one
- Use clear prefixes like `WORKSPACE_RULES:`, `PERFORMANCE:`, `SECURITY:` for categorization
- Maximum length: 50KB
- **`errorMessage`** (optional): String shown to user when `cancel` is `true`
- Only displayed when blocking an action
- Should explain why the action was blocked
### Logging During Execution
Your hook script can output logging or diagnostic information to stdout during execution, as long as the JSON response is the last thing written:
```bash
#!/usr/bin/env bash
echo "Processing hook..." # This is fine
echo "Tool: $tool_name" # This is also fine
# The JSON must be last:
echo '{"cancel": false}'
```
Cline will parse only the final JSON object from stdout.
### Error Handling
Hook execution errors don't prevent task execution - only returning `"cancel": true` can halt a task. All other errors are treated as hook failures, not reasons to abort the task.
**Hook Status Display:**
- **Completed** (grey): Hook executed successfully, regardless of whether it returned `"cancel": false` or no JSON output
- **Failed** (red): Hook exited with non-zero status, output invalid JSON, or timed out. The UI displays the error details (e.g., exit code number)
- **Aborted** (red): Hook returned `"cancel": true`, halting the task. User must manually resume the task to continue
**Important:** Even when a hook fails (non-zero exit, invalid JSON, timeout), Cline continues with the task. Only `"cancel": true` stops execution.
### Context Modification Timing
Context injection affects future decisions, not current ones. When a hook runs:
1. The AI has already decided what to do
2. The hook can block or allow it
3. Any context gets added to the conversation
4. The next AI request sees that context
This means:
- **PreToolUse hooks**: Use for blocking bad actions + injecting context for next decision
- **PostToolUse hooks**: Use for learning from completed actions
### Helpful Tip: String Escaping in JSON
When your hook needs to include strings containing unescaped quote characters (`"`) in JSON output, use jq's `--arg` flag for proper escaping:
```bash
#!/usr/bin/env bash
# When $output contains unescaped quote characters (")...
output='{"foo":"bar"}'
# Use the --arg flag for automatic string escaping
jq -n --arg ctx "$output" '{cancel: false, contextModification: $ctx}'
# This will result in:
# {
# "cancel": false,
# "contextModification": "{\"foo\":\"bar\"}"
# }
```
The `--arg` flag automatically escapes special characters, preventing JSON parsing errors when your context modification includes complex strings or nested JSON structures.
## Hook Execution Environment
### Execution Context
Hooks are executable scripts that run with the same permissions as VS Code. They have unrestricted access to:
- The entire filesystem (any file the user can access)
- All environment variables
- System commands and tools
- Network resources
Hooks can perform any operation the user could perform in a terminal, including reading and writing files outside the workspace, making network requests, and executing system commands.
### Security Considerations
<Warning>
Hooks run with the same permissions as VS Code. They can access all workspace files and environment variables. Review hooks from untrusted sources before enabling them.
</Warning>
### Performance Guidelines
Hooks have a 30 second timeout. As long as your hook completes within this time, it can perform any operations needed, including network calls or heavy computations.
### Hook Discovery
Cline searches for hooks in this order:
1. Project-specific: `.clinerules/hooks/` in workspace root
2. User-global: `~/Documents/Cline/Rules/Hooks/`
Project-specific hooks override global hooks with the same name.
-146
View File
@@ -1,146 +0,0 @@
---
title: "Hooks Overview"
sidebarTitle: "Overview"
description: "Inject custom logic into Cline's workflow to validate operations, monitor tool usage, and shape AI decisions"
---
Hooks let you inject custom logic into Cline's workflow at key moments. Think of them as automated checkpoints where you can validate operations before they execute, monitor tool usage as it happens, and shape how Cline makes decisions.
Hooks run automatically when specific events happen during development. They receive detailed information about each operation, can block problematic actions before they cause issues, and can inject context that guides future AI decisions.
The real power comes from combining these capabilities. You can:
- Stop operations before they cause problems (like creating `.js` files in a TypeScript project)
- Learn from what's happening and build up project knowledge over time
- Monitor performance and catch issues as they emerge
- Track everything for analytics or compliance
- Trigger external tools or services at the right moments
## Getting Started
<Frame>
<img src="https://storage.googleapis.com/cline_public_images/hooks.gif" alt="Hooks in action" />
</Frame>
<Note>
Hooks work across all platforms: Windows, macOS, and Linux. The bash examples in this documentation work with standard shells on all platforms (including Git Bash or WSL on Windows).
</Note>
Setting up hooks in Cline is user-friendly with the built-in hooks management interface. Here's how to get started:
<Steps>
<Step title="Access the Hooks Interface">
Navigate to the Hooks management interface:
<Frame>
<img src="/assets/hooks/hooks-interface-with-dropdown.png" alt="Hooks management interface showing Global Hooks and project-specific hooks with dropdown menu" />
</Frame>
1. Open Cline (ensure hooks are enabled in settings)
2. Look for the **Hooks** tab at the top (alongside Rules and Workflows)
3. Click on **Hooks** to open the hooks management panel
The interface shows you all available hook types and existing hooks organized by workspace.
</Step>
<Step title="Understand Hook Locations">
Hooks are automatically organized by location in the interface:
**Global Hooks** - Apply to all workspaces:
- Stored in `~/Documents/Cline/Rules/Hooks/`
- Perfect for personal coding standards and universal rules
**Project-Specific Hooks** - Apply only to current project:
- Stored in `.clinerules/hooks/` within your repo
- Great for project-specific validation and team workflows
- Can be committed to version control for team sharing
Multi-root workspaces run hooks from all of the repos in your open workspace, making it easy to manage and run hooks across different repos within the same workspace.
</Step>
<Step title="Create Your First Hook">
Use the intuitive interface to create hooks:
<Frame>
<img src="/assets/hooks/hooks-empty-state.png" alt="Empty hooks interface showing New hook... dropdowns for both Global Hooks and project-specific hooks before any hooks are created" />
</Frame>
1. **Choose your location**: Decide between Global Hooks or project-specific hooks
2. **Select hook type**: Click the **"New hook..."** dropdown in your chosen location
3. **Pick a hook type**: The dropdown shows all available hook types that haven't been created yet in this location. Only one of each hook type is allowed per hooks directory, so the dropdown automatically filters to show only the remaining available types.
<Frame>
<img src="/assets/hooks/new-hook-dropdown.png" alt="Creating a new hook with the dropdown menu showing UserPromptSubmit selected with description" />
</Frame>
4. **Review and edit the hook**: Click the pencil icon to review the hook's code and add your custom logic
5. **Enable the hook**: Once you understand and approve of the hook's behavior, toggle the switch to activate it
<Frame>
<img src="/assets/hooks/hook-controls.png" alt="Hook management controls showing toggle, edit, and delete buttons for each hook" />
</Frame>
<Warning>
Always review a hook's code before enabling it. Hooks execute automatically during your workflow, so it's important to understand what they do before activation.
</Warning>
</Step>
<Step title="Test Your Hook">
To develop and refine your hook, you'll need to trigger it multiple times during testing. Each hook type is triggered by different events in Cline's workflow. For example:
- **TaskStart** hooks trigger when you start a new task
- **PreToolUse** hooks trigger before Cline executes tools like file editing
- **PostToolUse** hooks trigger after tool execution completes
- **UserPromptSubmit** hooks trigger when you submit a message to Cline
For complete details on when each hook type is triggered and how to test them effectively, see the [Hook Reference](/features/hooks/hook-reference) documentation. This includes the specific conditions that trigger each hook and examples of how to invoke them during development.
</Step>
</Steps>
<Tip>
Start with a simple hook that just logs information before building complex validation logic. This helps you understand the data structure and timing.
</Tip>
## What You Can Build
Once you understand the basics, hooks open up creative possibilities:
<CardGroup cols={2}>
<Card title="Intelligent Code Review" icon="code-branch">
Run linters or custom validators before files get saved. Block commits that don't pass checks. Track code quality metrics over time.
</Card>
<Card title="Security Enforcement" icon="shield-halved">
Prevent operations that violate security policies. Detect when sensitive data might be exposed. Audit all file access for compliance.
</Card>
<Card title="Development Analytics" icon="chart-line">
Measure how long different operations take. Identify patterns in how the AI works. Generate productivity reports from hook data.
</Card>
<Card title="Integration Hub" icon="plug">
Connect to issue trackers when certain keywords appear. Update project management tools. Sync with external APIs at the right moments.
</Card>
</CardGroup>
The key is combining hooks with external tools. A hook can be the glue between Cline's workflow and the rest of your development ecosystem.
## Explore the Documentation
<CardGroup cols={2}>
<Card title="Hook Reference" icon="book" href="/features/hooks/hook-reference">
Complete API reference for all hook types, JSON schemas, and field documentation.
</Card>
<Card title="Samples" icon="code" href="/features/hooks/samples">
Practical examples and complete working scripts for common use cases.
</Card>
</CardGroup>
## Related Features
Hooks complement other Cline features:
- [Cline Rules](/features/cline-rules) define high-level guidance that hooks can enforce
- [Checkpoints](/features/checkpoints) let you roll back changes if a hook didn't catch an issue
- [Auto-Approve](/features/auto-approve) works well with hooks as safety nets for automated operations
-755
View File
@@ -1,755 +0,0 @@
---
title: "Samples"
sidebarTitle: "Samples"
description: "Practical hook examples organized by complexity level - from beginner to advanced patterns"
---
This page provides complete, production-ready hook examples organized by skill level. Each example includes full working code, detailed explanations, and guidance on when to use each pattern.
## How to Use These Samples
Each sample is designed to be:
- **Copy-and-paste ready**: Use them directly or as starting points
- **Educational**: Learn hook concepts through progressive complexity
- **Practical**: Solve real development workflow challenges
Choose samples based on your experience level and gradually work up to more advanced patterns.
---
## Beginner Examples
Perfect for getting started with hooks. These examples demonstrate core concepts with straightforward logic.
### 1. Project Type Detection
**Hook:** `TaskStart`
```bash
#!/usr/bin/env bash
# Project Type Detection Hook
#
# Overview: Automatically detects project type at task start and injects relevant
# coding standards and best practices into the AI context. This helps Cline understand
# your project structure and apply appropriate conventions from the beginning.
#
# Demonstrates: Basic hook input/output, file system checks, conditional logic,
# and context injection to guide AI behavior.
input=$(cat)
# Read basic JSON structure and detect project type
context=""
# Check for different project indicators
if [[ -f "package.json" ]]; then
if grep -q "react" package.json; then
context="PROJECT_TYPE: React application detected. Follow component-based architecture and use functional components."
elif grep -q "express" package.json; then
context="PROJECT_TYPE: Express.js API detected. Follow RESTful patterns and proper middleware structure."
else
context="PROJECT_TYPE: Node.js project detected. Use proper npm scripts and dependency management."
fi
elif [[ -f "requirements.txt" ]] || [[ -f "pyproject.toml" ]]; then
context="PROJECT_TYPE: Python project detected. Follow PEP 8 standards and use virtual environments."
elif [[ -f "Cargo.toml" ]]; then
context="PROJECT_TYPE: Rust project detected. Follow Rust conventions and use proper error handling."
elif [[ -f "go.mod" ]]; then
context="PROJECT_TYPE: Go project detected. Follow Go conventions and use proper package structure."
fi
# Return the context to guide Cline's behavior
if [[ -n "$context" ]]; then
jq -n --arg ctx "$context" '{"cancel": false, "contextModification": $ctx}'
else
echo '{"cancel": false}'
fi
```
**Key Concepts:**
- Reading hook input with `input=$(cat)`
- Using file system checks to detect project type
- Returning context to influence AI behavior
- Basic JSON output with `jq`
### 2. File Extension Validator
**Hook:** `PreToolUse`
```bash
#!/usr/bin/env bash
# File Extension Validator Hook
#
# Overview: Enforces TypeScript file extensions in TypeScript projects by blocking
# creation of .js and .jsx files. This prevents common mistakes where developers
# accidentally create JavaScript files when they should be using TypeScript.
#
# Demonstrates: PreToolUse blocking, parameter extraction, conditional validation,
# and providing clear error messages to guide users toward correct file extensions.
input=$(cat)
# Extract tool information
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
# Only process file creation tools
if [[ "$tool_name" != "write_to_file" ]]; then
echo '{"cancel": false}'
exit 0
fi
# Check if this is a TypeScript project
if [[ ! -f "tsconfig.json" ]]; then
echo '{"cancel": false}'
exit 0
fi
# Get the file path from tool parameters
file_path=$(echo "$input" | jq -r '.preToolUse.parameters.path // empty')
if [[ -z "$file_path" ]]; then
echo '{"cancel": false}'
exit 0
fi
# Block .js files in TypeScript projects
if [[ "$file_path" == *.js ]]; then
echo '{"cancel": true, "errorMessage": "JavaScript files (.js) are not allowed in TypeScript projects. Use .ts extension instead."}'
exit 0
fi
# Block .jsx files, suggest .tsx
if [[ "$file_path" == *.jsx ]]; then
echo '{"cancel": true, "errorMessage": "JSX files (.jsx) are not allowed in TypeScript projects. Use .tsx extension instead."}'
exit 0
fi
# Everything is OK
echo '{"cancel": false}'
```
**Key Concepts:**
- Extracting tool name and parameters
- Conditional logic based on project state
- Blocking operations with `"cancel": true`
- Providing helpful error messages
### 3. Basic Performance Monitor
**Hook:** `PostToolUse`
```bash
#!/usr/bin/env bash
# Basic Performance Monitor Hook
#
# Overview: Monitors tool execution times and logs operations that exceed a 3-second
# threshold. This helps identify performance bottlenecks and provides feedback to
# users about system resource issues that may be slowing down Cline's operations.
#
# Demonstrates: PostToolUse hook usage, arithmetic operations in bash, simple file
# logging, and conditional context injection based on performance metrics.
input=$(cat)
# Extract performance information
tool_name=$(echo "$input" | jq -r '.postToolUse.toolName')
execution_time=$(echo "$input" | jq -r '.postToolUse.executionTimeMs // 0')
success=$(echo "$input" | jq -r '.postToolUse.success')
# Log slow operations (threshold: 3 seconds)
if (( execution_time > 3000 )); then
# Create simple log directory
mkdir -p "$HOME/.cline_logs"
# Log the slow operation
echo "$(date -Iseconds): SLOW OPERATION - $tool_name took ${execution_time}ms" >> "$HOME/.cline_logs/performance.log"
# Provide feedback to user
context="PERFORMANCE: Operation $tool_name took ${execution_time}ms. Consider checking system resources if this happens frequently."
jq -n --arg ctx "$context" '{"cancel": false, "contextModification": $ctx}'
else
echo '{"cancel": false}'
fi
```
**Key Concepts:**
- Processing results after tool execution
- Basic arithmetic operations in bash
- Simple file logging
- Conditional context injection
## Intermediate Examples
These examples demonstrate more advanced concepts including external tool integration, pattern matching, and structured logging.
### 4. Code Quality with Linting
**Hook:** `PreToolUse`
```bash
#!/usr/bin/env bash
# Code Quality Linting Hook
#
# Overview: Integrates ESLint and Flake8 to enforce code quality standards before
# files are written. Blocks file creation if linting errors are detected, ensuring
# all code meets quality standards. Supports TypeScript, JavaScript, and Python files.
#
# Demonstrates: External tool integration, temporary file handling, regex pattern
# matching, and comprehensive error reporting with actionable feedback.
input=$(cat)
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
# Only lint file write operations
if [[ "$tool_name" != "write_to_file" ]]; then
echo '{"cancel": false}'
exit 0
fi
file_path=$(echo "$input" | jq -r '.preToolUse.parameters.path // empty')
# Skip non-code files
if [[ ! "$file_path" =~ \.(ts|tsx|js|jsx|py|rs)$ ]]; then
echo '{"cancel": false}'
exit 0
fi
# Get file content from the tool parameters
content=$(echo "$input" | jq -r '.preToolUse.parameters.content // empty')
if [[ -z "$content" ]]; then
echo '{"cancel": false}'
exit 0
fi
# Create temporary file for linting
temp_file=$(mktemp)
echo "$content" > "$temp_file"
# Run appropriate linter based on file extension
lint_errors=""
if [[ "$file_path" =~ \.(ts|tsx)$ ]] && command -v eslint > /dev/null; then
lint_output=$(eslint "$temp_file" --format=json 2>/dev/null || true)
if [[ "$lint_output" != "[]" ]] && [[ -n "$lint_output" ]]; then
error_count=$(echo "$lint_output" | jq '.[0].errorCount // 0')
if (( error_count > 0 )); then
messages=$(echo "$lint_output" | jq -r '.[0].messages[] | "\(.line):\(.column) \(.message)"')
lint_errors="ESLint errors found:\n$messages"
fi
fi
elif [[ "$file_path" =~ \.py$ ]] && command -v flake8 > /dev/null; then
lint_output=$(flake8 "$temp_file" 2>/dev/null || true)
if [[ -n "$lint_output" ]]; then
lint_errors="Flake8 errors found:\n$lint_output"
fi
fi
# Cleanup
rm -f "$temp_file"
# Block if linting errors found
if [[ -n "$lint_errors" ]]; then
error_message="Code quality check failed. Please fix these issues:\n\n$lint_errors"
jq -n --arg msg "$error_message" '{"cancel": true, "errorMessage": $msg}'
else
echo '{"cancel": false}'
fi
```
**Key Concepts:**
- Temporary file creation and cleanup
- External tool integration (eslint, flake8)
- Complex pattern matching with regex
- Structured error reporting
### 5. Security Scanner
**Hook:** `PreToolUse`
```bash
#!/usr/bin/env bash
# Security Scanner Hook
#
# Overview: Scans file content for hardcoded secrets (API keys, tokens, passwords)
# before files are written. Blocks creation of files containing secrets except in
# safe locations like .env.example files or documentation, preventing credential leaks.
#
# Demonstrates: Pattern matching with regex arrays, file path exception handling,
# security-focused validation, and clear user guidance in error messages.
input=$(cat)
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
# Only check file operations
if [[ "$tool_name" != "write_to_file" ]]; then
echo '{"cancel": false}'
exit 0
fi
content=$(echo "$input" | jq -r '.preToolUse.parameters.content // empty')
file_path=$(echo "$input" | jq -r '.preToolUse.parameters.path // empty')
# Skip if no content
if [[ -z "$content" ]]; then
echo '{"cancel": false}'
exit 0
fi
# Define secret patterns (simplified for readability)
secrets_found=""
# Check for API keys
if echo "$content" | grep -qi "api[_-]*key.*[=:].*['\"][a-z0-9_-]{10,}['\"]"; then
secrets_found+="- API key pattern detected\n"
fi
# Check for tokens
if echo "$content" | grep -qi "token.*[=:].*['\"][a-z0-9_-]{10,}['\"]"; then
secrets_found+="- Token pattern detected\n"
fi
# Check for passwords
if echo "$content" | grep -qi "password.*[=:].*['\"][^'\"]{8,}['\"]"; then
secrets_found+="- Password pattern detected\n"
fi
# Allow secrets in safe files
safe_patterns=("\.env\.example$" "\.env\.template$" "/docs/" "\.md$")
is_safe_file=false
for safe_pattern in "${safe_patterns[@]}"; do
if [[ "$file_path" =~ $safe_pattern ]]; then
is_safe_file=true
break
fi
done
if [[ -n "$secrets_found" ]] && [[ "$is_safe_file" == false ]]; then
error_message="🔒 SECURITY ALERT: Potential secrets detected in $file_path
$secrets_found
Please use environment variables or a secrets management service instead."
jq -n --arg msg "$error_message" '{"cancel": true, "errorMessage": $msg}'
else
echo '{"cancel": false}'
fi
```
**Key Concepts:**
- Pattern arrays and iteration
- File path exception handling
- Security-focused validation
- Clear user guidance in error messages
### 6. Git Workflow Assistant
**Hook:** `PostToolUse`
```bash
#!/usr/bin/env bash
# Git Workflow Assistant Hook
#
# Overview: Analyzes file modifications and provides intelligent git workflow suggestions
# based on file types and current branch. Encourages best practices like feature branches
# for components and test branches for test files, with actionable git commands.
#
# Demonstrates: Git integration, branch analysis, file path pattern matching, and
# contextual suggestions to guide users toward better git practices.
input=$(cat)
tool_name=$(echo "$input" | jq -r '.postToolUse.toolName')
success=$(echo "$input" | jq -r '.postToolUse.success')
# Only process successful file modifications
if [[ "$success" != "true" ]] || [[ "$tool_name" != "write_to_file" && "$tool_name" != "replace_in_file" ]]; then
echo '{"cancel": false}'
exit 0
fi
# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo '{"cancel": false}'
exit 0
fi
file_path=$(echo "$input" | jq -r '.postToolUse.parameters.path // empty')
current_branch=$(git branch --show-current 2>/dev/null || echo "main")
# Analyze file type and suggest appropriate branch naming
context=""
if [[ "$file_path" == *"component"* ]] && [[ "$current_branch" == "main" || "$current_branch" == "master" ]]; then
component_name=$(basename "$file_path" .tsx .ts .jsx .js)
context="GIT_WORKFLOW: Consider creating a feature branch: git checkout -b feature/add-${component_name,,}-component"
elif [[ "$file_path" == *"test"* ]] || [[ "$file_path" == *"spec"* ]]; then
if [[ "$current_branch" == "main" || "$current_branch" == "master" ]]; then
context="GIT_WORKFLOW: Consider creating a test branch: git checkout -b test/add-tests-$(basename "$(dirname "$file_path")")"
fi
fi
# Add staging guidance
if [[ -n "$context" ]]; then
context="$context After completing changes, use 'git add $file_path' to stage for commit."
else
context="GIT_WORKFLOW: File modified: $file_path. Use 'git add $file_path' when ready to commit."
fi
jq -n --arg ctx "$context" '{"cancel": false, "contextModification": $ctx}'
```
**Key Concepts:**
- Git repository detection
- Branch analysis and suggestions
- File path analysis for context
- Actionable user guidance
## Advanced Examples
These examples showcase sophisticated patterns including external integrations, asynchronous processing, and complex state management.
### 7. Comprehensive Task Lifecycle Manager
**Hook:** `TaskComplete`
```bash
#!/usr/bin/env bash
# Comprehensive Task Lifecycle Manager Hook
#
# Overview: Tracks task completions by generating detailed markdown reports with
# workspace information and git state, and optionally sends webhook notifications
# to external systems. Perfect for enterprise environments requiring audit trails.
#
# Demonstrates: Complex data extraction, structured report generation, markdown
# heredocs, asynchronous webhook notifications, and robust error handling.
input=$(cat)
# Extract task metadata using proper API field paths
task_id=$(echo "$input" | jq -r '.taskId')
ulid=$(echo "$input" | jq -r '.taskComplete.taskMetadata.ulid // "unknown"')
completion_time=$(echo "$input" | jq -r '.timestamp')
# Create completion report directory with error handling
reports_dir="$HOME/.cline_reports"
if [[ ! -d "$(dirname "$reports_dir")" ]]; then
echo '{"cancel": false, "errorMessage": "Cannot access home directory"}'
exit 0
fi
mkdir -p "$reports_dir" || exit 0
# Generate safe, unique report filename
safe_task_id=$(echo "$task_id" | tr -cd '[:alnum:]_-' | head -c 50)
report_file="$reports_dir/completion_$(date +%Y%m%d_%H%M%S)_${safe_task_id}.md"
# Collect comprehensive workspace information
git_branch=$(git branch --show-current 2>/dev/null || echo "No git repository")
git_status_count=$(git status --porcelain 2>/dev/null | wc -l || echo "0")
project_name=$(basename "$PWD")
# Generate detailed completion report
cat > "$report_file" << EOF
# Cline Task Completion Report
**Task ID:** $task_id
**ULID:** $ulid
**Completed:** $(date -Iseconds)
**Completion Time:** $completion_time
## Workspace Information
- **Project:** $project_name
- **Git Branch:** $git_branch
- **Modified Files:** $git_status_count
## Completion Status
✅ Task completed successfully
## Next Steps
- Review changes made during this task
- Consider committing changes if appropriate
- Run tests to verify functionality
EOF
# Send webhook notification if configured
webhook_url="${COMPLETION_WEBHOOK_URL:-}"
if [[ -n "$webhook_url" ]]; then
payload=$(jq -n \
--arg task_id "$task_id" \
--arg ulid "$ulid" \
--arg workspace "$project_name" \
--arg timestamp "$completion_time" \
'{
event: "task_completed",
task_id: $task_id,
ulid: $ulid,
workspace: $workspace,
timestamp: $timestamp
}')
# Send notification in background with timeout
(curl -X POST \
-H "Content-Type: application/json" \
-d "$payload" \
"$webhook_url" \
--max-time 5 \
--silent > /dev/null 2>&1) &
fi
context="TASK_COMPLETED: ✅ Task $task_id finished successfully. Report saved to: $(basename "$report_file")"
jq -n --arg ctx "$context" '{"cancel": false, "contextModification": $ctx}'
```
**Key Concepts:**
- Complex data extraction and validation
- Structured report generation
- Asynchronous webhook notifications
- Error handling and resource management
### 8. Intelligent User Input Enhancer
**Hook:** `UserPromptSubmit`
```bash
#!/usr/bin/env bash
# Intelligent User Input Enhancer Hook
#
# Overview: Analyzes user prompts to detect potentially harmful commands, logs user
# activity for analytics, and intelligently injects project and git context based on
# prompt keywords. Provides safety guards while enhancing AI responses with relevant context.
#
# Demonstrates: UserPromptSubmit hook usage, multi-pattern safety validation, intelligent
# context detection from prompts, structured JSON logging, and dynamic suggestion generation.
input=$(cat)
user_prompt=$(echo "$input" | jq -r '.userPromptSubmit.prompt')
task_id=$(echo "$input" | jq -r '.taskId')
user_id=$(echo "$input" | jq -r '.userId')
# Log user activity for analytics
activity_log="$HOME/.cline_user_activity/$(date +%Y-%m-%d).log"
mkdir -p "$(dirname "$activity_log")"
activity_entry=$(jq -n \
--arg timestamp "$(date -Iseconds)" \
--arg task_id "$task_id" \
--arg user_id "$user_id" \
--arg prompt_length "${#user_prompt}" \
'{
timestamp: $timestamp,
task_id: $task_id,
user_id: $user_id,
prompt_length: ($prompt_length | tonumber),
workspace: env.PWD
}')
echo "$activity_entry" >> "$activity_log"
context_modifications=""
cancel_request=false
# Safety validation
harmful_patterns=("rm -rf" "delete.*all" "format.*drive" "sudo.*passwd")
for pattern in "${harmful_patterns[@]}"; do
if echo "$user_prompt" | grep -qi "$pattern"; then
cancel_request=true
error_message="🚨 SAFETY ALERT: Potentially harmful command detected. Please review your request."
break
fi
done
# Intelligent context enhancement
if [[ "$cancel_request" == false ]]; then
# Detect project context
if echo "$user_prompt" | grep -qi "file\|directory\|folder"; then
if [[ -f "package.json" ]]; then
project_name=$(jq -r '.name // "unknown"' package.json 2>/dev/null)
context_modifications+="PROJECT_CONTEXT: Working in Node.js project '$project_name'. "
elif [[ -f "requirements.txt" ]]; then
context_modifications+="PROJECT_CONTEXT: Working in Python project. "
fi
fi
# Git context enhancement
if echo "$user_prompt" | grep -qi "git\|commit\|branch" && git rev-parse --git-dir > /dev/null 2>&1; then
current_branch=$(git branch --show-current 2>/dev/null)
uncommitted=$(git status --porcelain | wc -l)
context_modifications+="GIT_CONTEXT: On branch '$current_branch' with $uncommitted uncommitted changes. "
fi
# Tool suggestions
if echo "$user_prompt" | grep -qi "search.*code\|find.*function"; then
context_modifications+="SUGGESTION: Consider using search_files tool for code exploration. "
fi
fi
# Return response
if [[ "$cancel_request" == true ]]; then
jq -n --arg msg "$error_message" '{"cancel": true, "errorMessage": $msg}'
else
if [[ -n "$context_modifications" ]]; then
jq -n --arg ctx "$context_modifications" '{"cancel": false, "contextModification": $ctx}'
else
echo '{"cancel": false}'
fi
fi
```
**Key Concepts:**
- User interaction analysis and logging
- Multi-pattern safety validation
- Intelligent context detection
- Dynamic suggestion generation
### 9. Multi-Service Integration Hub
**Hook:** `PostToolUse`
```bash
#!/usr/bin/env bash
# Multi-Service Integration Hub Hook
#
# Overview: Detects file modifications by type (dependencies, CI/CD, frontend, backend, tests)
# and sends asynchronous webhook notifications to multiple external services like Slack and
# CI/CD systems. Enables seamless integration of Cline operations into enterprise workflows.
#
# Demonstrates: Advanced pattern matching with associative arrays, multi-service webhook
# orchestration, asynchronous background processing, and enterprise notification patterns.
input=$(cat)
tool_name=$(echo "$input" | jq -r '.postToolUse.toolName')
success=$(echo "$input" | jq -r '.postToolUse.success')
file_path=$(echo "$input" | jq -r '.postToolUse.parameters.path // empty')
# Only process successful file operations
if [[ "$success" != "true" ]] || [[ "$tool_name" != "write_to_file" && "$tool_name" != "replace_in_file" ]]; then
echo '{"cancel": false}'
exit 0
fi
# Define workflow triggers
declare -A triggers=(
["package\\.json|yarn\\.lock"]="dependencies"
["\\.github/workflows/"]="ci_cd"
["src/.*component"]="frontend"
["api/.*\\.(ts|js)"]="backend"
[".*\\.(test|spec)\\."]="testing"
)
# Determine triggered workflows
triggered_workflows=""
for pattern in "${!triggers[@]}"; do
if [[ "$file_path" =~ $pattern ]]; then
workflow_type="${triggers[$pattern]}"
triggered_workflows+="$workflow_type "
fi
done
context="WORKFLOW: File modified: $file_path"
if [[ -n "$triggered_workflows" ]]; then
# Slack notification (async)
slack_webhook="${SLACK_WEBHOOK_URL:-}"
if [[ -n "$slack_webhook" ]]; then
slack_payload=$(jq -n \
--arg file "$file_path" \
--arg workflows "$triggered_workflows" \
--arg workspace "$(basename "$PWD")" \
'{
text: ("🔧 Cline modified `" + $file + "` in " + $workspace),
color: "good",
fields: [{
title: "Triggered Workflows",
value: $workflows,
short: true
}]
}')
(curl -X POST -H "Content-Type: application/json" -d "$slack_payload" "$slack_webhook" --max-time 5 --silent > /dev/null 2>&1) &
fi
# CI/CD webhook (async)
ci_webhook="${CI_WEBHOOK_URL:-}"
if [[ -n "$ci_webhook" ]]; then
ci_payload=$(jq -n \
--arg file "$file_path" \
--arg workflows "$triggered_workflows" \
'{
event: "file_modified",
file_path: $file,
workflows: ($workflows | split(" "))
}')
(curl -X POST -H "Content-Type: application/json" -d "$ci_payload" "$ci_webhook" --max-time 5 --silent > /dev/null 2>&1) &
fi
context+=" Triggered workflows: $triggered_workflows. Notifications sent to configured services."
fi
jq -n --arg ctx "$context" '{"cancel": false, "contextModification": $ctx}'
```
**Key Concepts:**
- Multi-service integration patterns
- Asynchronous webhook orchestration
- Complex workflow detection
- Enterprise notification systems
## Usage Tips
### Running Multiple Hooks
You can use multiple hooks together by creating separate files for each hook type:
```bash
# Create hooks directory
mkdir -p .clinerules/hooks
# Create multiple hooks
touch .clinerules/hooks/PreToolUse
touch .clinerules/hooks/PostToolUse
touch .clinerules/hooks/TaskStart
# Make them executable
chmod +x .clinerules/hooks/*
```
### Environment Configuration
Set up environment variables for external integrations:
```bash
# Add to your .bashrc or .zshrc
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."
export JIRA_URL="https://yourcompany.atlassian.net"
export JIRA_USER="your-email@company.com"
export JIRA_TOKEN="your-api-token"
export CI_WEBHOOK_URL="https://your-ci-system.com/hooks/cline"
```
### Testing Your Hooks
Test hooks manually by simulating their input:
```bash
# Test a PreToolUse hook
echo '{
"clineVersion": "1.0.0",
"hookName": "PreToolUse",
"timestamp": "2024-01-01T12:00:00Z",
"taskId": "test",
"workspaceRoots": ["/path/to/workspace"],
"userId": "test-user",
"preToolUse": {
"toolName": "write_to_file",
"parameters": {
"path": "test.js",
"content": "console.log(\"test\");"
}
}
}' | .clinerules/hooks/PreToolUse
```
These examples provide a solid foundation for implementing hooks in your development workflow. Customize them based on your specific needs, tools, and integrations.
@@ -1,283 +0,0 @@
---
title: "Explain Changes Command"
sidebarTitle: "/explain-changes"
---
`/explain-changes` is a slash command that generates AI-powered explanations for any git diff. Unlike the [Explain Changes button](/features/explain-changes) which explains changes from a completed task, this command lets you explain changes between any two git references - commits, branches, tags, PRs, staged changes, or your working directory.
<video
src="https://storage.googleapis.com/cline_public_images/slash-code-explain.mp4"
autoPlay
loop
muted
playsInline
/>
## Requirements
<Note>
The `/explain-changes` command requires a **git repository**. Make sure you're working in a directory that has been initialized with git.
</Note>
For PR explanations, you'll need the [GitHub CLI (gh)](https://cli.github.com/) installed and authenticated. For GitLab merge request explanations, you'll need the [GitLab CLI (glab)](https://gitlab.com/gitlab-org/cli) installed and authenticated.
Unlike the Explain Changes button, this command does **not** require checkpoints to be enabled since it uses git references directly.
## Using the Command
Type `/explain-changes` in the chat input. Cline will:
1. Analyze your git history to understand what changes exist
2. Gather context by reading relevant files
3. Determine appropriate git references to compare
4. Generate a diff view with streaming inline explanations
## How It Works
When you use `/explain-changes`, Cline:
1. **Gathers context**: Runs git commands to understand your repository state
2. **Identifies changes**: Determines which files changed between references
3. **Reads relevant files**: Builds context for better explanations
4. **Calls generate_explanation**: Creates the diff view and streams explanations
5. **Displays results**: Opens a multi-file diff with inline comments
## Use Cases
### Explain the Last Commit
The most common use case - understand what changed in the most recent commit:
```
/explain-changes
```
Cline will examine HEAD and compare it to HEAD~1, explaining all the changes in that commit.
**When to use:**
- After pulling changes from a teammate
- Reviewing your own work before pushing
- Understanding what a merge commit brought in
### Explain Uncommitted Changes
Understand your work-in-progress changes before committing:
```
/explain-changes for my uncommitted work
```
Cline compares HEAD to your working directory, explaining all modified files.
**When to use:**
- Before staging changes to ensure they're complete
- After a long coding session to remember what you changed
- To verify changes before creating a commit
### Explain Staged Changes
Review exactly what you're about to commit:
```
/explain-changes for my staged changes
```
Cline examines only the changes you've staged with `git add`.
**When to use:**
- Final review before committing
- When you've staged a subset of changes and want to verify
- To ensure you haven't accidentally staged unintended files
### Explain a Specific Commit
Understand any commit in your history:
```
/explain-changes for commit abc123
```
Or by commit message:
```
/explain-changes for the commit that added authentication
```
Cline will find the commit and explain what it changed.
**When to use:**
- Investigating when a bug was introduced
- Understanding historical decisions
- Learning how a feature was implemented
### Explain a Range of Commits
Understand multiple commits at once:
```
/explain-changes for the last 3 commits
```
Or a specific range:
```
/explain-changes from v1.0.0 to v1.1.0
```
Cline compares the endpoints and explains all changes between them.
**When to use:**
- Understanding what changed in a release
- Reviewing a series of related commits
- Catching up after being away from the project
### Explain a Pull Request
Get AI explanations for any PR:
```
/explain-changes for PR #42
```
Cline uses the GitHub CLI to fetch PR details and explain the changes.
**When to use:**
- Reviewing someone else's PR
- Understanding a PR before approving
- Learning from PRs in open source projects
- Preparing to give PR feedback
### Explain Branch Differences
Compare any two branches:
```
/explain-changes between main and feature-branch
```
Or see what's changed on a feature branch:
```
/explain-changes for everything on my-feature that's not in main
```
**When to use:**
- Before merging a feature branch
- Understanding divergence between branches
- Planning a merge or rebase strategy
- Reviewing what a colleague has been working on
### Explain Changes to Specific Files
Focus on particular files or directories:
```
/explain-changes for src/auth in the last 5 commits
```
Cline filters the diff to show only relevant changes.
**When to use:**
- Understanding changes to a specific module
- Tracking modifications to critical files
- Learning how a particular feature evolved
### Explain Changes Since a Tag
Understand what's changed since a release:
```
/explain-changes since v2.0.0
```
Cline compares the tag to HEAD and explains all subsequent changes.
**When to use:**
- Preparing release notes
- Understanding what's new since a deployment
- Identifying changes for a changelog
### Explain a Merge Commit
Understand what a merge brought in:
```
/explain-changes for the merge from feature-x
```
Cline explains all the changes that were merged.
**When to use:**
- After merging a large feature branch
- Understanding what a merge conflict resolution changed
- Reviewing what others merged into main
### Explain Stashed Changes
Review what's in your stash:
```
/explain-changes for my stashed changes
```
Cline examines stash@{0} and explains its contents.
**When to use:**
- Before applying a stash
- Deciding whether to keep or drop a stash
- Remembering what you stashed days ago
## Interactive Comments
Just like the [Explain Changes](/features/explain-changes) button, the generated comments are fully interactive:
### Reply to Comments
Ask follow-up questions directly in any comment thread:
- "Why was this function refactored?"
- "What's the purpose of this new parameter?"
- "Could this cause any breaking changes?"
- "Is this change backwards compatible?"
The AI responds with context-aware explanations, understanding both the code and the broader changes.
### Move to Main Chat
Click the title area of any comment thread to move that conversation into Cline's main chat. This is useful when:
- You want Cline to make additional changes
- The discussion reveals something that needs more investigation
- You want to continue working with full Cline capabilities
- A review comment sparks an idea for improvements
### The generate_explanation Tool
Under the hood, `/explain-changes` uses the `generate_explanation` tool with these parameters:
| Parameter | Description | Example |
|-----------|-------------|---------|
| `title` | Descriptive title for the diff view | "Changes in commit abc123" |
| `from_ref` | Git reference for the "before" state | `HEAD~1`, `main`, `origin/main` |
| `to_ref` | Git reference for the "after" state (optional) | `HEAD`, `develop` |
If `to_ref` is omitted, the tool compares against the working directory.
## Tips for Better Explanations
1. **Be specific**: Instead of just `/explain-changes`, tell Cline what you want explained. "Explain the authentication changes in PR #42" gives better context than just "explain PR #42".
2. **Ask about intent**: The AI can explain not just what changed but why. Ask follow-up questions like "What problem was this solving?"
3. **Chain with other commands**: Use `/explain-changes` after investigating an issue to understand potential fixes, then continue with Cline to implement improvements.
4. **Use for learning**: When onboarding to a new codebase, use `/explain-changes` on significant PRs or commits to understand how features were built.
## Related Features
- [Explain Changes](/features/explain-changes) - The button-based version for task completions
- [Checkpoints](/features/checkpoints) - Enables the Explain Changes button
- [@git mentions](/features/at-mentions/git-mentions) - Reference git diffs in your prompts
+445
View File
@@ -0,0 +1,445 @@
---
title: "Workflows"
sidebarTitle: "Workflows"
---
Workflows allow you to define a series of steps to guide Cline through a repetitive set of tasks, such as deploying a service or submitting a PR.
To invoke a workflow, type `/[workflow-name.md]` in the chat.
## How to Create and Use Workflows
Workflows live alongside [Cline Rules](/features/cline-rules). Creating one is straightforward:
<Frame>
<img src="https://storage.googleapis.com/cline_public_images/docs/assets/workflows.png" alt="Workflows tab in Cline" />
</Frame>
1. Create a markdown file with clear instructions for the steps Cline should take
2. Save it with a `.md` extension in your workflows directory
3. To trigger a workflow, just type `/` followed by the workflow filename
4. Provide any required parameters when prompted
The real power comes from how you structure your workflow files. You can:
- Leverage Cline's [built-in tools](/exploring-clines-tools/cline-tools-guide) like `ask_followup_question`, `read_file`, `search_files`, and `new_task`
- Use command-line tools you already have installed like `gh` or `docker`
- Reference external [MCP tool calls](/mcp/mcp-overview) like Slack or Whatsapp
- Chain multiple actions together in a specific sequence
## Real-world Example
I created a PR Review workflow that's already saving me tons of time.
````md pr-review.md [expandable]
You have access to the `gh` terminal command. I already authenticated it for you. Please review it to use the PR that I asked you to review. You're already in the `cline` repo.
<detailed_sequence_of_steps>
# GitHub PR Review Process - Detailed Sequence of Steps
## 1. Gather PR Information
1. Get the PR title, description, and comments:
```bash
gh pr view <PR-number> --json title,body,comments
```
2. Get the full diff of the PR:
```bash
gh pr diff <PR-number>
```
## 2. Understand the Context
1. Identify which files were modified in the PR:
```bash
gh pr view <PR-number> --json files
```
2. Examine the original files in the main branch to understand the context:
```xml
<read_file>
<path>path/to/file</path>
</read_file>
```
3. For specific sections of a file, you can use search_files:
```xml
<search_files>
<path>path/to/directory</path>
<regex>search term</regex>
<file_pattern>*.ts</file_pattern>
</search_files>
```
## 3. Analyze the Changes
1. For each modified file, understand:
- What was changed
- Why it was changed (based on PR description)
- How it affects the codebase
- Potential side effects
2. Look for:
- Code quality issues
- Potential bugs
- Performance implications
- Security concerns
- Test coverage
## 4. Ask for User Confirmation
1. Before making a decision, ask the user if you should approve the PR, providing your assessment and justification:
```xml
<ask_followup_question>
<question>Based on my review of PR #<PR-number>, I recommend [approving/requesting changes]. Here's my justification:
[Detailed justification with key points about the PR quality, implementation, and any concerns]
Would you like me to proceed with this recommendation?</question>
<options>["Yes, approve the PR", "Yes, request changes", "No, I'd like to discuss further"]</options>
</ask_followup_question>
```
## 5. Ask if User Wants a Comment Drafted
1. After the user decides on approval/rejection, ask if they would like a comment drafted:
```xml
<ask_followup_question>
<question>Would you like me to draft a comment for this PR that you can copy and paste?</question>
<options>["Yes, please draft a comment", "No, I'll handle the comment myself"]</options>
</ask_followup_question>
```
2. If the user wants a comment drafted, provide a well-structured comment they can copy:
```
Thank you for this PR! Here's my assessment:
[Detailed assessment with key points about the PR quality, implementation, and any suggestions]
[Include specific feedback on code quality, functionality, and testing]
```
## 6. Make a Decision
1. Approve the PR if it meets quality standards:
```bash
# For single-line comments:
gh pr review <PR-number> --approve --body "Your approval message"
# For multi-line comments with proper whitespace formatting:
cat << EOF | gh pr review <PR-number> --approve --body-file -
Thanks @username for this PR! The implementation looks good.
I particularly like how you've handled X and Y.
Great work!
EOF
```
2. Request changes if improvements are needed:
```bash
# For single-line comments:
gh pr review <PR-number> --request-changes --body "Your feedback message"
# For multi-line comments with proper whitespace formatting:
cat << EOF | gh pr review <PR-number> --request-changes --body-file -
Thanks @username for this PR!
The implementation looks promising, but there are a few things to address:
1. Issue one
2. Issue two
Please make these changes and we can merge this.
EOF
```
Note: The `cat << EOF | ... --body-file -` approach preserves all whitespace and formatting without requiring temporary files. The `-` parameter tells the command to read from standard input.
</detailed_sequence_of_steps>
<example_review_process>
# Example PR Review Process
Let's walk through a real example of reviewing PR #3627 which fixes the thinking mode calculation for Claude 3.7 models.
## Step 1: Gather PR Information
```bash
# Get PR details
gh pr view 3627 --json title,body,comments
# Get the full diff
gh pr diff 3627
```
## Step 2: Understand the Context
```xml
# Examine the original files to understand what's being changed
<read_file>
<path>src/shared/api.ts</path>
</read_file>
# Look at the ThinkingBudgetSlider component implementation
<read_file>
<path>webview-ui/src/components/settings/ThinkingBudgetSlider.tsx</path>
</read_file>
# Check how thinking mode is currently handled in API providers
<search_files>
<path>src/api/providers</path>
<regex>reasoningOn</regex>
<file_pattern>*.ts</file_pattern>
</search_files>
```
## Step 3: Analyze the Changes
From the PR information and code examination, we can see that:
1. The PR fixes an issue with the thinking mode budget calculation for Claude 3.7 models
2. Currently, the thinking budget is incorrectly calculated as 80% of maxTokens (8192), resulting in 6553 tokens
3. Claude 3.7 actually supports a much larger thinking budget (up to 64000 tokens)
4. The PR adds a `thinkingConfig` property with `maxBudget: 64000` to model definitions
5. It updates API handlers to use this value when reasoning mode is enabled
6. It enhances the slider component to calculate max values based on model-specific percentages
7. Comprehensive tests were added to verify the calculations
## Step 4: Ask for User Confirmation
```xml
<ask_followup_question>
<question>Based on my review of PR #3627, I recommend approving this PR. Here's my justification:
1. The PR correctly fixes the thinking mode budget calculation for Claude 3.7 models
2. It properly implements the thinkingConfig.maxBudget property (64000) for Claude 3.7 models
3. It applies the appropriate percentage (50%) for the slider calculation
4. The changes are well-tested with comprehensive unit tests
5. The implementation is clean and follows the project's coding standards
6. The PR includes helpful screenshots demonstrating the fix
Would you like me to proceed with approving this PR?</question>
<options>["Yes, approve the PR", "No, I'd like to discuss further", "Let me review it myself first"]</options>
</ask_followup_question>
```
## Step 5: Ask if User Wants a Comment Drafted
```xml
<ask_followup_question>
<question>Would you like me to draft a comment for this PR that you can copy and paste?</question>
<options>["Yes, please draft a comment", "No, I'll handle the comment myself"]</options>
</ask_followup_question>
```
## Step 6: Make a Decision
```bash
# Option 1: Simple one-line comment
gh pr review 3627 --approve --body "This PR looks good! It correctly fixes the thinking mode budget calculation for Claude 3.7 models."
# Option 2: Multi-line comment with proper whitespace formatting
cat << EOF | gh pr review 3627 --approve --body-file -
This PR looks good! It correctly fixes the thinking mode budget calculation for Claude 3.7 models.
I particularly like:
1. The proper implementation of thinkingConfig.maxBudget property (64000)
2. The appropriate percentage (50%) for the slider calculation
3. The comprehensive unit tests
4. The clean implementation that follows project coding standards
Great work!
EOF
```
</example_review_process>
<common_gh_commands>
# Common GitHub CLI Commands for PR Review
## Basic PR Commands
```bash
# List open PRs
gh pr list
# View a specific PR
gh pr view <PR-number>
# View PR with specific fields
gh pr view <PR-number> --json title,body,comments,files,commits
# Check PR status
gh pr status
```
## Diff and File Commands
```bash
# Get the full diff of a PR
gh pr diff <PR-number>
# List files changed in a PR
gh pr view <PR-number> --json files
# Check out a PR locally
gh pr checkout <PR-number>
```
## Review Commands
```bash
# Approve a PR (single-line comment)
gh pr review <PR-number> --approve --body "Your approval message"
# Approve a PR (multi-line comment with proper whitespace)
cat << EOF | gh pr review <PR-number> --approve --body-file -
Your multi-line
approval message with
proper whitespace formatting
EOF
# Request changes on a PR (single-line comment)
gh pr review <PR-number> --request-changes --body "Your feedback message"
# Request changes on a PR (multi-line comment with proper whitespace)
cat << EOF | gh pr review <PR-number> --request-changes --body-file -
Your multi-line
change request with
proper whitespace formatting
EOF
# Add a comment review (without approval/rejection)
gh pr review <PR-number> --comment --body "Your comment message"
# Add a comment review with proper whitespace
cat << EOF | gh pr review <PR-number> --comment --body-file -
Your multi-line
comment with
proper whitespace formatting
EOF
```
## Additional Commands
```bash
# View PR checks status
gh pr checks <PR-number>
# View PR commits
gh pr view <PR-number> --json commits
# Merge a PR (if you have permission)
gh pr merge <PR-number> --merge
```
</common_gh_commands>
<general_guidelines_for_commenting>
When reviewing a PR, please talk normally and like a friendly reviwer. You should keep it short, and start out by thanking the author of the pr and @ mentioning them.
Whether or not you approve the PR, you should then give a quick summary of the changes without being too verbose or definitive, staying humble like that this is your understanding of the changes. Kind of how I'm talking to you right now.
If you have any suggestions, or things that need to be changed, request changes instead of approving the PR.
Leaving inline comments in code is good, but only do so if you have something specific to say about the code. And make sure you leave those comments first, and then request changes in the PR with a short comment explaining the overall theme of what you're asking them to change.
</general_guidelines_for_commenting>
<example_comments_that_i_have_written_before>
<brief_approve_comment>
Looks good, though we should make this generic for all providers & models at some point
</brief_approve_comment>
<brief_approve_comment>
Will this work for models that may not match across OR/Gemini? Like the thinking models?
</brief_approve_comment>
<approve_comment>
This looks great! I like how you've handled the global endpoint support - adding it to the ModelInfo interface makes total sense since it's just another capability flag, similar to how we handle other model features.
The filtered model list approach is clean and will be easier to maintain than hardcoding which models work with global endpoints. And bumping the genai library was obviously needed for this to work.
Thanks for adding the docs about the limitations too - good for users to know they can't use context caches with global endpoints but might get fewer 429 errors.
</approve_comment>
<requesst_changes_comment>
This is awesome. Thanks @scottsus.
My main concern though - does this work for all the possible VS Code themes? We struggled with this initially which is why it's not super styled currently. Please test and share screenshots with the different themes to make sure before we can merge
</request_changes_comment>
<request_changes_comment>
Hey, the PR looks good overall but I'm concerned about removing those timeouts. Those were probably there for a reason - VSCode's UI can be finicky with timing.
Could you add back the timeouts after focusing the sidebar? Something like:
```typescript
await vscode.commands.executeCommand("claude-dev.SidebarProvider.focus")
await setTimeoutPromise(100) // Give UI time to update
visibleWebview = WebviewProvider.getSidebarInstance()
```
</request_changes_comment>
<request_changes_comment>
Heya @alejandropta thanks for working on this!
A few notes:
1 - Adding additional info to the environment variables is fairly problematic because env variables get appended to **every single message**. I don't think this is justifiable for a somewhat niche use case.
2 - Adding this option to settings to include that could be an option, but we want our options to be simple and straightforward for new users
3 - We're working on revisualizing the way our settings page is displayed/organized, and this could potentially be reconciled once that is in and our settings page is more clearly delineated.
So until the settings page is update, and this is added to settings in a way that's clean and doesn't confuse new users, I don't think we can merge this. Please bear with us.
</request_changes_comment>
<request_changes_comment>
Also, don't forget to add a changeset since this fixes a user-facing bug.
The architectural change is solid - moving the focus logic to the command handlers makes sense. Just don't want to introduce subtle timing issues by removing those timeouts.
</request_changes_comment>
</example_comments_that_i_have_written_before>
````
When I get a new PR to review, I used to manually gather context: checking the PR description, examining the diff, looking at surrounding files, and finally forming an opinion. Now I just:
1. Type `/pr-review.md` in chat
2. Paste in the PR number
3. Let Cline handle everything else
My workflow uses the `gh` command-line tool and Cline's built in `ask_followup_question` to:
- Pull the PR description and comments
- Examine the diff
- Check surrounding files for context
- Analyze potential issues
- Asks me if it's cool approve it if everything looks good, with justification for why it should be approved
- If I say "yes," Cline automatically approves the PR with the `gh` command
This has taken my PR review process from a manual, multi-step operation to a single command that gives me everything I need to make an informed decision.
> This is just one example of a workflow file. You can find more in our [prompts repository](https://github.com/cline/prompts) for inspiration.
## Building Your Own Workflows
The beauty of workflows is they're completely customizable to your needs. You might create workflows for all kinds of repetitive tasks:
- For releases, you could have a workflow that grabs all merged PRs, builds a changelog, and handles version bumps.
- Setting up new projects is perfect for workflows. Just run one command to create your folder structure, install dependencies, and set up configs.
- Need to create a report? Create a workflow that grabs stats from different sources and formats them exactly how you like. You can even visualize them with a charting library and then make a presentation out of it with a library like [slidev](https://sli.dev/).
- You can even use workflows to draft messages to your team using an MCP server like Slack or Whatsapp after you submit a PR.
With Workflows, your imagination is the limit. The true potential comes from spotting those annoying repetitive tasks you do all the time.
If you can describe something as "first I do X, then Y, then Z" - that's a perfect workflow candidate.
Start with something small that bugs you, turn it into a workflow, and keep refining it. You'll be shocked how much of your day can be automated this way.
@@ -1,135 +0,0 @@
---
title: "Workflows Best Practices"
sidebarTitle: "Best Practices"
description: "Tips and strategies for creating effective and reliable Cline workflows."
---
Creating effective workflows requires a balance of clear instructions, modular design, and intelligent tool usage. Follow these best practices to get the most out of Cline's automation capabilities.
## Use Cline to Build Workflows
We highly recommend using Cline to help you build your workflows. Since Cline understands your project's context and structure, it can be an invaluable partner in designing automation that fits your specific needs.
### Building your own workflows
Creating a workflow is simpler than you might think. There's actually a workflow for building workflows!
First, **save the [create-new-workflow.md](https://github.com/cline/prompts/blob/main/workflows/create-new-workflow.md) file to your workspace** (e.g., in `.clinerules/workflows/`).
Then, type `/create-new-workflow.md` and Cline guides you through it:
1. It asks for the purpose and a concise name.
2. You describe the objective and expected outputs.
3. You list the major steps (Cline can help determine details).
4. It generates the properly structured workflow file.
<Tip>
**Automate Your History:** The best workflows come from tasks you've already done. After completing something you'll need to repeat, tell Cline: "Create a workflow for the process I just completed." It analyzes the conversation, identifies the steps, and generates the workflow file. Your accumulated context becomes reusable automation.
</Tip>
Workflows live in `.clinerules/workflows/` for project-specific ones or `~/Documents/Cline/Workflows/` for global ones you use across projects. Project workflows take precedence when names match.
## Workflow Design
<Tip>
**Start Simple:** Begin with small, single-task workflows. As you get comfortable, you can combine them or create more complex sequences.
</Tip>
### Be Modular
Instead of creating one massive workflow file, break complex tasks into smaller, reusable workflows. This makes them easier to maintain and debug.
### Use Clear Comments
Just like with code, commenting your workflow steps is crucial. Explain *why* a step is happening, not just *what* is happening. This helps both you (the future maintainer) and Cline understand the intent.
### Version Control
Treat your workflows as part of your codebase. Store them in your Git repository (in `.clinerules/workflows/`) so they are versioned, reviewed, and shared with your team.
## Prompt Engineering for Cline
### Be Specific with Tool Use
Don't just say "find the file." Be explicit about which tool Cline should use.
* **Bad:** "Find the user controller."
* **Good:** "Use `search_files` to look for `UserController` in the `src/controllers` directory."
## Advanced Techniques
### Available Tools
Cline has a powerful set of tools you can use within your workflows. Here are the most common ones:
#### execute_command
Executes a CLI command on your system. Use this for running tests, builds, git commands, or any other terminal operation.
```xml
<execute_command>
<command>npm run test</command>
<requires_approval>false</requires_approval>
</execute_command>
```
#### read_file
Reads the contents of a file. Essential for analyzing code or configuration.
```xml
<read_file>
<path>src/config.json</path>
</read_file>
```
#### write_to_file
Creates or overwrites a file. Use this to generate boilerplate, config files, or documentation.
```xml
<write_to_file>
<path>src/components/Button.tsx</path>
<content>
// File content goes here...
</content>
</write_to_file>
```
#### search_files
Searches for a regex pattern across files in a directory. Great for finding TODOs, usage examples, or specific code patterns.
```xml
<search_files>
<path>src</path>
<regex>TODO</regex>
<file_pattern>*.ts</file_pattern>
</search_files>
```
#### ask_followup_question
Asks the user for input or confirmation. This makes your workflow interactive and allows for human-in-the-loop decision making.
```xml
<ask_followup_question>
<question>Do you want to deploy to production?</question>
<options>["Yes", "No"]</options>
</ask_followup_question>
```
#### browser_action
Controls a built-in browser to interact with websites or local servers. Useful for testing web UIs or scraping data.
```xml
<browser_action>
<action>launch</action>
<url>http://localhost:3000</url>
</browser_action>
```
### Leverage MCP Tools
You can use Model Context Protocol (MCP) tools within your workflows to interact with external services like GitHub, Slack, or databases. This allows you to create powerful end-to-end automations.
### Manage Context Window
Be mindful of Cline's context window. If a workflow is too long or processes too much data, it might exceed the token limit.
* **Break it down:** Split long workflows into smaller parts.
* **Be concise:** Keep instructions clear and to the point.
## Learn More
<Card title="Cline Learn" icon="lightbulb" href="https://cline.bot/learn">
Dive deeper into general prompt engineering strategies to write even better instructions for Cline.
</Card>
@@ -1,139 +0,0 @@
---
title: "Workflows Overview"
sidebarTitle: "Overview"
description: "Learn what Cline workflows are, why they are useful, and how to structure them."
---
Workflows in Cline are Markdown files that define a series of steps to guide Cline through repetitive or complex tasks. They are a powerful way to automate your development processes directly within your editor.
To invoke a workflow, you simply type `/` followed by the workflow's filename in the chat (e.g., `/deploy.md`).
## Why Use Cline Workflows?
* **Automation:** Automate repetitive tasks like setting up a new project, deploying a service, or running a specific test suite.
* **Consistency:** Ensure that tasks are performed the same way every time, reducing errors.
* **Reduced Cognitive Load:** Don't waste mental energy remembering complex sequences of commands or steps.
* **Contextual:** Workflows run within your project's context, so Cline has access to your files and can use its tools to interact with them.
## How They Work
A workflow file is a standard Markdown file with a `.md` extension. Cline reads this file and interprets the instructions step-by-step. The real power comes from Cline's ability to use its built-in tools and other capabilities within these instructions:
* **Cline Tools:** Use tools like `read_file`, `write_to_file`, `execute_command`, and `ask_followup_question`.
* **Command-Line Tools:** Instruct Cline to use any CLI tool installed on your machine (e.g., `git`, `gh`, `npm`, `docker`).
* **MCP Tools:** Reference tools from connected Model Context Protocol (MCP) servers.
## Workflows vs. Rules
It's important to understand the difference between Cline Workflows and Cline Rules, as they serve different purposes:
| Feature | Purpose | When to Use |
| :--- | :--- | :--- |
| **Cline Rules** | Define *how* Cline should behave generally. They are always active (or contextually triggered) and set the "ground rules" for your project. | Enforcing coding standards, tech stack preferences, or project-specific constraints (e.g., "Always use TypeScript", "Never edit the `db` folder"). |
| **Cline Workflows** | Define *what* specific task Cline should perform. They are sequences of steps invoked on-demand to automate a process. | Automating repetitive tasks like creating a component, running a release process, or generating a daily report. |
Think of **Rules** as the *environment* Cline works in, and **Workflows** as the *scripts* you give Cline to execute.
### Example: Automating a Release
Imagine you need to prepare a new release for your library.
**Without a workflow**, you might have to manually:
1. Open `package.json` and bump the version number.
2. Run your test suite to make sure everything is green.
3. Update `CHANGELOG.md` with the latest commits.
4. Run `git commit -am "v1.0.1"`.
5. Run `git tag v1.0.1`.
6. Run `git push origin main --tags`.
This is tedious and easy to mess up. You might forget to run the tests or format the changelog correctly.
**With a Cline workflow**, you define these steps once in a `release.md` file. Then, you just type:
```bash
/release.md
```
Cline will then meticulously follow your instructions: updating files, running tests, and executing git commands—pausing only if it encounters an error or needs your input.
## Where are Workflows Stored?
You can store workflows in two locations, depending on whether they are specific to a project or meant to be global.
<Tabs>
<Tab title="Project-Specific Workflows">
Store workflows that are specific to a single project in a `.clinerules/workflows/` directory in your project's root.
1. Create a `.clinerules` folder in your project's root directory (if it doesn't already exist).
<Note>
The `.clinerules` directory may be hidden by default on some systems. You might need to enable **Show Hidden Files** to see it.
</Note>
2. Inside `.clinerules`, create a `workflows` folder.
3. Create your Markdown workflow files (e.g., `deploy.md`) in this folder.
These workflows will only be available when you have this specific project open.
</Tab>
<Tab title="Global Workflows">
Store workflows that you want to use across all your projects in a global directory.
* **macOS/Linux:** `~/Documents/Cline/Workflows/`
* **Windows:** `C:\Users\USERNAME\Documents\Cline\Workflows\`
Create your Markdown workflow files directly in this directory. They will be available in any project you open with Cline.
</Tab>
</Tabs>
## Manage Workflows
You can easily manage your workflows directly within the extension. This feature provides a unified interface to handle all your automation needs without leaving your editor or hunting through file directories. It consolidates both project-specific rules and global workflows into one view, giving you full control over your automation environment.
1. Click the **Manage Cline Rules and Workflows** button (<Icon icon="scale-balanced" />) at the bottom of the extension.
2. This opens an interface where you can:
* **View all available workflows:** See a comprehensive list of both project-specific and global workflows.
* **Control automation:** Toggle individual workflows on and off as needed for your current task.
* **Create and Edit:** Add new workflows or modify existing ones directly within the interface.
* **Clean up:** Delete workflows you no longer need.
<Frame caption="Manage Workflows">
<img src="https://storage.googleapis.com/cline_public_images/workflow-menu.gif" alt="Manage Cline Rules and Workflows Interface" />
</Frame>
## Workflow Structure Example
Here is a simple example of a workflow file (`daily-changelog.md`) that helps you create a daily changelog.
````markdown daily-changelog.md
# Daily Changelog Generator
This workflow helps you create a changelog for your daily work.
1. **Check your recent git commits:**
I will run the following command to see your commits from today.
```bash
git log --author="$(git config user.name)" --since="yesterday" --oneline
```
2. **Summarize your work:**
I will present the commits to you and ask for a summary of your changes to be added to the `changelog.md` file.
3. **Create/Append to daily changelog:**
I will append to the `changelog.md` file. The content will include a header with the current date, the list of commits, and your summary.
````
### Breakdown of the Workflow
This workflow demonstrates that you don't always need to provide specific tool calls (like XML blocks). Cline is smart enough to interpret your high-level instructions.
1. **Step 1: Check recent git commits**
* We give Cline a specific command to run. This ensures it gets exactly the data we want (today's commits).
<Tip>
After Cline shows the git commit history, you may need to click the **Proceed While Running** button to allow the workflow to continue.
</Tip>
2. **Step 2: Summarize your work**
* Instead of forcing a specific tool, we simply tell Cline what to do: "ask for a summary".
* Cline knows it needs to use its capabilities to ask you a question.
3. **Step 3: Create/Append to daily changelog**
* We describe the desired outcome: "append to the `changelog.md` file" with specific content.
* Cline figures out how to format the file and use its file-writing tools to accomplish the task.
@@ -1,112 +0,0 @@
---
title: "Workflows Quick Start"
sidebarTitle: "Quick Start"
description: "A step-by-step guide to creating your first Cline workflow."
---
In this tutorial, you will create a powerful workflow that automates the process of reviewing a GitHub Pull Request. This example demonstrates how to combine CLI tools, file analysis, and user interaction into a seamless process.
### Prerequisites
* You have Cline installed.
* You have the [GitHub CLI (`gh`)](https://cli.github.com/) installed and authenticated.
* You have a Git repository open with a Pull Request you want to test this on.
## Creating a Pull Request Review Workflow
This workflow will automate the process of fetching PR details, analyzing the code changes for issues, and drafting a review comment.
<Steps>
<Step title="Create the Workflow File">
First, create the directory structure for your project-specific workflows.
1. In the root of your project, create a new folder named `.clinerules`.
2. Inside `.clinerules`, create another folder named `workflows`.
3. Finally, create a new file named `pr-review.md` inside the `workflows` folder.
</Step>
<Step title="Write the Workflow Content">
Open the `pr-review.md` file and add the following content. This workflow will gather PR details, analyze the changes, and help you submit a review.
````markdown pr-review.md
# Pull Request Reviewer
This workflow helps me review a pull request by analyzing the changes and drafting a review.
## 1. Gather PR Information
First, I need to understand what this PR is about. I'll fetch the title, description, and list of changed files.
```bash
gh pr view PR_NUMBER --json title,body,files
```
## 2. Examine Modified Files
Now I will examine the diff to understand the specific code changes.
```bash
gh pr diff PR_NUMBER
```
## 3. Analyze Changes
I will analyze the code changes for:
* **Bugs:** Logic errors or edge cases.
* **Performance:** Inefficient loops or operations.
* **Security:** Vulnerabilities or unsafe practices.
## 4. Confirm Assessment
Based on my analysis, I will present my findings and ask how you want to proceed.
```xml
<ask_followup_question>
<question>I've reviewed PR #PR_NUMBER. Here is my assessment:
[Insert Analysis Here]
Do you want me to approve this PR, request changes, or just leave a comment?</question>
<options>["Approve", "Request Changes", "Comment", "Do nothing"]</options>
</ask_followup_question>
```
## 5. Execute Review
Finally, I will execute the review command based on your decision.
```bash
# If approving:
gh pr review PR_NUMBER --approve --body "Looks good to me! [Summary of analysis]"
# If requesting changes:
gh pr review PR_NUMBER --request-changes --body "Please address the following: [Issues list]"
# If commenting:
gh pr review PR_NUMBER --comment --body "[Comments]"
```
````
<Note>
When you run this workflow, you will replace `PR_NUMBER` with the actual number of the pull request you want to review (e.g., `/pr-review.md 123`).
</Note>
</Step>
<Step title="Run the Workflow">
Now you're ready to run your new workflow.
1. Open the Cline chat panel.
2. Type `/pr-review.md` followed by the PR number (e.g., `/pr-review.md 42`) and press Enter.
3. Cline will fetch the PR details, analyze the code, and present you with its findings before submitting the review.
<Tip>
As Cline executes commands (like `gh pr view`), it may show you the output and pause. You will need to click the **Proceed While Running** button to allow Cline to analyze the content and continue with the workflow.
</Tip>
</Step>
</Steps>
### Other Common Use Cases
This is just one example. You can create workflows for a wide variety of tasks, such as:
* **Creating Components:** Automate the boilerplate for new files (like React components or API endpoints).
* **Running Tests:** Create a workflow that runs your test suite and summarizes the results.
* **Deploying Your Application:** Automate your deployment pipeline using tools like `docker` and `kubectl`.
* **Refactoring Code:** Guide Cline through a complex refactoring process step-by-step.
Explore Cline's capabilities and your own development processes to find repetitive tasks that can be turned into efficient workflows.
+6 -5
View File
@@ -361,9 +361,10 @@ description: "Get Cline up and running in your favorite IDE with these simple in
<Info>
You'll be redirected to [app.cline.bot](https://app.cline.bot) to authenticate. After signing in, you'll automatically return to your editor.
</Info>
<Info>
You'll be redirected to the Cline authentication page to sign in with your account.
</Info>
<Frame>
<img src="/assets/installation/login.png" alt="Cline sign up screen"
/>
</Frame>
</Step>
<Step title="You're All Set!">
@@ -402,7 +403,7 @@ description: "Get Cline up and running in your favorite IDE with these simple in
Connect with our team and community for support, tips, and discussions.
</Card>
<Card title="Read the Docs" icon="book-open" href="/getting-started/selecting-your-model">
Explore model selection guides and advanced features to get the most out of Cline.
<Card title="Read the Docs" icon="book-open" href="/getting-started/for-new-coders">
Explore guides for new coders, model selection, and advanced features.
</Card>
</CardGroup>
+37 -9
View File
@@ -5017,9 +5017,9 @@
}
},
"node_modules/glob": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
@@ -5146,6 +5146,28 @@
"node": ">=6.0"
}
},
"node_modules/gray-matter/node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"license": "MIT",
"dependencies": {
"sprintf-js": "~1.0.2"
}
},
"node_modules/gray-matter/node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/has-bigints": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
@@ -6468,9 +6490,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -10213,6 +10235,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"license": "BSD-3-Clause"
},
"node_modules/stack-utils": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -10581,9 +10609,9 @@
}
},
"node_modules/tar-fs": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
"integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz",
"integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==",
"license": "MIT",
"dependencies": {
"pump": "^3.0.0",
-4
View File
@@ -14,9 +14,5 @@
"description": "",
"dependencies": {
"mintlify": "^4.2.23"
},
"overrides": {
"tar-fs": "^3.1.1",
"js-yaml": "^4.1.1"
}
}
-1
View File
@@ -17,7 +17,6 @@ description: "Learn how to configure and use Anthropic Claude models with Cline.
Cline supports the following Anthropic Claude models:
- `claude-haiku-4-5-20251001`
- `claude-opus-4-5-20251101`
- `claude-opus-4-1-20250805`
- `claude-opus-4-20250514`
- `anthropic/claude-sonnet-4.5` (Recommended)
+43 -14
View File
@@ -3,7 +3,7 @@ title: "Baseten"
description: "Learn how to configure and use Baseten's Model APIs with Cline. Access frontier open-source models with enterprise-grade performance, reliability, and competitive pricing."
---
Baseten provides on-demand frontier model APIs designed for production applications, not just experimentation. Built on the Baseten Inference Stack, these APIs deliver optimized inference for leading open-source models from OpenAI, DeepSeek, Moonshot AI, and Alibaba Cloud.
Baseten provides on-demand frontier model APIs designed for production applications, not just experimentation. Built on the Baseten Inference Stack, these APIs deliver enterprise-grade performance and reliability with optimized inference for leading open-source models from OpenAI, DeepSeek, Meta, Moonshot AI, and Alibaba Cloud.
**Website:** [https://www.baseten.co/products/model-apis/](https://www.baseten.co/products/model-apis/)
@@ -14,21 +14,13 @@ Baseten provides on-demand frontier model APIs designed for production applicati
3. **Create a Key:** Generate a new API key. Give it a descriptive name (e.g., "Cline").
4. **Copy the Key:** Copy the API key immediately and store it securely.
### Configuration in Cline
1. **Open Cline Settings:** Click the settings icon (⚙️) in the Cline panel.
2. **Select Provider:** Choose "Baseten" from the "API Provider" dropdown.
3. **Enter API Key:** Paste your Baseten API key into the "Baseten API Key" field.
4. **Select Model:** Choose your desired model from the "Model" dropdown.
**IMPORTANT: For Kimi K2 Thinking:** To use the `moonshotai/Kimi-K2-Thinking` model, you must enable **Native Tool Call (Experimental)** in Cline settings. This setting allows Cline to call tools through their native tool processor and is required for this reasoning model to function properly.
### Supported Models
Cline supports all current models under Baseten Model APIs, including:
For the most updated pricing, please visit: https://www.baseten.co/products/model-apis/
Note: Kimi K2 0711, Llama 4 Maverick, and Llama 4 Scout Model APIs have been deprecated at 5pm PT on October 8th.
https://www.baseten.co/resources/changelog/model-api-deprecation-notice-kimi-k2-0711-scout-maverick/
- `moonshotai/Kimi-K2-Thinking` (Moonshot AI) - Enhanced reasoning capabilities with step-by-step thought processes (262K context) - \$0.60/\$2.50 per 1M tokens
- `zai-org/GLM-4.6` (Z AI) - Frontier open model with advanced agentic, reasoning and coding capabilities by Z AI (200k context) \$0.60/\$2.20 per 1M tokens
- `moonshotai/Kimi-K2-Instruct-0905` (Moonshot AI) - September update with enhanced capabilities (262K context) - \$0.60/\$2.50 per 1M tokens
- `openai/gpt-oss-120b` (OpenAI) - 120B MoE with strong reasoning capabilities (128K context) - \$0.10/\$0.50 per 1M tokens
@@ -39,6 +31,13 @@ For the most updated pricing, please visit: https://www.baseten.co/products/mode
- `deepseek-ai/DeepSeek-V3.1` - Hybrid reasoning with advanced tool calling (163K context) - \$0.50/\$1.50 per 1M tokens
- `deepseek-ai/DeepSeek-V3-0324` - Fast general-purpose with enhanced reasoning (163K context) - \$0.77/\$0.77 per 1M tokens
### Configuration in Cline
1. **Open Cline Settings:** Click the settings icon (⚙️) in the Cline panel.
2. **Select Provider:** Choose "Baseten" from the "API Provider" dropdown.
3. **Enter API Key:** Paste your Baseten API key into the "Baseten API Key" field.
4. **Select Model:** Choose your desired model from the "Model" dropdown.
### Production-First Architecture
Baseten's Model APIs are built for production environments with several key advantages:
@@ -60,17 +59,47 @@ Baseten's Model APIs are built for production environments with several key adva
#### Developer Experience
- **OpenAI compatible API** - migrate by swapping a single URL
- **Drop-in replacement** for closed models with comprehensive observability and analytics
- **Drop-in replacement** for closed models with comprehensive observability
- **Seamless scaling** from Model APIs to dedicated deployments
### Special Features
#### Function Calling & Tool Use
All Baseten models support structured outputs, function calling, and tool use as part of the Baseten Inference Stack, making them ideal for agentic applications and coding workflows.
All Baseten models support structured outputs, function calling, and tool use as part of the Baseten Inference Stack, making them ideal for agentic applications.
#### Reasoning Capabilities
DeepSeek models offer enhanced reasoning with step-by-step thought processes, while maintaining production-ready performance.
#### Long Context Support
- **Up to 1 million tokens** for Llama 4 models (Maverick and Scout)
- **262K tokens** for Qwen3 models
- **163K tokens** for DeepSeek models
- **Perfect for code repositories** and complex multi-turn conversations
#### Quantization Optimizations
Models are deployed with advanced quantization techniques (fp4, fp8, fp16) for optimal performance while maintaining quality.
### Migration from Other Providers
Baseten's OpenAI compatibility makes migration straightforward:
**From OpenAI:**
- Swap `api.openai.com` with `inference.baseten.co/v1`
- Keep existing request/response formats
- Benefit from significant cost savings
**From Other Providers:**
- Use standard OpenAI SDK format
- Maintain existing prompting strategies
- Access to newer open-source models
### Tips and Notes
- **Dynamic Model Updates:** Cline automatically fetches the latest model list from Baseten, ensuring access to new models as they're released in real time.
- **Model Selection:** Choose models based on your specific use case - reasoning models for complex tasks, coding models for development work, and flagship models for general applications.
- **Cost Optimization:** Baseten offers some of the most competitive pricing in the market, especially for open-source models.
- **Context Windows:** Take advantage of large context windows (up to 1M tokens) for including substantial codebases and documentation.
- **Enterprise Ready:** Baseten is designed for production use with enterprise-grade security, compliance, and reliability.
- **Dynamic Model Updates:** Cline automatically fetches the latest model list from Baseten, ensuring access to new models as they're released.
- **Multi-Cloud Capacity Management (MCM):** Baseten's multi-cloud infrastructure ensures high availability and low latency globally.
- **Support:** Baseten provides dedicated support for production deployments and can work with you on dedicated resources as you scale.
+1 -1
View File
@@ -14,7 +14,7 @@ SAP AI Core, and Generative AI Hub, are offerings from SAP BTP. You need an acti
### Getting a Service Binding
1. **Access:** Go to your subaccount via [BTP Cloud Cockpit](https://cockpit.btp.cloud.sap/cockpit)
1. **Access:** Go to your subaccount via [BTP Cloud Cockpit](cockpit.btp.cloud.sap/cockpit)
2. **Create a Service Binding:** Go to "Instances and Subscriptions", select your SAP AI Core service instance and click on Service Bindings > Create.
3. **Copy the Service Binding:** Copy the service binding values.
@@ -1,229 +0,0 @@
---
title: "Networking and Proxies"
sidebarTitle: "Networking & Proxies"
description: "Configure Cline to work behind firewalls and proxies"
---
If you're working behind a corporate proxy or firewall, you'll need to configure
proxy settings for Cline to connect to AI providers. The configuration varies
depending on which version of Cline you're using.
## VSCode Extension
The VSCode extension automatically uses VSCode's built-in proxy settings. See
[Network Connections in Visual Studio Code, Proxy server support](https://code.visualstudio.com/docs/setup/network#_proxy-server-support)
for instructions on how to set up proxies in VSCode. No additional configuration
is needed for Cline itself.
## CLI
The Cline CLI uses standard HTTP proxy environment variables. Configure these before running `cline` commands.
### Basic Configuration
**Windows (Command Prompt)**
```cmd
set https_proxy=http://proxy.company.com:8080
set http_proxy=http://proxy.company.com:8080
cline start
```
**Windows (PowerShell)**
```powershell
$env:https_proxy="http://proxy.company.com:8080"
$env:http_proxy="http://proxy.company.com:8080"
cline start
```
**macOS/Linux**
```bash
export https_proxy=http://proxy.company.com:8080
export http_proxy=http://proxy.company.com:8080
cline start
```
### Proxy with Authentication
If your proxy requires authentication, include credentials in the URL:
```bash
export https_proxy=http://username:password@proxy.company.com:8080
export http_proxy=http://username:password@proxy.company.com:8080
```
<Warning>
Storing credentials in environment variables can be a security risk.
</Warning>
### Bypass Proxy for Localhost
To prevent localhost traffic from going through the proxy, set the `no_proxy` environment variable:
**Windows**
```cmd
set no_proxy=localhost,127.0.0.1,.local
```
**macOS/Linux**
```bash
export no_proxy=localhost,127.0.0.1,.local
```
### Custom Certificate Authority
If your proxy uses a custom CA certificate:
**Windows**
```cmd
set NODE_EXTRA_CA_CERTS=C:\path\to\ca-certificate.crt
cline start
```
**macOS/Linux**
```bash
export NODE_EXTRA_CA_CERTS=/path/to/ca-certificate.pem
cline start
```
### Permanent Configuration
To avoid setting these variables every time, add them to your shell profile or system environment variables.
**macOS/Linux** (add to `~/.bashrc`, `~/.zshrc`, or `~/.profile`):
```bash
# Proxy configuration
export https_proxy=http://proxy.company.com:8080
export http_proxy=http://proxy.company.com:8080
export no_proxy=localhost,127.0.0.1,.local
export NODE_EXTRA_CA_CERTS=/path/to/ca-certificate.pem
```
**Windows** (System Environment Variables):
1. Search for "Environment Variables" in Windows Settings
2. Add the variables under "User variables" or "System variables"
3. Restart your terminal or IDE
### Known Limitations
Cline CLI only supports HTTP proxies. It does not support SOCKS proxies,
proxy autoconfiguration (PAC) scripts, or HTTP proxies which require
authentication beyond a basic username and password.
## JetBrains IDEs
The JetBrains plugin uses the IDE's HTTP proxy settings.
### Configure JetBrains Proxy
1. Open Settings/Preferences:
- **Windows/Linux**: File > Settings
- **macOS**: IntelliJ IDEA > Preferences
- Or press `Ctrl+Alt+S` (Windows/Linux) or `Cmd+,` (macOS)
2. Navigate to:
```
Appearance & Behavior > System Settings > HTTP Proxy
```
3. Select "Manual proxy configuration"
4. Configure your proxy:
- **Host name**: `proxy.company.com`
- **Port number**: `8080`
- **No proxy for**: `localhost,127.0.0.1`
- Check "Proxy authentication" if required
- Enter your username and password
5. Click "Check connection" to verify the settings
6. Click "OK" to apply
7. Restart the IDE
### Test Connection
After configuring the proxy, test that Cline can connect to your AI provider:
1. Open the Cline panel
2. Try sending a simple message
3. If connection fails, check the IDE's Event Log for error messages
### Custom Certificate Authority
If your proxy uses a custom CA:
1. Add the certificate to your system's trust store, or
2. Import it into the JetBrains IDE:
- Settings > Tools > Server Certificates
- Click "+" to add your certificate
### Known Limitations
Cline in JetBrains only supports HTTP proxies. It does not support SOCKS
proxies, proxy autoconfiguration (PAC) scripts, or HTTP proxies which require
authentication beyond a basic username and password.
Cline does not pick up changed proxy settings dynamically. After changing proxy
settings, restart the IDE for Cline to use the new settings.
## Troubleshooting
### Connection Timeouts
If you're experiencing connection timeouts:
1. Verify your proxy address and port are correct
2. Check if the proxy requires authentication
3. Ensure the AI provider's API endpoints aren't blocked by your firewall
### SSL/TLS Certificate Errors
If you see certificate-related errors:
1. Check that `NODE_EXTRA_CA_CERTS` points to the correct certificate file
2. Ensure the certificate file is in PEM format
3. Use curl to verify the certificate works, for example, `curl -x proxy.corp.example:8080 --cacert /path/to/ca-cert.pem -o - -vv https://api.cline.bot/`
4. Consider disabling `http.proxyStrictSSL` in VSCode (not recommended for production)
### Testing Proxy Configuration
If you encounter problems with Cline networking, first verify your proxy
configuration works using curl:
```bash
# Linux/macOS
export https_proxy=http://proxy.company.com:8080
curl -vv https://api.anthropic.com
# Windows PowerShell
$env:https_proxy="http://proxy.company.com:8080"
curl.exe -vv https://api.anthropic.com
```
Use `--cacert $NODE_EXTRA_CA_CERTS` to specify a certificate if necessary.
Next, check ~/.cline/cline-core-service.log (CLI, JetBrains) for log messages
confirming your proxy configuration and any network-related errors.
## Common Proxy Patterns
### Authenticated HTTPS Proxy
```bash
export https_proxy=http://username:password@proxy.company.com:8080
export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem
```
### Proxy with No Authentication
```bash
export https_proxy=http://proxy.company.com:8080
export http_proxy=http://proxy.company.com:8080
```
### Proxy with Bypass Rules
```bash
export https_proxy=http://proxy.company.com:8080
export no_proxy=localhost,127.0.0.1,.company.local,192.168.0.0/16
```
@@ -1,195 +0,0 @@
---
title: "Task History Recovery Guide"
sidebarTitle: "Task History Recovery"
description: "How to recover and reconstruct your Cline task history"
---
Sometimes when you update Cline or when certain settings change, you might lose access to your previous tasks. This guide will help you recover and reconstruct your Cline task history, so you can regain access to your important conversations and work.
<Tip>
Most cases are solved by running the built-in recovery command.
</Tip>
## Quick Recovery
<Steps>
<Step title="Open Command Palette">
Press `Cmd/Ctrl + Shift + P` to open the Command Palette.
</Step>
<Step title="Run the recovery command">
Type **"Cline: Reconstruct Task History"** and select it.
</Step>
<Step title="Confirm the action">
A confirmation prompt will appear. Click **Yes** to proceed.
</Step>
<Step title="Wait for reconstruction">
Cline will scan your task folders and rebuild the history index.
</Step>
<Step title="Verify recovery">
Check your history panel to confirm your tasks have been restored.
</Step>
</Steps>
## Where Cline Stores Data
### Storage Paths
<Tabs>
<Tab title="VS Code">
```bash
# macOS
~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/
# Windows
%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\
# Linux
~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/
```
</Tab>
<Tab title="JetBrains">
```bash
# macOS
~/Library/Application Support/JetBrains/<IDE>/globalStorage/saoudrizwan.claude-dev/
# Windows
%APPDATA%\JetBrains\<IDE>\globalStorage\saoudrizwan.claude-dev\
# Linux
~/.config/JetBrains/<IDE>/globalStorage/saoudrizwan.claude-dev/
```
</Tab>
</Tabs>
For VS Code Insiders, replace `Code` with `Code - Insiders`. For JetBrains IDEs, replace `<IDE>` with your specific IDE name (e.g., `IntelliJIdea2023.3`, `PyCharm2023.3`, `WebStorm2023.3`).
### Directory Structure
```
saoudrizwan.claude-dev/
├── state/
│ ├── taskHistory.json # Main history index
│ └── taskHistory.backup.*.json # Backups
├── tasks/
│ └── <task-id>/ # Individual task data
│ ├── api_conversation_history.json
│ ├── ui_messages.json
│ └── task_metadata.json
└── checkpoints/
└── <workspace-hash>/ # Per-workspace checkpoint storage
└── .git/ # Shadow Git repository for snapshots
```
The `taskHistory.json` file is just an index. The actual conversation data lives in individual task folders under `tasks/`.
## Using the Recovery Command
The recovery command scans all task folders and rebuilds the history index from scratch.
What it does:
1. Backs up your current `taskHistory.json`
2. Scans the `tasks/` directory
3. Reads conversation data from each task folder
4. Recalculates token usage and costs
5. Creates a new `taskHistory.json`
```mermaid
graph LR
A[Backup current] --> B[Scan tasks/]
B --> C[Read each task]
C --> D[Rebuild index]
D --> E[Done]
```
<Warning>
The recovery command rebuilds the index by reading data from existing task folders. If the `tasks/` directory or individual task folders have been permanently deleted, the associated data cannot be recovered.
</Warning>
## Manual Recovery
### Restoring from Backup
Cline creates backups automatically. Find them in the `state/` folder:
<Tabs>
<Tab title="VS Code">
```bash
# macOS/Linux
cd ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/state/
ls taskHistory.backup.*.json
# Pick the most recent one and restore it
cp taskHistory.backup.1234567890.json taskHistory.json
```
</Tab>
<Tab title="JetBrains">
```bash
# macOS
cd ~/Library/Application\ Support/JetBrains/<IDE>/globalStorage/saoudrizwan.claude-dev/state/
ls taskHistory.backup.*.json
# Pick the most recent one and restore it
cp taskHistory.backup.1234567890.json taskHistory.json
```
</Tab>
</Tabs>
### Migrating to a New Machine
Switching to a new computer? You can bring all your Cline conversations with you. The process is the same whether you're using VS Code or a JetBrains IDE.
<Steps>
<Step title="Locate and copy your Cline data">
On your **old machine**, find the `saoudrizwan.claude-dev` folder using the [storage paths above](#storage-paths) and copy the entire folder.
</Step>
<Step title="Set up your new machine">
On your **new machine**, install your IDE (VS Code or JetBrains) and the Cline extension.
</Step>
<Step title="Close your IDE">
Make sure your IDE is completely closed before proceeding.
</Step>
<Step title="Transfer your data">
Paste the `saoudrizwan.claude-dev` folder to the same storage path on your new machine.
</Step>
<Step title="Launch and verify">
Open your IDE. Your task history should now appear in Cline.
</Step>
</Steps>
<Info>
The data format is the same across operating systems—cross-platform migration (e.g., Windows → macOS) works without any additional steps.
</Info>
## Common Problems
Here are some common issues and their solutions:
### History empty after VS Code update
Run **"Cline: Reconstruct Task History"** from the command palette. If that doesn't work, check if there's a backup file to restore.
### History lost after reinstalling VS Code
VS Code usually keeps extension data when uninstalled. Simply reinstall Cline and run the recovery command.
### "No tasks found" error
This occurs when the `tasks/` folder is empty or missing. Common causes:
- VS Code data was completely cleared
- You're looking at the wrong VS Code installation (standard vs Insiders)
- The folder was manually deleted
Check the correct storage path for your IDE and verify the folder exists.
### Recovery runs but some tasks missing
Corrupted task folders get skipped during recovery. The command will show how many tasks were recovered vs skipped. Check the error messages for details about which tasks couldn't be restored.
## Need Additional Help?
If you need assistance, you can:
1. **Open a GitHub issue** at [cline/cline](https://github.com/cline/cline/issues)
2. **Ask for help on our Discord server** - join our community for faster support
When reporting an issue, please include:
- Your OS and IDE (VS Code or JetBrains IDE name/version)
- Cline version
- What happened before the data loss
- Any error messages
- **Task export data**: Include the relevant JSON files from the affected task folder (e.g., `api_conversation_history.json`, `ui_messages.json`, `task_metadata.json`) to help us understand what went wrong
@@ -535,7 +535,7 @@ CAPABILITIES
- You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring.
- You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task.
- For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the replace_in_file tool to implement changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed.
- You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Prefer non-interactive commands when possible: use flags to disable pagers (e.g., '--no-pager'), auto-confirm prompts (e.g., '-y' when safe), provide input via flags/arguments rather than stdin, suppress interactive behavior, etc. For long-running commands, the user may keep them running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${
- You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${
supportsBrowserUse
? "\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser."
: ""
@@ -551,7 +551,7 @@ CAPABILITIES
- You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring.
- You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task.
- For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the replace_in_file tool to implement changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed.
- You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Prefer non-interactive commands when possible: use flags to disable pagers (e.g., '--no-pager'), auto-confirm prompts (e.g., '-y' when safe), provide input via flags/arguments rather than stdin, suppress interactive behavior, etc. For long-running commands, the user may keep them running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${
- You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${
supportsBrowserUse
? "\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser."
: ""
+1422
View File
File diff suppressed because it is too large Load Diff
+45 -46
View File
@@ -1,48 +1,47 @@
{
"name": "cline-evals",
"version": "0.1.0",
"description": "Evaluation scripts and tools for Cline",
"main": "cli/dist/index.js",
"scripts": {
"build:cli": "cd cli && tsc",
"start:cli": "cd cli && node dist/index.js",
"dev:cli": "cd cli && ts-node src/index.ts",
"diff-eval": "./diff-edits/run_and_open_dashboard.sh",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"cline",
"evaluation",
"benchmark",
"diff-edits"
],
"author": "",
"license": "MIT",
"dependencies": {
"axios": "^1.12.0",
"better-sqlite3": "^12.4.1",
"chalk": "5.6.2",
"dotenv": "^16.5.0",
"commander": "^9.4.1",
"execa": "^5.1.1",
"node-fetch": "^2.7.0",
"ora": "^5.4.1",
"sqlite": "^4.1.2",
"tiktoken": "^1.0.21",
"uuid": "^9.0.0",
"yargs": "^17.6.2"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.3",
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.12",
"@types/uuid": "^9.0.0",
"@types/yargs": "^17.0.19",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
},
"overrides": {
"tar-fs": "^3.1.1",
"js-yaml": "^4.1.1"
}
"name": "cline-evals",
"version": "0.1.0",
"description": "Evaluation scripts and tools for Cline",
"main": "cli/dist/index.js",
"scripts": {
"build:cli": "cd cli && tsc",
"start:cli": "cd cli && node dist/index.js",
"dev:cli": "cd cli && ts-node src/index.ts",
"diff-eval": "./diff-edits/run_and_open_dashboard.sh",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"cline",
"evaluation",
"benchmark",
"diff-edits"
],
"author": "",
"license": "MIT",
"dependencies": {
"axios": "^1.12.0",
"better-sqlite3": "^12.4.1",
"chalk": "5.6.2",
"dotenv": "^16.5.0",
"commander": "^9.4.1",
"execa": "^5.1.1",
"node-fetch": "^2.7.0",
"ora": "^5.4.1",
"sqlite": "^4.1.2",
"tiktoken": "^1.0.21",
"uuid": "^9.0.0",
"yargs": "^17.6.2"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.3",
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.12",
"@types/uuid": "^9.0.0",
"@types/yargs": "^17.0.19",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
},
"overrides": {
"tar-fs": "^3.1.1"
}
}
+1047 -485
View File
File diff suppressed because it is too large Load Diff
+16 -56
View File
@@ -2,7 +2,7 @@
"name": "claude-dev",
"displayName": "Cline",
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
"version": "3.40.0",
"version": "3.37.1",
"icon": "assets/icons/icon.png",
"engines": {
"vscode": "^1.84.0"
@@ -46,15 +46,6 @@
],
"main": "./dist/extension.js",
"contributes": {
"icons": {
"cline-icon": {
"description": "cline",
"default": {
"fontPath": "assets/icons/cline-bot.woff",
"fontCharacter": "\\e900"
}
}
},
"walkthroughs": [
{
"id": "ClineWalkthrough",
@@ -183,7 +174,10 @@
"command": "cline.generateGitCommitMessage",
"title": "Generate Commit Message with Cline",
"category": "Cline",
"icon": "$(cline-icon)"
"icon": {
"light": "assets/icons/robot_panel_light.png",
"dark": "assets/icons/robot_panel_dark.png"
}
},
{
"command": "cline.abortGitCommitMessage",
@@ -210,26 +204,9 @@
"command": "cline.reconstructTaskHistory",
"title": "Reconstruct Task History",
"category": "Cline"
},
{
"command": "cline.reviewComment.reply",
"title": "Reply",
"category": "Cline",
"enablement": "!commentIsEmpty"
},
{
"command": "cline.reviewComment.addToChat",
"title": "Add to Cline Chat",
"category": "Cline",
"icon": "$(link-external)"
}
],
"keybindings": [
{
"command": "editor.action.submitComment",
"key": "enter",
"when": "commentEditorFocused && commentController == cline-ai-review && !commentIsEmpty"
},
{
"command": "cline.addToChat",
"key": "cmd+'",
@@ -312,24 +289,6 @@
{
"command": "cline.abortGitCommitMessage",
"when": "config.git.enabled && cline.isGeneratingCommit"
},
{
"command": "cline.reviewComment.reply",
"when": "false"
}
],
"comments/commentThread/context": [
{
"command": "cline.reviewComment.reply",
"group": "inline",
"when": "commentController == cline-ai-review"
}
],
"comments/commentThread/title": [
{
"command": "cline.reviewComment.addToChat",
"group": "inline",
"when": "commentController == cline-ai-review"
}
]
},
@@ -347,6 +306,8 @@
"compile-cli-all-platforms": "scripts/build-cli-all-platforms.sh",
"compile-cli-man-page": "pandoc cli/man/cline.1.md -s -t man -o cli/man/cline.1",
"build:npm": "scripts/build-npm-package.sh",
"build:docker:dev": "node scripts/build-docker-dev.mjs",
"docker:shell": "node scripts/docker-shell.mjs",
"test:install": "bash scripts/test-install.sh",
"dev:cli:watch": "node scripts/dev-cli-watch.mjs",
"postcompile-standalone": "node scripts/package-standalone.mjs",
@@ -400,8 +361,7 @@
"docs": "cd docs && npm run dev",
"docs:check-links": "cd docs && npm run check",
"docs:rename-file": "cd docs && npm run rename",
"report-issue": "node scripts/report-issue.js",
"storybook": "cd webview-ui && npm run storybook"
"report-issue": "node scripts/report-issue.js"
},
"lint-staged": {
"*": [
@@ -459,7 +419,7 @@
"@bufbuild/protobuf": "^2.2.5",
"@cerebras/cerebras_cloud_sdk": "^1.35.0",
"@google-cloud/vertexai": "^1.9.3",
"@google/genai": "^1.30.0",
"@google/genai": "^1.11.0",
"@grpc/grpc-js": "^1.9.15",
"@grpc/reflection": "^1.0.4",
"@mistralai/mistralai": "^1.5.0",
@@ -484,8 +444,9 @@
"@opentelemetry/sdk-trace-node": "^1.30.1",
"@opentelemetry/semantic-conventions": "^1.37.0",
"@playwright/test": "^1.55.1",
"@sap-ai-sdk/ai-api": "^2.1.0",
"@sap-ai-sdk/orchestration": "^2.1.0",
"@sap-ai-sdk/ai-api": "^1.17.0",
"@sap-ai-sdk/orchestration": "^1.17.0",
"@sentry/browser": "^9.12.0",
"@streamparser/json": "^0.0.22",
"@tailwindcss/vite": "^4.1.14",
"@types/uuid": "^10.0.0",
@@ -502,6 +463,7 @@
"exceljs": "^4.4.0",
"execa": "^9.5.2",
"fast-deep-equal": "^3.1.3",
"firebase": "^11.2.0",
"fzf": "^0.5.2",
"get-folder-size": "^5.0.0",
"globby": "^14.0.2",
@@ -511,6 +473,7 @@
"image-size": "^2.0.2",
"isbinaryfile": "^5.0.2",
"jschardet": "^3.1.4",
"jwt-decode": "^4.0.0",
"mammoth": "^1.11.0",
"nanoid": "^5.1.6",
"nice-grpc": "^2.1.12",
@@ -518,7 +481,7 @@
"ollama": "^0.5.13",
"open": "^10.1.2",
"open-graph-scraper": "^6.9.0",
"openai": "^6.9.0",
"openai": "^4.83.0",
"os-name": "^6.0.0",
"p-mutex": "^1.0.0",
"p-timeout": "^6.1.4",
@@ -543,10 +506,7 @@
"zod": "^3.24.2"
},
"overrides": {
"tar-fs": ">=3.1.1",
"tar": "^7.5.2",
"vite": "^7.1.11",
"js-yaml": "^4.1.1"
"tar-fs": ">=3.1.1"
},
"c8": {
"reporter": [
-70
View File
@@ -69,18 +69,6 @@ service FileService {
// Opens or creates a focus chain checklist markdown file for editing
rpc openFocusChainFile(StringRequest) returns (Empty);
// Refreshes all hook toggles (discovers hooks and their enabled state)
rpc refreshHooks(EmptyRequest) returns (HooksToggles);
// Toggles a hook on or off via chmod +x/-x
rpc toggleHook(ToggleHookRequest) returns (ToggleHookResponse);
// Creates a new hook from template
rpc createHook(CreateHookRequest) returns (CreateHookResponse);
// Deletes an existing hook file
rpc deleteHook(DeleteHookRequest) returns (DeleteHookResponse);
}
// Response for refreshRules operation
@@ -220,61 +208,3 @@ message ToggleWorkflowRequest {
bool enabled = 3;
RuleScope scope = 4; // Scope of the workflow (local, global, or remote)
}
// Maps from hook name to enabled/disabled status
message HookInfo {
string name = 1;
bool enabled = 2;
string absolute_path = 3;
}
message WorkspaceHooks {
string workspace_name = 1;
repeated HookInfo hooks = 2;
}
message HooksToggles {
repeated HookInfo global_hooks = 1;
repeated WorkspaceHooks workspace_hooks = 2;
bool is_windows = 3; // Whether the system is Windows (toggles disabled)
}
// Request to toggle a hook
message ToggleHookRequest {
Metadata metadata = 1;
string hook_name = 2; // Name of the hook (e.g., "TaskStart")
bool is_global = 3; // Whether this is a global or workspace hook
bool enabled = 4; // Whether to enable (chmod +x) or disable (chmod -x)
optional string workspace_name = 5; // For multi-root workspaces, specifies which workspace
}
// Response for toggleHook operation
message ToggleHookResponse {
HooksToggles hooks_toggles = 1;
}
// Request to create a hook
message CreateHookRequest {
Metadata metadata = 1;
string hook_name = 2; // Name of the hook to create
bool is_global = 3; // Whether to create in global or workspace hooks directory
optional string workspace_name = 4; // For multi-root workspaces, specifies which workspace
}
// Response for createHook operation
message CreateHookResponse {
HooksToggles hooks_toggles = 1;
}
// Request to delete a hook
message DeleteHookRequest {
Metadata metadata = 1;
string hook_name = 2; // Name of the hook to delete
bool is_global = 3; // Whether this is a global or workspace hook
optional string workspace_name = 4; // For multi-root workspaces, specifies which workspace
}
// Response for deleteHook operation
message DeleteHookResponse {
HooksToggles hooks_toggles = 1;
}
-8
View File
@@ -27,12 +27,8 @@ service ModelsService {
rpc refreshRequestyModels(EmptyRequest) returns (OpenRouterCompatibleModelInfo);
// Refreshes and returns Hicap models
rpc refreshHicapModels(EmptyRequest) returns (OpenRouterCompatibleModelInfo);
// Refreshes and returns LiteLLM models
rpc refreshLiteLlmModelsRpc(EmptyRequest) returns (OpenRouterCompatibleModelInfo);
// Subscribe to OpenRouter models updates
rpc subscribeToOpenRouterModels(EmptyRequest) returns (stream OpenRouterCompatibleModelInfo);
// Subscribe to LiteLLM models updates
rpc subscribeToLiteLlmModels(EmptyRequest) returns (stream OpenRouterCompatibleModelInfo);
// Updates API configuration (legacy - uses combined configuration)
rpc updateApiConfigurationProto(UpdateApiConfigurationRequest) returns (Empty);
// Updates API configuration (new - uses separate options and secrets)
@@ -101,8 +97,6 @@ message OpenRouterModelInfo {
optional bool supports_global_endpoint = 11;
repeated ModelTier tiers = 12;
optional string name = 13;
optional double temperature = 14;
optional bool supports_reasoning = 15;
}
// Shared response message for model information
@@ -594,7 +588,6 @@ message ModelsApiConfiguration {
optional string plan_mode_aihubmix_model_id = 135;
optional OpenAiCompatibleModelInfo plan_mode_aihubmix_model_info = 136;
optional string plan_mode_nous_research_model_id = 137;
optional string gemini_plan_mode_thinking_level = 138;
// Act mode configurations
optional ApiProvider act_mode_api_provider = 200;
@@ -635,5 +628,4 @@ message ModelsApiConfiguration {
optional string act_mode_aihubmix_model_id = 235;
optional OpenAiCompatibleModelInfo act_mode_aihubmix_model_info = 236;
optional string act_mode_nous_research_model_id = 237;
optional string gemini_act_mode_thinking_level = 238;
}
+1 -22
View File
@@ -28,8 +28,6 @@ service StateService {
rpc updateInfoBannerVersion(Int64Request) returns (Empty);
rpc updateModelBannerVersion(Int64Request) returns (Empty);
rpc updateCliBannerVersion(Int64Request) returns (Empty);
rpc dismissBanner(StringRequest) returns (Empty);
rpc trackBannerEvent(TrackBannerEventRequest) returns (Empty);
rpc installClineCli(EmptyRequest) returns (Empty);
rpc checkCliInstallation(EmptyRequest) returns (Boolean);
rpc getProcessInfo(EmptyRequest) returns (ProcessInfo);
@@ -364,7 +362,7 @@ message UpdateSettingsRequest {
optional int32 subagent_terminal_output_line_limit = 30;
optional string cline_env = 31;
optional bool native_tool_call_enabled = 32;
optional OnboardingModelGroup onboarding_models = 33;
optional bool show_onboarding_flow = 33;
}
message UpdateTerminalConnectionTimeoutRequest {
@@ -392,22 +390,3 @@ message OnboardingProgressRequest {
optional bool completed = 3;
optional string model_selected = 4;
}
message OnboardingModelGroup {
repeated OnboardingModel models = 1;
}
message OnboardingModel {
string id = 1;
string name = 2;
int32 score = 3;
int32 latency = 4;
string badge = 5;
string group = 6;
OpenRouterModelInfo info = 7;
}
message TrackBannerEventRequest {
string banner_id = 1;
string event_type = 2;
}
-9
View File
@@ -40,8 +40,6 @@ service TaskService {
rpc executeQuickWin(ExecuteQuickWinRequest) returns (Empty);
// Deletes all task history
rpc deleteAllTaskHistory(EmptyRequest) returns (DeleteAllTaskHistoryCount);
// Explains changes with AI and adds inline comments to the diff view
rpc explainChanges(ExplainChangesRequest) returns (Empty);
}
// Request message for creating a new task
@@ -125,10 +123,3 @@ message ExecuteQuickWinRequest {
message DeleteAllTaskHistoryCount {
int32 tasks_deleted = 1;
}
// Request for explaining changes with AI
message ExplainChangesRequest {
Metadata metadata = 1;
// Timestamp of the completion message to explain changes for
int64 message_ts = 2;
}
-1
View File
@@ -66,7 +66,6 @@ enum ClineSay {
INFO = 26;
TASK_PROGRESS = 27;
ERROR_RETRY = 28;
GENERATE_EXPLANATION = 29;
}
// Enum for ClineSayTool tool types
+80
View File
@@ -0,0 +1,80 @@
#!/usr/bin/env node
import { execSync } from "child_process"
/**
* Build Docker image for Cline CLI
* This script builds a Docker image using pre-built binaries from dist-standalone/
*
* Prerequisites:
* - Run `npm run compile-standalone` first to build all platform binaries
* - Run `npm run compile-cli` first to build CLI binaries
*/
function runCommand(command, description) {
console.log(`\n${description}...`)
try {
execSync(command, { stdio: "inherit" })
console.log("✓ Success\n")
} catch (error) {
console.error(`✗ Failed: ${error.message}`)
process.exit(1)
}
}
function getCommandOutput(command) {
try {
return execSync(command, { encoding: "utf-8" }).trim()
} catch (error) {
return ""
}
}
function buildPrerequisites() {
console.log("Building prerequisites...\n")
// Build standalone (includes cline-core and platform-specific native modules)
runCommand("npm run compile-standalone", "Running npm run compile-standalone")
// Build CLI binaries for all platforms
runCommand("npm run compile-cli-all-platforms", "Running npm run compile-cli-all-platforms")
console.log("✓ All prerequisites built successfully\n")
}
function main() {
console.log("🐳 Building Cline CLI Docker Image\n")
// Remove existing container to ensure clean state after rebuild
const containerId = getCommandOutput(`docker ps -aq --filter "name=^cline-cli-dev$"`)
if (containerId) {
console.log("🗑️ Removing existing container to ensure fresh start...")
try {
execSync(`docker rm -f cline-cli-dev`, { stdio: "inherit" })
console.log("✓ Container removed\n")
} catch (error) {
console.log("Note: Container cleanup failed, continuing anyway\n")
}
}
buildPrerequisites()
// Build Docker image for native platform
// Docker will automatically use the correct architecture (arm64 on Apple Silicon, amd64 on Intel)
runCommand("docker build -f docker/Dockerfile -t cline-cli:dev .", "Building Docker image")
console.log("✅ Docker image built successfully!")
console.log("\n📋 Next steps:\n")
console.log("Interactive shell:")
console.log(" npm run docker:shell\n")
console.log("This will:")
console.log(" • Reuse existing 'cline-cli-dev' container if running")
console.log(" • Start stopped container if it exists")
console.log(" • Create new persistent container if none exists")
console.log(" • Mount current directory at /workspace")
console.log(" • Provide all CLI commands (cline auth, cline task, etc.)")
console.log("\nContainer persists between sessions. To remove:")
console.log(" docker rm -f cline-cli-dev\n")
}
main()
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env node
import { execSync } from "child_process"
import { platform } from "os"
const CONTAINER_NAME = "cline-cli-dev"
function runCommand(command) {
try {
return execSync(command, { encoding: "utf-8" }).trim()
} catch (error) {
return ""
}
}
function getCurrentDirectory() {
// Get current working directory in a cross-platform way
return process.cwd()
}
function main() {
console.log("🐳 Cline CLI Docker Shell\n")
// Check if container exists (running or stopped)
const containerId = runCommand(`docker ps -a --filter "name=^${CONTAINER_NAME}$" --format "{{.ID}}"`)
if (containerId) {
// Check if container is running
const isRunning = runCommand(`docker ps --filter "id=${containerId}" --format "{{.ID}}"`)
if (isRunning) {
console.log(`📦 Connecting to running container: ${CONTAINER_NAME}\n`)
try {
execSync(`docker exec -it ${containerId} /bin/bash`, { stdio: "inherit" })
} catch (error) {
// User exited shell normally
}
} else {
console.log(`▶️ Starting stopped container: ${CONTAINER_NAME}\n`)
try {
execSync(`docker start ${containerId}`, { stdio: "inherit" })
execSync(`docker exec -it ${containerId} /bin/bash`, { stdio: "inherit" })
} catch (error) {
// User exited shell normally
}
}
} else {
console.log(`🚀 Creating new container: ${CONTAINER_NAME}\n`)
const cwd = getCurrentDirectory()
try {
// Use different volume mount syntax for Windows vs Unix
const isWindows = platform() === "win32"
const volumeMount = isWindows ? `${cwd.replace(/\\/g, "/")}:/workspace` : `${cwd}:/workspace`
execSync(
`docker run -it --name ${CONTAINER_NAME} -v "${volumeMount}" -w /workspace --entrypoint /bin/bash cline-cli:dev`,
{ stdio: "inherit" },
)
} catch (error) {
// User exited shell normally
}
}
}
main()
+1 -1
View File
@@ -11,7 +11,7 @@ import fs from "fs"
import https from "https"
import path from "path"
import { pipeline } from "stream/promises"
import * as tar from "tar"
import tar from "tar"
import { promisify } from "util"
import { createGunzip } from "zlib"
+1
View File
@@ -24,6 +24,7 @@ const TARGET_PLATFORMS = [
{ platform: "darwin", arch: "x64", targetDir: "darwin-x64" },
{ platform: "darwin", arch: "arm64", targetDir: "darwin-arm64" },
{ platform: "linux", arch: "x64", targetDir: "linux-x64" },
{ platform: "linux", arch: "arm64", targetDir: "linux-arm64" },
]
const SUPPORTED_BINARY_MODULES = ["better-sqlite3"]
+3 -3
View File
@@ -1,5 +1,5 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { ModelInfo } from "@shared/api"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler } from "../../core/api/index"
import { ApiStream } from "../../core/api/transform/stream"
@@ -33,7 +33,7 @@ export class DifyHandler implements ApiHandler {
}
}
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
console.log("[DIFY DEBUG] createMessage called with:", {
systemPromptLength: systemPrompt?.length || 0,
messagesCount: messages?.length || 0,
@@ -255,7 +255,7 @@ export class DifyHandler implements ApiHandler {
}
}
private convertMessagesToQuery(systemPrompt: string, messages: ClineStorageMessage[]): string {
private convertMessagesToQuery(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): string {
// Dify's context is managed by `conversation_id`. The `query` should be the last user message.
// The system prompt is typically configured in the Dify App itself.
const lastUserMessage = messages.filter((m) => m.role === "user").pop()
+29
View File
@@ -9,6 +9,14 @@ export interface EnvironmentConfig {
appBaseUrl: string
apiBaseUrl: string
mcpBaseUrl: string
firebase: {
apiKey: string
authDomain: string
projectId: string
storageBucket?: string
messagingSenderId?: string
appId?: string
}
}
class ClineEndpoint {
@@ -55,6 +63,14 @@ class ClineEndpoint {
appBaseUrl: "https://staging-app.cline.bot",
apiBaseUrl: "https://core-api.staging.int.cline.bot",
mcpBaseUrl: "https://core-api.staging.int.cline.bot/v1/mcp",
firebase: {
apiKey: "AIzaSyASSwkwX1kSO8vddjZkE5N19QU9cVQ0CIk",
authDomain: "cline-staging.firebaseapp.com",
projectId: "cline-staging",
storageBucket: "cline-staging.firebasestorage.app",
messagingSenderId: "853479478430",
appId: "1:853479478430:web:2de0dba1c63c3262d4578f",
},
}
case Environment.local:
return {
@@ -62,6 +78,11 @@ class ClineEndpoint {
appBaseUrl: "http://localhost:3000",
apiBaseUrl: "http://localhost:7777",
mcpBaseUrl: "https://api.cline.bot/v1/mcp",
firebase: {
apiKey: "AIzaSyD8wtkd1I-EICuAg6xgAQpRdwYTvwxZG2w",
authDomain: "cline-preview.firebaseapp.com",
projectId: "cline-preview",
},
}
default:
return {
@@ -69,6 +90,14 @@ class ClineEndpoint {
appBaseUrl: "https://app.cline.bot",
apiBaseUrl: "https://api.cline.bot",
mcpBaseUrl: "https://api.cline.bot/v1/mcp",
firebase: {
apiKey: "AIzaSyC5rx59Xt8UgwdU3PCfzUF7vCwmp9-K2vk",
authDomain: "cline-prod.firebaseapp.com",
projectId: "cline-prod",
storageBucket: "cline-prod.firebasestorage.app",
messagingSenderId: "941048379330",
appId: "1:941048379330:web:45058eedeefc5cdfcc485b",
},
}
}
}
File diff suppressed because it is too large Load Diff
-606
View File
@@ -1,606 +0,0 @@
import { ClineStorageMessage } from "@/shared/messages/content"
const APPLY_PATCH_PATCH_REGEX = /\*\*\* Begin Patch\s+([\s\S]*?)\s+\*\*\* End Patch/m
/**
* Convert apply_patch tool calls to write_to_file and replace_in_file format
*/
export function convertApplyPatchToolCalls(messages: Array<ClineStorageMessage>): Array<ClineStorageMessage> {
// Map to track tool_use_id to converted tool info and original input
const toolUseIdMap = new Map<string, { name: string; input: any; originalInput: any }>()
return messages.map((message) => {
if (!Array.isArray(message.content)) {
return message
}
const convertedContent = message.content.map((block) => {
// Handle tool_use blocks
if (block.type === "tool_use" && block.name === "apply_patch") {
const converted = convertApplyPatchToToolCalls(block.input)
// Store the conversion with original input for matching tool_result
toolUseIdMap.set(block.id, { ...converted, originalInput: block.input })
return {
...block,
name: converted.name,
input: converted.input,
}
}
// Handle tool_result blocks
if (block.type === "tool_result") {
const conversion = toolUseIdMap.get(block.tool_use_id)
if (conversion) {
// Reconstruct the tool_result content to match apply_patch format
const reconstructedContent = reconstructApplyPatchResult(
block,
conversion.name,
conversion.input,
conversion.originalInput,
)
return {
...block,
content: reconstructedContent,
}
}
}
return block
})
return {
...message,
content: convertedContent,
}
})
}
interface ConvertedTool {
name: string
input: any
}
/**
* Parse apply_patch input and convert to write_to_file or replace_in_file format
*/
function convertApplyPatchToToolCalls(input: any): ConvertedTool {
const patchInput = typeof input === "string" ? input : input?.input || ""
// Parse the patch format
const patchMatch = patchInput.match(APPLY_PATCH_PATCH_REGEX)
if (!patchMatch) {
// If we can't parse it, return as-is with write_to_file
return {
name: "write_to_file",
input: input,
}
}
const patchContent = patchMatch[1]
// Extract file operation (Add, Update, or Delete)
const fileMatch = patchContent.match(/\*\*\* (Add|Update|Delete) File: (.+?)(?:\n|$)/m)
if (!fileMatch) {
return {
name: "write_to_file",
input: input,
}
}
const action = fileMatch[1]
const filePath = fileMatch[2].trim()
// If it's an Add operation, convert to write_to_file
if (action === "Add") {
// Extract the content after the file line
const contentAfterFile = patchContent.substring(fileMatch.index! + fileMatch[0].length)
return {
name: "write_to_file",
input: {
absolutePath: filePath,
content: extractNewContentFromPatch(contentAfterFile),
},
}
}
// If it's Update or Delete, convert to replace_in_file
if (action === "Update" || action === "Delete") {
const diff = convertPatchToDiff(patchContent.substring(fileMatch.index! + fileMatch[0].length))
return {
name: "replace_in_file",
input: {
absolutePath: filePath,
diff: diff,
},
}
}
// Fallback
return {
name: "write_to_file",
input: input,
}
}
/**
* Extract new content from add operation patch
*/
function extractNewContentFromPatch(patchContent: string): string {
// For Add operations, the patch should contain lines starting with +
const lines = patchContent.split("\n")
const contentLines: string[] = []
for (const line of lines) {
if (line.startsWith("+")) {
// Remove the + prefix and exactly ONE space if present (but not if it's a tab)
let content = line.substring(1)
if (content.startsWith(" ") && !content.startsWith("\t")) {
content = content.substring(1)
}
contentLines.push(content)
}
}
return contentLines.join("\n")
}
/**
* Convert V4A patch format to SEARCH/REPLACE format
*/
function convertPatchToDiff(patchContent: string): string {
const diffBlocks: string[] = []
const lines = patchContent.split("\n")
let i = 0
while (i < lines.length) {
const line = lines[i]
// Skip empty lines at the start
if (!line.trim() && i === 0) {
i++
continue
}
// Check if this is the start of a hunk (@@) or a direct change line
if (line.trim().startsWith("@@") || line.startsWith("-") || line.startsWith("+")) {
const currentSearch: string[] = []
const currentReplace: string[] = []
// Collect @@ context marker lines
// @@ prefix marks context lines. If @@something, then "something" is context.
// If just @@, then it's an empty context line.
while (i < lines.length && lines[i].trim().startsWith("@@")) {
const trimmedLine = lines[i].trim()
// Extract the actual context content after @@
const contextLine = trimmedLine.substring(2)
// Always add the context line (even if empty)
currentSearch.push(contextLine)
currentReplace.push(contextLine)
i++
}
if (i >= lines.length) {
break
}
// Collect all remaining lines in this hunk until we hit end of content or next @@
const hunkLines: string[] = []
while (i < lines.length) {
// Check if this is a new hunk (starts with @@)
if (lines[i].trim().startsWith("@@")) {
break
}
hunkLines.push(lines[i])
i++
}
// Now process the hunk to build SEARCH/REPLACE
let hasChanges = false
for (let j = 0; j < hunkLines.length; j++) {
const hunkLine = hunkLines[j]
if (hunkLine.startsWith("-")) {
hasChanges = true
// Strip the - prefix and exactly ONE space if present (but not if it's a tab)
let content = hunkLine.substring(1)
if (content.startsWith(" ") && !content.startsWith(" \t")) {
content = content.substring(1)
}
currentSearch.push(content)
} else if (hunkLine.startsWith("+")) {
hasChanges = true
// Strip the + prefix and exactly ONE space if present (but not if it's a tab)
let content = hunkLine.substring(1)
if (content.startsWith(" ") && !content.startsWith(" \t")) {
content = content.substring(1)
}
currentReplace.push(content)
} else {
// Context line without @@ prefix - add to both sides
currentSearch.push(hunkLine)
currentReplace.push(hunkLine)
}
}
// Create the diff block if we have changes
if (hasChanges && (currentSearch.length > 0 || currentReplace.length > 0)) {
diffBlocks.push(
"------- SEARCH\n" +
currentSearch.join("\n") +
"\n=======\n" +
currentReplace.join("\n") +
"\n+++++++ REPLACE",
)
}
} else {
i++
}
}
return diffBlocks.join("\n")
}
/**
* Reconstruct tool_result content to match apply_patch format by extracting
* the final file content and converting it back to V4A patch format
*/
function reconstructApplyPatchResult(
block: any,
convertedToolName: string,
_convertedInput: any,
originalInput: any,
): string | any[] {
// Extract the content from the tool_result
const content = typeof block.content === "string" ? block.content : ""
// Try to extract the final_file_content
const finalContentMatch = content.match(/<final_file_content path="([^"]+)">\s*([\s\S]*?)\s*<\/final_file_content>/)
if (!finalContentMatch) {
// If no final_file_content found, return original content
return block.content
}
const filePath = finalContentMatch[1]
const finalContent = finalContentMatch[2]
// Reconstruct the result message based on the converted tool type
if (convertedToolName === "write_to_file") {
// For write_to_file, we just need to confirm the file was created/written
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully saved to ${filePath}.\n\nThe file has been created/updated with the new content.`
}
if (convertedToolName === "replace_in_file") {
// For replace_in_file, we need to reconstruct the V4A patch format result
// Try to parse the original patch to get the action and build context
const patchInput = typeof originalInput === "string" ? originalInput : originalInput?.input || ""
const patchMatch = patchInput.match(APPLY_PATCH_PATCH_REGEX)
if (patchMatch) {
const patchContent = patchMatch[1]
const fileMatch = patchContent.match(/\*\*\* (Add|Update|Delete) File: (.+?)(?:\n|$)/m)
if (fileMatch) {
const action = fileMatch[1]
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully updated in ${filePath}.\n\nThe file has been modified using ${action} operation.\n\n<final_file_content path="${filePath}">\n${finalContent}\n</final_file_content>\n\nIMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference.`
}
}
// Fallback for replace_in_file
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully updated in ${filePath}.\n\n<final_file_content path="${filePath}">\n${finalContent}\n</final_file_content>\n\nIMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference.`
}
// Default fallback
return block.content
}
/**
* Convert write_to_file and replace_in_file tool calls to apply_patch format
*/
export function convertWriteToFileToolCalls(messages: Array<ClineStorageMessage>): Array<ClineStorageMessage> {
// Map to track tool_use_id to converted tool info and original input
const toolUseIdMap = new Map<string, { originalName: string; originalInput: any; patchInput?: string }>()
// First pass: collect tool_use blocks
for (const message of messages) {
if (!Array.isArray(message.content)) {
continue
}
for (const block of message.content) {
if (block.type === "tool_use" && (block.name === "write_to_file" || block.name === "replace_in_file")) {
toolUseIdMap.set(block.id, {
originalName: block.name,
originalInput: block.input,
})
}
}
}
// Second pass: find tool_results and extract final content to build proper patches
const finalContentMap = new Map<string, string>()
for (const message of messages) {
if (!Array.isArray(message.content)) {
continue
}
for (const block of message.content) {
if (block.type === "tool_result" && toolUseIdMap.has(block.tool_use_id)) {
const content = typeof block.content === "string" ? block.content : ""
const finalContentMatch = content.match(
/<final_file_content path="([^"]+)">\s*([\s\S]*?)\s*<\/final_file_content>/,
)
if (finalContentMatch) {
finalContentMap.set(block.tool_use_id, finalContentMatch[2])
}
}
}
}
// Third pass: convert messages
return messages.map((message) => {
if (!Array.isArray(message.content)) {
return message
}
const convertedContent = message.content.map((block) => {
// Handle tool_use blocks for write_to_file and replace_in_file
if (block.type === "tool_use" && (block.name === "write_to_file" || block.name === "replace_in_file")) {
const finalContent = finalContentMap.get(block.id)
const patchInput = convertToPatchFormat(block.name, block.input, finalContent)
// Update the map with the generated patch
const existingEntry = toolUseIdMap.get(block.id)
if (existingEntry) {
existingEntry.patchInput = patchInput
}
return {
...block,
name: "apply_patch",
input: {
input: patchInput,
},
}
}
// Handle tool_result blocks
if (block.type === "tool_result") {
const conversion = toolUseIdMap.get(block.tool_use_id)
if (conversion) {
// Reconstruct the tool_result content to match apply_patch format
const reconstructedContent = reconstructWriteToFileResult(
block,
conversion.originalName,
conversion.originalInput,
)
return {
...block,
content: reconstructedContent,
}
}
}
return block
})
return {
...message,
content: convertedContent,
}
})
}
/**
* Convert write_to_file or replace_in_file input to apply_patch format
*/
function convertToPatchFormat(toolName: string, input: any, finalContent?: string): string {
const filePath = input.absolutePath || input.path || ""
if (toolName === "write_to_file") {
// Convert write_to_file to Add operation
const content = input.content || ""
const lines = content.split("\n")
const patchLines = ["@@"]
patchLines.push(...lines.map((line: string) => `+ ${line}`))
return `apply_patch <<"EOF"
*** Begin Patch
*** Add File: ${filePath}
${patchLines.join("\n")}
*** End Patch
EOF`
}
if (toolName === "replace_in_file") {
// Convert replace_in_file to Update operation
const diff = input.diff || ""
// Parse SEARCH/REPLACE blocks and convert to V4A format with context
const patchContent = convertDiffToPatchWithContext(diff, finalContent)
return `apply_patch <<"EOF"
*** Begin Patch
*** Update File: ${filePath}
${patchContent}
*** End Patch
EOF`
}
return ""
}
/**
* Convert SEARCH/REPLACE diff format to V4A patch format with additional context from final content
*/
function convertDiffToPatchWithContext(diff: string, finalContent?: string): string {
const patchLines: string[] = []
// Match all SEARCH/REPLACE blocks
const blockRegex = /------- SEARCH\s*\n([\s\S]*?)\n=======\s*\n([\s\S]*?)\n\+{7} REPLACE/g
let match
while ((match = blockRegex.exec(diff)) !== null) {
const searchContent = match[1]
const replaceContent = match[2]
const searchLines = searchContent.split("\n")
const replaceLines = replaceContent.split("\n")
// Find common prefix and suffix between search and replace
let prefixEnd = 0
while (
prefixEnd < searchLines.length &&
prefixEnd < replaceLines.length &&
searchLines[prefixEnd] === replaceLines[prefixEnd]
) {
prefixEnd++
}
let suffixStart = searchLines.length
let replaceSuffixStart = replaceLines.length
while (
suffixStart > prefixEnd &&
replaceSuffixStart > prefixEnd &&
searchLines[suffixStart - 1] === replaceLines[replaceSuffixStart - 1]
) {
suffixStart--
replaceSuffixStart--
}
// If we have finalContent, extract additional context from it
if (finalContent) {
const finalLines = finalContent.split("\n")
// Find where the replaced content appears in the final file
let matchIndex = -1
for (let i = 0; i < finalLines.length; i++) {
// Try to match the first replace line
if (replaceLines.length > 0 && finalLines[i] === replaceLines[0]) {
// Check if subsequent lines also match
let allMatch = true
for (let j = 1; j < replaceLines.length && i + j < finalLines.length; j++) {
if (finalLines[i + j] !== replaceLines[j]) {
allMatch = false
break
}
}
if (allMatch) {
matchIndex = i
break
}
}
}
if (matchIndex >= 0) {
// Extract up to 3 lines before as context
const contextStart = Math.max(0, matchIndex - 3)
const contextLines: string[] = []
for (let i = contextStart; i < matchIndex; i++) {
contextLines.push(finalLines[i])
}
// Pad to 3 lines if needed (with empty strings)
while (contextLines.length < 3) {
contextLines.unshift("")
}
// Add @@ marker with the first context line
if (contextLines[0] === "") {
patchLines.push("@@")
} else {
patchLines.push(`@@${contextLines[0]}`)
}
// Add remaining context lines (without @@ marker)
for (let i = 1; i < contextLines.length; i++) {
patchLines.push(contextLines[i])
}
// Add common prefix lines (without +/- markers)
for (let i = 0; i < prefixEnd; i++) {
patchLines.push(searchLines[i])
}
// Add the actual changes (lines that differ)
for (let i = prefixEnd; i < suffixStart; i++) {
patchLines.push(`- ${searchLines[i]}`)
}
for (let i = prefixEnd; i < replaceSuffixStart; i++) {
patchLines.push(`+ ${replaceLines[i]}`)
}
// Add common suffix lines (without +/- markers)
for (let i = suffixStart; i < searchLines.length; i++) {
patchLines.push(searchLines[i])
}
// Extract up to 3 lines after as trailing context (without @@ markers)
const contextEnd = Math.min(finalLines.length, matchIndex + replaceLines.length + 3)
for (let i = matchIndex + replaceLines.length; i < contextEnd; i++) {
patchLines.push(finalLines[i])
}
continue
}
}
// Fallback: if no finalContent or couldn't find match, use the prefix/suffix from SEARCH/REPLACE
patchLines.push("@@")
// Add common prefix lines (without +/- markers)
for (let i = 0; i < prefixEnd; i++) {
patchLines.push(searchLines[i])
}
// Add the actual changes (lines that differ)
for (let i = prefixEnd; i < suffixStart; i++) {
patchLines.push(`- ${searchLines[i]}`)
}
for (let i = prefixEnd; i < replaceSuffixStart; i++) {
patchLines.push(`+ ${replaceLines[i]}`)
}
// Add common suffix lines (without +/- markers)
for (let i = suffixStart; i < searchLines.length; i++) {
patchLines.push(searchLines[i])
}
}
return patchLines.join("\n")
}
/**
* Reconstruct tool_result content to match apply_patch result format
*/
function reconstructWriteToFileResult(block: any, originalToolName: string, originalInput: any): string | any[] {
// Extract the content from the tool_result
const content = typeof block.content === "string" ? block.content : ""
// Try to extract the final_file_content
const finalContentMatch = content.match(/<final_file_content path="([^"]+)">\s*([\s\S]*?)\s*<\/final_file_content>/)
const filePath = originalInput.absolutePath || originalInput.path || ""
if (!finalContentMatch) {
// If no final_file_content found, create a simple success message
if (originalToolName === "write_to_file") {
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully saved to ${filePath}.\n\nThe file has been created/updated with the new content.`
} else {
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully updated in ${filePath}.\n\nThe file has been modified.`
}
}
const finalContent = finalContentMatch[2]
// Reconstruct the result message based on the original tool type
if (originalToolName === "write_to_file") {
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully saved to ${filePath}.\n\nThe file has been created/updated with the new content.`
}
if (originalToolName === "replace_in_file") {
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully updated in ${filePath}.\n\nThe file has been modified using Update operation.\n\n<final_file_content path="${filePath}">\n${finalContent}\n</final_file_content>\n\nIMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference.`
}
// Default fallback
return block.content
}
-60
View File
@@ -1,60 +0,0 @@
import { ClineStorageMessage } from "@/shared/messages/content"
import { ClineDefaultTool } from "@/shared/tools"
import { convertApplyPatchToolCalls, convertWriteToFileToolCalls } from "./diff-editors"
/**
* Transforms tool call messages between different tool formats based on native tool support.
* Converts between apply_patch and write_to_file/replace_in_file formats as needed.
*
* @param clineMessages - Array of messages containing tool calls to transform
* @param nativeTools - Array of tools natively supported by the current provider
* @returns Transformed messages array, or original if no transformation needed
*/
export function transformToolCallMessages(
clineMessages: ClineStorageMessage[],
nativeTools?: ClineDefaultTool[],
): ClineStorageMessage[] {
// Early return if no messages or native tools provided
if (!clineMessages?.length || !nativeTools?.length) {
return clineMessages
}
// Create Sets for O(1) lookup performance
const nativeToolSet = new Set(nativeTools)
const usedToolSet = new Set<string>()
// Single pass: collect all tools used in assistant messages
for (const msg of clineMessages) {
if (msg.role === "assistant" && Array.isArray(msg.content)) {
for (const block of msg.content) {
if (block.type === "tool_use" && block.name) {
usedToolSet.add(block.name)
}
}
}
}
// Early return if no tools were used
if (usedToolSet.size === 0) {
return clineMessages
}
// Determine which conversion to apply
const hasApplyPatchNative = nativeToolSet.has(ClineDefaultTool.APPLY_PATCH)
const hasFileEditNative = nativeToolSet.has(ClineDefaultTool.FILE_EDIT) || nativeToolSet.has(ClineDefaultTool.FILE_NEW)
const hasApplyPatchUsed = usedToolSet.has(ClineDefaultTool.APPLY_PATCH)
const hasFileEditUsed = usedToolSet.has(ClineDefaultTool.FILE_EDIT) || usedToolSet.has(ClineDefaultTool.FILE_NEW)
// Convert write_to_file/replace_in_file → apply_patch
if (hasApplyPatchNative && hasFileEditUsed) {
return convertWriteToFileToolCalls(clineMessages)
}
// Convert apply_patch → write_to_file/replace_in_file
if (hasFileEditNative && hasApplyPatchUsed) {
return convertApplyPatchToolCalls(clineMessages)
}
return clineMessages
}
+3 -7
View File
@@ -1,6 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { ApiConfiguration, ModelInfo, QwenApiRegions } from "@shared/api"
import { Mode } from "@shared/storage/types"
import { ClineStorageMessage } from "@/shared/messages/content"
import { ClineTool } from "@/shared/tools"
import { AIhubmixHandler } from "./providers/aihubmix"
import { AnthropicHandler } from "./providers/anthropic"
@@ -47,8 +47,9 @@ import { ApiStream, ApiStreamUsageChunk } from "./transform/stream"
export type CommonApiHandlerOptions = {
onRetryAttempt?: ApiConfiguration["onRetryAttempt"]
}
export interface ApiHandler {
createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: ClineTool[], useResponseApi?: boolean): ApiStream
createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], tools?: ClineTool[]): ApiStream
getModel(): ApiHandlerModel
getApiStreamUsage?(): Promise<ApiStreamUsageChunk | undefined>
}
@@ -61,7 +62,6 @@ export interface ApiHandlerModel {
export interface ApiProviderInfo {
providerId: string
model: ApiHandlerModel
mode: Mode
customPrompt?: string // "compact"
autoCondenseThreshold?: number // 0-1 range
}
@@ -95,7 +95,6 @@ function createHandlerForProvider(
reasoningEffort: mode === "plan" ? options.planModeReasoningEffort : options.actModeReasoningEffort,
thinkingBudgetTokens:
mode === "plan" ? options.planModeThinkingBudgetTokens : options.actModeThinkingBudgetTokens,
geminiThinkingLevel: mode === "plan" ? options.geminiPlanModeThinkingLevel : options.geminiActModeThinkingLevel,
})
case "bedrock":
return new AwsBedrockHandler({
@@ -130,7 +129,6 @@ function createHandlerForProvider(
mode === "plan" ? options.planModeThinkingBudgetTokens : options.actModeThinkingBudgetTokens,
geminiApiKey: options.geminiApiKey,
geminiBaseUrl: options.geminiBaseUrl,
thinkingLevel: mode === "plan" ? options.geminiPlanModeThinkingLevel : options.geminiActModeThinkingLevel,
ulid: options.ulid,
})
case "openai":
@@ -169,7 +167,6 @@ function createHandlerForProvider(
geminiBaseUrl: options.geminiBaseUrl,
thinkingBudgetTokens:
mode === "plan" ? options.planModeThinkingBudgetTokens : options.actModeThinkingBudgetTokens,
thinkingLevel: mode === "plan" ? options.geminiPlanModeThinkingLevel : options.geminiActModeThinkingLevel,
apiModelId: mode === "plan" ? options.planModeApiModelId : options.actModeApiModelId,
ulid: options.ulid,
})
@@ -254,7 +251,6 @@ function createHandlerForProvider(
openRouterProviderSorting: options.openRouterProviderSorting,
openRouterModelId: mode === "plan" ? options.planModeOpenRouterModelId : options.actModeOpenRouterModelId,
openRouterModelInfo: mode === "plan" ? options.planModeOpenRouterModelInfo : options.actModeOpenRouterModelInfo,
geminiThinkingLevel: mode === "plan" ? options.geminiPlanModeThinkingLevel : options.geminiActModeThinkingLevel,
})
case "litellm":
return new LiteLlmHandler({
@@ -1,8 +1,8 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { afterEach, beforeEach, describe, it } from "mocha"
import sinon from "sinon"
import "should"
import { ClaudeCodeHandler } from "@core/api/providers/claude-code"
import { ClineStorageMessage } from "@/shared/messages/content"
describe("ClaudeCodeHandler", () => {
let handler: ClaudeCodeHandler
@@ -71,7 +71,7 @@ describe("ClaudeCodeHandler", () => {
runClaudeCodeStub.returns(mockGenerator() as any)
const systemPrompt = "You are a helpful assistant."
const messages: ClineStorageMessage[] = [{ role: "user", content: "Hello" }]
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const usageData: any[] = []
@@ -140,7 +140,7 @@ describe("ClaudeCodeHandler", () => {
runClaudeCodeStub.returns(mockGenerator() as any)
const systemPrompt = "You are a helpful assistant."
const messages: ClineStorageMessage[] = [{ role: "user", content: "Hello" }]
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const usageData: any[] = []
@@ -199,7 +199,7 @@ describe("ClaudeCodeHandler", () => {
runClaudeCodeStub.returns(mockGenerator() as any)
const systemPrompt = "You are a helpful assistant."
const messages: ClineStorageMessage[] = [{ role: "user", content: "Hello" }]
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const usageData: any[] = []
@@ -1,8 +1,8 @@
import Anthropic from "@anthropic-ai/sdk"
import { LiteLlmHandler, type LiteLlmModelInfoResponse } from "@core/api/providers/litellm"
import { convertToOpenAiMessages } from "@core/api/transform/openai-format"
import { expect } from "chai"
import sinon from "sinon"
import { ClineStorageMessage } from "@/shared/messages/content"
import { mockFetchForTesting } from "@/shared/net"
const fakeClient = {
@@ -109,7 +109,7 @@ describe("LiteLlmHandler", () => {
it("sends the system prompt and messages with the openai format", async () => {
const systemPrompt = "Test System Prompt"
const messages: ClineStorageMessage[] = [
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "first message",
@@ -161,7 +161,7 @@ describe("LiteLlmHandler", () => {
it("inserts the cache control in the system prompt and the last two user messages", async () => {
const systemPrompt = "Test System Prompt"
const messages: ClineStorageMessage[] = [
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: "first message",
@@ -1,9 +1,9 @@
import { afterEach, before, beforeEach, describe, it } from "mocha"
import "should"
import { Anthropic } from "@anthropic-ai/sdk"
import { ApiHandlerOptions } from "@shared/api"
import axios from "axios"
import sinon from "sinon"
import { ClineStorageMessage } from "@/shared/messages/content"
import { OllamaHandler } from "../ollama"
describe("OllamaHandler", () => {
@@ -59,7 +59,7 @@ describe("OllamaHandler", () => {
} as any)
const systemPrompt = "You are a helpful assistant."
const messages: ClineStorageMessage[] = [{ role: "user", content: "Hello" }]
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const result = []
const usageInfo = []
@@ -114,7 +114,7 @@ describe("OllamaHandler", () => {
}
const systemPrompt = "You are a helpful assistant."
const messages: ClineStorageMessage[] = [{ role: "user", content: "Hello" }]
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
// Start the request and catch the error
let errorMessage = ""
@@ -158,7 +158,7 @@ describe("OllamaHandler", () => {
} as any)
const systemPrompt = "You are a helpful assistant."
const messages: ClineStorageMessage[] = [{ role: "user", content: "Hello" }]
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const result = []
@@ -204,7 +204,7 @@ describe("OllamaHandler", () => {
}
const systemPrompt = "You are a helpful assistant."
const messages: ClineStorageMessage[] = [{ role: "user", content: "Hello" }]
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
const result = []
+20 -15
View File
@@ -2,8 +2,8 @@ import { Anthropic } from "@anthropic-ai/sdk"
import { Tool as AnthropicTool } from "@anthropic-ai/sdk/resources/index"
import { Stream as AnthropicStream } from "@anthropic-ai/sdk/streaming"
import { AnthropicModelId, anthropicDefaultModelId, anthropicModels, CLAUDE_SONNET_1M_SUFFIX, ModelInfo } from "@shared/api"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ClineTool } from "@/shared/tools"
import { ApiHandler, CommonApiHandlerOptions } from "../index"
import { withRetry } from "../retry"
import { sanitizeAnthropicMessages } from "../transform/anthropic-format"
@@ -43,7 +43,7 @@ export class AnthropicHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: AnthropicTool[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], tools?: ClineTool[]): ApiStream {
const client = this.ensureClient()
const model = this.getModel()
@@ -63,12 +63,6 @@ export class AnthropicHandler implements ApiHandler {
switch (modelId) {
// 'latest' alias does not support cache_control
case "claude-haiku-4-5@20251001":
case "claude-sonnet-4-5@20250929":
case "claude-sonnet-4@20250514":
case "claude-opus-4-5@20251101":
case "claude-opus-4-1@20250805":
case "claude-opus-4@20250514":
case "claude-haiku-4-5-20251001":
case "claude-sonnet-4-5-20250929:1m":
case "claude-sonnet-4-5-20250929":
@@ -76,12 +70,23 @@ export class AnthropicHandler implements ApiHandler {
case "claude-3-7-sonnet-20250219":
case "claude-3-5-sonnet-20241022":
case "claude-3-5-haiku-20241022":
case "claude-opus-4-5-20251101":
case "claude-opus-4-20250514":
case "claude-opus-4-1-20250805":
case "claude-3-opus-20240229":
case "claude-3-haiku-20240307": {
const anthropicMessages = sanitizeAnthropicMessages(messages, true)
/*
The latest message will be the new user message, one before will be the assistant message from a previous request, and the user message before that will be a previously cached user message. So we need to mark the latest user message as ephemeral to cache it for the next request, and mark the second to last user message as ephemeral to let the server know the last message to retrieve from the cache for the current request..
*/
const userMsgIndices = messages.reduce((acc, msg, index) => {
if (msg.role === "user") {
acc.push(index)
}
return acc
}, [] as number[])
const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1
const anthropicMessages = sanitizeAnthropicMessages(messages, lastUserMsgIndex, secondLastMsgUserIndex)
stream = await client.messages.create(
{
@@ -101,7 +106,7 @@ export class AnthropicHandler implements ApiHandler {
messages: anthropicMessages,
// tools, // cache breakpoints go from tools > system > messages, and since tools dont change, we can just set the breakpoint at the end of system (this avoids having to set a breakpoint at the end of tools which by itself does not meet min requirements for haiku caching)
stream: true,
tools: nativeToolsOn ? tools : undefined,
tools: nativeToolsOn ? (tools as AnthropicTool[]) : undefined,
// tool_choice options:
// - none: disables tool use, even if tools are provided. Claude will not call any tools.
// - auto: allows Claude to decide whether to call any provided tools or not. This is the default value when tools are provided.
@@ -130,9 +135,9 @@ export class AnthropicHandler implements ApiHandler {
max_tokens: model.info.maxTokens || 8192,
temperature: 0,
system: [{ text: systemPrompt, type: "text" }],
messages: sanitizeAnthropicMessages(messages, false),
tools: nativeToolsOn ? tools : undefined,
tool_choice: { type: "auto" },
messages: sanitizeAnthropicMessages(messages),
// tools,
// tool_choice: { type: "auto" },
stream: true,
})
break
@@ -211,7 +216,7 @@ export class AnthropicHandler implements ApiHandler {
case "content_block_delta":
switch (chunk.delta.type) {
case "thinking_delta":
// 'reasoning' type just displays in the UI, but ant_thinking will be used to send the thinking traces back to the API
// 'reasoning' type just displays in the UI, but reasoning with signature will be used to send the thinking traces back to the API
yield {
type: "reasoning",
reasoning: chunk.delta.thinking,
+4 -83
View File
@@ -1,5 +1,5 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { AskSageModelId, askSageDefaultModelId, askSageDefaultURL, askSageModels, ModelInfo } from "@shared/api"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from ".."
import { withRetry } from "../retry"
@@ -19,16 +19,6 @@ type AskSageRequest = {
}[]
model: string
dataset: "none"
usage: boolean
}
type AskSageUsage = {
model_tokens: {
completion_tokens: number
prompt_tokens: number
total_tokens: number
}
asksage_tokens: number
}
type AskSageResponse = {
@@ -38,18 +28,6 @@ type AskSageResponse = {
response: string
// Generated response message
message: string
// whether embedding & vector systems are down
embedding_down: boolean
vectors_down: boolean
// references if dataset is not none
references: string
type: string
added_obj: any
tool_calls: any
// usage metrics
usage: AskSageUsage | null
tool_responses: any[]
tool_calls_unified: any[]
}
export class AskSageHandler implements ApiHandler {
@@ -69,9 +47,10 @@ export class AskSageHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
try {
const model = this.getModel()
// Transform messages into AskSageRequest format
const formattedMessages = messages.map((msg) => {
const content = Array.isArray(msg.content)
@@ -89,7 +68,6 @@ export class AskSageHandler implements ApiHandler {
message: formattedMessages,
model: model.id,
dataset: "none",
usage: true,
}
// Make request to AskSage API
@@ -113,72 +91,15 @@ export class AskSageHandler implements ApiHandler {
throw new Error("No content in AskSage response")
}
// Yield tool responses if they exist
if (result.tool_responses && result.tool_responses.length > 0) {
for (const toolResponse of result.tool_responses) {
yield {
type: "text",
text: `[Tool Response: ${JSON.stringify(toolResponse)}]\n`,
}
}
}
// Yield the main response text
// Return entire response as a single chunk since streaming is not supported
yield {
type: "text",
text: result.message,
}
// Yield usage information if available
if (result.usage) {
yield {
type: "usage",
inputTokens: result.usage.model_tokens.prompt_tokens,
outputTokens: result.usage.model_tokens.completion_tokens,
cacheReadTokens: 0,
cacheWriteTokens: 0,
totalCost: result.usage.asksage_tokens, // Cost = Consumed AskSage tokens
}
}
} catch (error) {
if (error instanceof Error) {
throw new Error(`AskSage request failed: ${error.message}`)
}
throw error
}
}
async getApiStreamUsage() {
if (!this.apiKey) {
return undefined
}
try {
const response = await fetch(`${this.apiUrl}/count-monthly-tokens`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-access-tokens": this.apiKey,
},
body: JSON.stringify({ app_name: "asksage" }),
})
if (!response.ok) {
console.error("Failed to fetch AskSage usage", await response.text())
return undefined
}
const data = await response.json()
const usedTokens = data.response as number
return {
type: "usage" as const,
inputTokens: usedTokens,
outputTokens: 0,
}
} catch (error) {
console.error("Error fetching AskSage usage:", error)
return undefined
}
}
+7 -15
View File
@@ -1,14 +1,12 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { BasetenModelId, basetenDefaultModelId, basetenModels, ModelInfo } from "@shared/api"
import { calculateApiCostOpenAI } from "@utils/cost"
import OpenAI from "openai"
import type { ChatCompletionTool as OpenAITool } from "openai/resources/chat/completions"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { withRetry } from "../retry"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
import { ToolCallProcessor } from "../transform/tool-call-processor"
interface BasetenHandlerOptions extends CommonApiHandlerOptions {
basetenApiKey?: string
@@ -100,11 +98,10 @@ export class BasetenHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: OpenAITool[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const client = this.ensureClient()
const model = this.getModel()
const maxTokens = this.getOptimalMaxTokens(model)
const toolCallProcessor = new ToolCallProcessor()
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
@@ -118,22 +115,21 @@ export class BasetenHandler implements ApiHandler {
stream: true,
stream_options: { include_usage: true },
temperature: 0,
tools,
tool_choice: tools && tools.length > 0 ? "auto" : undefined,
})
let didOutputUsage = false
for await (const chunk of stream) {
const delta = chunk?.choices?.[0]?.delta
const delta = chunk.choices[0]?.delta
// Handle reasoning field if present (for reasoning models with parsed output)
if (delta && "reasoning" in delta && delta?.reasoning) {
const reasoning = typeof delta.reasoning === "string" ? delta.reasoning : JSON.stringify(delta.reasoning)
if ((delta as any)?.reasoning) {
const reasoningContent = (delta as any).reasoning as string
yield {
type: "reasoning",
reasoning,
reasoning: reasoningContent,
}
continue
}
// Handle content field
@@ -144,10 +140,6 @@ export class BasetenHandler implements ApiHandler {
}
}
if (delta?.tool_calls) {
yield* toolCallProcessor.processToolCallDeltas(delta.tool_calls)
}
// Handle usage information - only output once
if (!didOutputUsage && chunk.usage) {
yield* this.yieldUsage(model.info, chunk.usage)
+17 -30
View File
@@ -1,3 +1,4 @@
import { Anthropic } from "@anthropic-ai/sdk"
// Import proper AWS SDK types
import type { ContentBlock, Message } from "@aws-sdk/client-bedrock-runtime"
import {
@@ -11,7 +12,6 @@ import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
import { BedrockModelId, bedrockDefaultModelId, bedrockModels, CLAUDE_SONNET_1M_SUFFIX, ModelInfo } from "@shared/api"
import { calculateApiCostOpenAI, calculateApiCostQwen } from "@utils/cost"
import { ExtensionRegistryInfo } from "@/registry"
import { ClineStorageMessage } from "@/shared/messages/content"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { withRetry } from "../retry"
import { convertToR1Format } from "../transform/r1-format"
@@ -104,7 +104,6 @@ interface CachePointContentBlock {
// Define provider options type based on AWS SDK patterns
interface ProviderChainOptions {
clientConfig?: { userAgentAppId?: string }
ignoreCache?: boolean
profile?: string
}
@@ -122,7 +121,7 @@ export class AwsBedrockHandler implements ApiHandler {
}
@withRetry({ maxRetries: 4 })
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
// cross region inference requires prefixing the model id with the region
const rawModelId = await this.getModelId()
@@ -212,12 +211,7 @@ export class AwsBedrockHandler implements ApiHandler {
sessionToken?: string
}> {
// Configure provider options
const providerOptions: ProviderChainOptions = {
clientConfig: {
// set the inner sts client userAgentAppId
userAgentAppId: `cline#${ExtensionRegistryInfo.version}`,
},
}
const providerOptions: ProviderChainOptions = {}
const useProfile =
(this.options.awsAuthentication === undefined && this.options.awsUseProfile) ||
this.options.awsAuthentication === "profile"
@@ -348,7 +342,7 @@ export class AwsBedrockHandler implements ApiHandler {
*/
private async *createDeepseekMessage(
systemPrompt: string,
messages: ClineStorageMessage[],
messages: Anthropic.Messages.MessageParam[],
modelId: string,
model: { id: string; info: ModelInfo },
): ApiStream {
@@ -486,7 +480,7 @@ export class AwsBedrockHandler implements ApiHandler {
* First uses convertToR1Format to merge consecutive messages with the same role,
* then converts to the string format that DeepSeek R1 expects
*/
private formatDeepseekR1Prompt(systemPrompt: string, messages: ClineStorageMessage[]): string {
private formatDeepseekR1Prompt(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): string {
// First use convertToR1Format to merge consecutive messages with the same role
const r1Messages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages])
@@ -519,7 +513,7 @@ export class AwsBedrockHandler implements ApiHandler {
* Estimates token count based on text length (approximate)
* Note: This is a rough estimation, as the actual token count depends on the tokenizer
*/
private estimateInputTokens(systemPrompt: string, messages: ClineStorageMessage[]): number {
private estimateInputTokens(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): number {
// For Deepseek R1, we estimate the token count of the formatted prompt
// The formatted prompt includes special tokens and consistent formatting
const formattedPrompt = this.formatDeepseekR1Prompt(systemPrompt, messages)
@@ -686,7 +680,11 @@ export class AwsBedrockHandler implements ApiHandler {
}
}
} catch (error) {
throw error
console.error("Error processing Converse API response:", error)
yield {
type: "text",
text: `[ERROR] Failed to process response: ${error instanceof Error ? error.message : String(error)}`,
}
}
}
@@ -705,20 +703,9 @@ export class AwsBedrockHandler implements ApiHandler {
text: `[ERROR] Model stream error: ${chunk.modelStreamErrorException.message}`,
}
} else if (chunk.validationException) {
// Check if this is a context window error - if so, throw it
// so the retry mechanism can handle truncation
const message = chunk.validationException.message || ""
const isContextError = /input.*too long|context.*exceed|maximum.*token|input length.*max.*tokens/i.test(message)
if (isContextError) {
// Throw as exception so context management can handle it
throw chunk.validationException
}
// Otherwise yield as error text
yield {
type: "text",
text: `[ERROR] Validation error: ${message}`,
text: `[ERROR] Validation error: ${chunk.validationException.message}`,
}
} else if (chunk.throttlingException) {
yield {
@@ -792,7 +779,7 @@ export class AwsBedrockHandler implements ApiHandler {
*/
private async *createAnthropicMessage(
systemPrompt: string,
messages: ClineStorageMessage[],
messages: Anthropic.Messages.MessageParam[],
modelId: string,
model: { id: string; info: ModelInfo },
enable1mContextWindow: boolean,
@@ -848,7 +835,7 @@ export class AwsBedrockHandler implements ApiHandler {
* Formats messages for models using the Converse API specification
* Used by both Anthropic and Nova models to avoid code duplication
*/
private formatMessagesForConverseAPI(messages: ClineStorageMessage[]): Message[] {
private formatMessagesForConverseAPI(messages: Anthropic.Messages.MessageParam[]): Message[] {
return messages.map((message) => {
// Determine role (user or assistant)
const role = message.role === "user" ? ConversationRole.USER : ConversationRole.ASSISTANT
@@ -981,7 +968,7 @@ export class AwsBedrockHandler implements ApiHandler {
*/
private async *createNovaMessage(
systemPrompt: string,
messages: ClineStorageMessage[],
messages: Anthropic.Messages.MessageParam[],
modelId: string,
model: { id: string; info: ModelInfo },
): ApiStream {
@@ -1021,7 +1008,7 @@ export class AwsBedrockHandler implements ApiHandler {
*/
private async *createOpenAIMessage(
systemPrompt: string,
messages: ClineStorageMessage[],
messages: Anthropic.Messages.MessageParam[],
modelId: string,
model: { id: string; info: ModelInfo },
): ApiStream {
@@ -1156,7 +1143,7 @@ export class AwsBedrockHandler implements ApiHandler {
*/
private async *createQwenMessage(
systemPrompt: string,
messages: ClineStorageMessage[],
messages: Anthropic.Messages.MessageParam[],
modelId: string,
model: { id: string; info: ModelInfo },
): ApiStream {
+2 -5
View File
@@ -1,6 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import Cerebras from "@cerebras/cerebras_cloud_sdk"
import { CerebrasModelId, cerebrasDefaultModelId, cerebrasModels, ModelInfo } from "@shared/api"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from "../index"
import { withRetry } from "../retry"
@@ -33,9 +33,6 @@ export class CerebrasHandler implements ApiHandler {
apiKey: cleanApiKey,
timeout: 30000, // 30 second timeout
fetch, // Use configured fetch with proxy support
defaultHeaders: {
"X-Cerebras-3rd-Party-Integration": "cline",
},
})
} catch (error) {
throw new Error(`Error creating Cerebras client: ${error.message}`)
@@ -49,7 +46,7 @@ export class CerebrasHandler implements ApiHandler {
baseDelay: 5000, // Start with 5 second delay
maxDelay: 60000, // Allow up to 60 second delays to respect rate limits
})
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const client = this.ensureClient()
// Convert Anthropic messages to Cerebras format
+2 -2
View File
@@ -1,7 +1,7 @@
import type { Anthropic } from "@anthropic-ai/sdk"
import { filterMessagesForClaudeCode } from "@/integrations/claude-code/message-filter"
import { runClaudeCode } from "@/integrations/claude-code/run"
import { ClaudeCodeModelId, claudeCodeDefaultModelId, claudeCodeModels } from "@/shared/api"
import { ClineStorageMessage } from "@/shared/messages/content"
import { type ApiHandler, CommonApiHandlerOptions } from ".."
import { withRetry } from "../retry"
import { type ApiStream, ApiStreamUsageChunk } from "../transform/stream"
@@ -24,7 +24,7 @@ export class ClaudeCodeHandler implements ApiHandler {
baseDelay: 2000,
maxDelay: 15000,
})
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
// Filter out image blocks since Claude Code doesn't support them
const filteredMessages = filterMessagesForClaudeCode(messages)
+4 -9
View File
@@ -1,3 +1,4 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from "@shared/api"
import { shouldSkipReasoningForModel } from "@utils/model-utils"
import axios from "axios"
@@ -7,9 +8,7 @@ import { ClineEnv } from "@/config"
import { ClineAccountService } from "@/services/account/ClineAccountService"
import { AuthService } from "@/services/auth/AuthService"
import { buildClineExtraHeaders } from "@/services/EnvUtils"
import { Logger } from "@/services/logging/Logger"
import { CLINE_ACCOUNT_AUTH_ERROR_MESSAGE } from "@/shared/ClineAccount"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch, getAxiosSettings } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { withRetry } from "../retry"
@@ -27,7 +26,6 @@ interface ClineHandlerOptions extends CommonApiHandlerOptions {
openRouterModelId?: string
openRouterModelInfo?: ModelInfo
clineAccountId?: string
geminiThinkingLevel?: string
}
export class ClineHandler implements ApiHandler {
@@ -98,7 +96,7 @@ export class ClineHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: OpenAITool[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], tools?: OpenAITool[]): ApiStream {
try {
const client = await this.ensureClient()
@@ -116,13 +114,11 @@ export class ClineHandler implements ApiHandler {
this.options.thinkingBudgetTokens,
this.options.openRouterProviderSorting,
tools,
this.options.geminiThinkingLevel,
)
const toolCallProcessor = new ToolCallProcessor()
for await (const chunk of stream) {
Logger.debug("ClineHandler chunk:" + JSON.stringify(chunk))
// openrouter returns an error object instead of the openai sdk throwing an error
if ("error" in chunk) {
const error = chunk.error as OpenRouterErrorResponse["error"]
@@ -153,7 +149,6 @@ export class ClineHandler implements ApiHandler {
}
const delta = choice?.delta
if (delta?.content) {
yield {
type: "text",
@@ -185,7 +180,7 @@ export class ClineHandler implements ApiHandler {
"reasoning_details" in delta &&
delta.reasoning_details &&
// @ts-ignore-next-line
delta?.reasoning_details?.length && // exists and non-0
delta.reasoning_details.length && // exists and non-0
!shouldSkipReasoningForModel(this.options.openRouterModelId)
) {
yield {
@@ -199,7 +194,7 @@ export class ClineHandler implements ApiHandler {
// @ts-ignore-next-line
let totalCost = (chunk.usage.cost || 0) + (chunk.usage.cost_details?.upstream_inference_cost || 0)
if (["x-ai/grok-code-fast-1", "minimax/minimax-m2", "stealth/microwave"].includes(this.getModel().id)) {
if (this.getModel().id === "x-ai/grok-code-fast-1" || this.getModel().id === "minimax/minimax-m2") {
totalCost = 0
}
+2 -2
View File
@@ -1,8 +1,8 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { DeepSeekModelId, deepSeekDefaultModelId, deepSeekModels, ModelInfo } from "@shared/api"
import { calculateApiCostOpenAI } from "@utils/cost"
import OpenAI from "openai"
import type { ChatCompletionTool as OpenAITool } from "openai/resources/chat/completions"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { withRetry } from "../retry"
@@ -75,7 +75,7 @@ export class DeepSeekHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: OpenAITool[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], tools?: OpenAITool[]): ApiStream {
const client = this.ensureClient()
const model = this.getModel()
+3 -3
View File
@@ -1,4 +1,4 @@
import { ClineStorageMessage } from "@/shared/messages/content"
import { Anthropic } from "@anthropic-ai/sdk"
import { fetch } from "@/shared/net"
import { ModelInfo } from "../../../shared/api"
import { ApiHandler } from "../index"
@@ -97,7 +97,7 @@ export class DifyHandler implements ApiHandler {
}
}
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
console.log("[DIFY DEBUG] createMessage called with:", {
systemPromptLength: systemPrompt?.length || 0,
messagesCount: messages?.length || 0,
@@ -384,7 +384,7 @@ export class DifyHandler implements ApiHandler {
}
}
private convertMessagesToQuery(systemPrompt: string, messages: ClineStorageMessage[]): string {
private convertMessagesToQuery(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): string {
// Dify's context is managed by `conversation_id`. The `query` should be the last user message.
// The system prompt is typically configured in the Dify App itself.
const lastUserMessage = messages.filter((m) => m.role === "user").pop()
+2 -2
View File
@@ -1,6 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { DoubaoModelId, doubaoDefaultModelId, doubaoModels, ModelInfo } from "@shared/api"
import OpenAI from "openai"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from ".."
import { withRetry } from "../retry"
@@ -50,7 +50,7 @@ export class DoubaoHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const client = this.ensureClient()
const model = this.getModel()
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
+2 -2
View File
@@ -1,6 +1,6 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { FireworksModelId, fireworksDefaultModelId, fireworksModels, ModelInfo } from "@shared/api"
import OpenAI from "openai"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from ".."
import { withRetry } from "../retry"
@@ -41,7 +41,7 @@ export class FireworksHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const client = this.ensureClient()
const modelId = this.options.fireworksModelId ?? ""
+47 -56
View File
@@ -1,3 +1,4 @@
import type { Anthropic } from "@anthropic-ai/sdk"
// Restore GenerateContentConfig import and add GenerateContentResponseUsageMetadata
import {
ApiError,
@@ -6,11 +7,10 @@ import {
type GenerateContentResponseUsageMetadata,
GoogleGenAI,
FunctionDeclaration as GoogleTool,
ThinkingLevel,
Part,
} from "@google/genai"
import { GeminiModelId, geminiDefaultModelId, geminiModels, ModelInfo } from "@shared/api"
import { telemetryService } from "@/services/telemetry"
import { ClineStorageMessage } from "@/shared/messages/content"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { RetriableError, withRetry } from "../retry"
import { convertAnthropicMessageToGemini } from "../transform/gemini-format"
@@ -28,7 +28,6 @@ interface GeminiHandlerOptions extends CommonApiHandlerOptions {
geminiApiKey?: string
geminiBaseUrl?: string
thinkingBudgetTokens?: number
thinkingLevel?: string
apiModelId?: string
ulid?: string
}
@@ -111,51 +110,34 @@ export class GeminiHandler implements ApiHandler {
baseDelay: 2000,
maxDelay: 15000,
})
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: GoogleTool[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], tools?: GoogleTool[]): ApiStream {
const client = this.ensureClient()
const { id: modelId, info } = this.getModel()
const contents = messages.map(convertAnthropicMessageToGemini)
// Configure thinking budget if supported
const _thinkingBudget = this.options.thinkingBudgetTokens ?? 0
const maxBudget = info.thinkingConfig?.maxBudget ?? 24576
const thinkingBudget = Math.min(_thinkingBudget, maxBudget)
// When ThinkingLevel is defineded, thinking budget cannot be zero
// and only level is used to control thinking behavior.
let thinkingLevel: ThinkingLevel | undefined
if (this.options.thinkingLevel === "high") {
thinkingLevel = ThinkingLevel.HIGH
} else if (this.options.thinkingLevel === "low" || modelId.includes("gemini-3-pro")) {
// Thinking level is required for Gemini 3 Pro models.
// Set it to LOW by default if not specified but is required.
thinkingLevel = ThinkingLevel.LOW
}
const thinkingBudget = this.options.thinkingBudgetTokens ?? 0
const _maxBudget = info.thinkingConfig?.maxBudget ?? 0
// Set up base generation config
const requestConfig: GenerateContentConfig = {
// Add base URL if configured
httpOptions: this.options.geminiBaseUrl ? { baseUrl: this.options.geminiBaseUrl } : undefined,
systemInstruction: systemPrompt,
...{ systemInstruction: systemPrompt },
// Set temperature (default to 0)
// Gemini 3.0 recommends 1.0
temperature: info.temperature ?? 1,
temperature: 0,
}
// Add thinking config if the model supports it
requestConfig.thinkingConfig = {
// Turn off thinking:
// thinkingBudget: 0
// Turn on dynamic thinking:
// thinkingBudget: -1
// Turn on fixed thinking budget:
thinkingBudget: thinkingLevel ? undefined : thinkingBudget, // Use budget only if thinkingLevel is not set
thinkingLevel,
includeThoughts: thinkingBudget > 0 || !!thinkingLevel,
if (thinkingBudget > 0) {
requestConfig.thinkingConfig = {
thinkingBudget: thinkingBudget,
includeThoughts: true,
}
}
// Generate content using the configured parameters
const sdkCallStartTime = Date.now()
let responseId: string | undefined
let sdkFirstChunkTime: number | undefined
let ttftSdkMs: number | undefined
let apiSuccess = false
@@ -166,8 +148,7 @@ export class GeminiHandler implements ApiHandler {
let thoughtsTokenCount = 0 // Initialize thought token counts
let lastUsageMetadata: GenerateContentResponseUsageMetadata | undefined
const isNativeToolCallsEnabled = tools?.length
if (isNativeToolCallsEnabled) {
if (tools?.length) {
requestConfig.tools = [{ functionDeclarations: tools }]
requestConfig.toolConfig = {
// Force the model to call 'any' function.
@@ -195,45 +176,56 @@ export class GeminiHandler implements ApiHandler {
}
// Handle thinking content from Gemini's response
const parts = chunk?.candidates?.[0]?.content?.parts || []
for (const part of parts) {
if (part.thought && part.text) {
yield {
type: "reasoning",
id: chunk.responseId,
reasoning: part.text || "",
signature: part.thoughtSignature,
}
} else if (part.text) {
yield {
type: "text",
text: part.text,
id: chunk.responseId,
signature: part.thoughtSignature,
const candidateForThoughts = chunk?.candidates?.[0]
const partsForThoughts = candidateForThoughts?.content?.parts
let thoughts = "" // Initialize as empty string
if (partsForThoughts) {
// This ensures partsForThoughts is a Part[] array
for (const part of partsForThoughts) {
const { thought, text } = part as Part
if (thought && text) {
// Ensure part.text exists
// Handle the thought part
thoughts += text + "\n" // Append thought and a newline
}
}
if (part.functionCall) {
const functionCall = part.functionCall
const args = Object.entries(functionCall.args || {}).filter(([_key, val]) => !!val)
if (functionCall.args && args.length > 0) {
}
if (thoughts.trim() !== "") {
yield {
type: "reasoning",
reasoning: thoughts.trim(),
}
thoughts = "" // Reset thoughts after yielding
}
if (chunk.text) {
yield {
type: "text",
text: chunk.text,
}
}
if (tools && chunk.functionCalls && chunk.functionCalls?.length > 0) {
for (const functionCall of chunk.functionCalls) {
if (functionCall.args) {
console.log("[GeminiHandler] tool call received:", functionCall)
yield {
type: "tool_calls",
id: chunk.responseId,
tool_call: {
function: {
id: chunk.responseId,
id: functionCall.id || functionCall.name,
name: functionCall.name,
arguments: JSON.stringify(functionCall.args),
},
},
signature: part.thoughtSignature,
}
}
}
}
if (chunk.usageMetadata) {
responseId = chunk.responseId
lastUsageMetadata = chunk.usageMetadata
promptTokens = lastUsageMetadata.promptTokenCount ?? promptTokens
outputTokens = lastUsageMetadata.candidatesTokenCount ?? outputTokens
@@ -259,7 +251,6 @@ export class GeminiHandler implements ApiHandler {
cacheReadTokens,
cacheWriteTokens: 0,
totalCost,
id: responseId,
}
}
} catch (error) {
+2 -2
View File
@@ -1,8 +1,8 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { GroqModelId, groqDefaultModelId, groqModels, ModelInfo } from "@shared/api"
import { calculateApiCostOpenAI } from "@utils/cost"
import OpenAI from "openai"
import type { ChatCompletionTool as OpenAITool } from "openai/resources/chat/completions"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { withRetry } from "../retry"
@@ -192,7 +192,7 @@ export class GroqHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: OpenAITool[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], tools?: OpenAITool[]): ApiStream {
const client = this.ensureClient()
const model = this.getModel()
const modelFamily = this.detectModelFamily(model.id)
+2 -2
View File
@@ -1,7 +1,7 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { hicapModelInfoSaneDefaults, ModelInfo } from "@shared/api"
import OpenAI from "openai"
import type { ChatCompletionReasoningEffort } from "openai/resources/chat/completions"
import { ClineStorageMessage } from "@/shared/messages/content"
import { ApiHandler, CommonApiHandlerOptions } from "../index"
import { withRetry } from "../retry"
import { convertToOpenAiMessages } from "../transform/openai-format"
@@ -44,7 +44,7 @@ export class HicapHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const client = this.ensureClient()
const modelId = this.options.hicapModelId ?? ""
+2 -2
View File
@@ -1,7 +1,7 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { HuaweiCloudMaasModelId, huaweiCloudMaasDefaultModelId, huaweiCloudMaasModels, ModelInfo } from "@shared/api"
import OpenAI from "openai"
import type { ChatCompletionTool as OpenAITool } from "openai/resources/chat/completions"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from ".."
import { withRetry } from "../retry"
@@ -62,7 +62,7 @@ export class HuaweiCloudMaaSHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: OpenAITool[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], tools?: OpenAITool[]): ApiStream {
const client = this.ensureClient()
const model = this.getModel()
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
+2 -2
View File
@@ -1,8 +1,8 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { HuggingFaceModelId, huggingFaceDefaultModelId, huggingFaceModels, ModelInfo } from "@shared/api"
import { calculateApiCostOpenAI } from "@utils/cost"
import OpenAI from "openai"
import type { ChatCompletionTool as OpenAITool } from "openai/resources/chat/completions"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { withRetry } from "../retry"
@@ -69,7 +69,7 @@ export class HuggingFaceHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: OpenAITool[]): ApiStream {
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], tools?: OpenAITool[]): ApiStream {
try {
const client = this.ensureClient()
const model = this.getModel()

Some files were not shown because too many files have changed in this diff Show More