Compare commits

..

1 Commits

Author SHA1 Message Date
abeatrix 85a7b7dbaf test: add comprehensive test suite for applyFileReadContextHistoryUpdates
Add extensive test coverage for the applyFileReadContextHistoryUpdates method in ContextManager. Tests cover various scenarios including:
- Early return when fileReadIndices is empty
- Handling single file occurrences
- Updating duplicate file reads (keeping only last occurrence)
- FILE_MENTION type with multiple files
- Text block replacements in API messages
- Edge cases and error conditions

This ensures the file read deduplication logic works correctly across different message types and file configurations.
2025-12-04 13:10:17 -08:00
274 changed files with 4375 additions and 17554 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Fixed TLS/proxy issues for users behind corporate MITM inspection proxies by correcting the IS_STANDALONE environment variable check. The check now uses explicit string comparison (`=== "true"`) instead of truthy evaluation, which was incorrectly triggering standalone mode in VSCode builds because the string `"false"` is truthy in JavaScript.
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Add DeepSeek 3.2 to native tool calling allow list
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Prevent simultaneuos refreshes when restoring auth info
-194
View File
@@ -1,194 +0,0 @@
# Hotfix Release
Create a hotfix release by cherry-picking specific commits from main onto the latest release tag.
## Overview
This workflow helps you:
1. Select specific commits from main to include in a hotfix
2. Create a release notes commit on main (changelog + version bump)
3. Cherry-pick everything onto the latest release tag
4. Tag and push the new release
## Step 1: Setup and Gather Information
First, ensure we're on main and up to date:
```bash
git checkout main && git pull origin main
```
Get the latest release tag:
```bash
git tag --sort=-v:refname | head -1
```
## Step 2: Present Commits Since Last Release
Show all commits on main since the last release tag:
```bash
LAST_TAG=$(git tag --sort=-v:refname | head -1)
git log ${LAST_TAG}..HEAD --oneline --format="%h %s (%an)"
```
Also get the commit messages already on the tag (to identify previously cherry-picked commits). Note: Run these as separate commands to avoid shell parsing issues with parentheses in author names:
```bash
LAST_TAG=$(git tag --sort=-v:refname | head -1)
PREV_TAG=$(git tag --sort=-v:refname | head -2 | tail -1)
```
```bash
git log $PREV_TAG..$LAST_TAG --oneline --format="%s"
```
**Present the list** to the user in a numbered format with commit hash, subject, and author. For any commits whose subject line already appears in the tag's history (previously cherry-picked in an earlier hotfix) or are "Release Notes" commits, add `(already in previous hotfix)` or `(release notes - skip)` after them so the user knows to skip those.
Ask which commits to include in the hotfix.
Use the ask_followup_question tool to let the user specify which commits they want (by number or hash).
## Step 3: Analyze Selected Commits
For each selected commit:
1. Get the full commit message: `git show --no-patch --format="%B" <hash>`
2. Get the diff to understand the change: `git show <hash> --stat`
3. Find the associated PR if any: `gh pr list --search "<hash>" --state merged --json number,title --jq '.[0]'`
Build a mental model of what these changes do for the changelog.
## Step 4: Determine New Version Number
Parse the current version from package.json and the last tag:
```bash
LAST_TAG=$(git tag --sort=-v:refname | head -1)
echo "Last release: $LAST_TAG"
cat package.json | grep '"version"'
```
Hotfixes always increment the patch version (e.g., 3.40.0 -> 3.40.1, or 3.40.1 -> 3.40.2).
**Ask the user to confirm the new version number.**
## Step 5: Create Release Notes Commit on Main
On the main branch, create a commit that updates:
1. **CHANGELOG.md** - Add a new section for the hotfix version at the top:
```markdown
## [3.40.1]
- Description of fix 1
- Description of fix 2
```
Write clear, user-friendly descriptions based on your analysis of the commits.
2. **package.json** - Update the version field to the new version
3. **Delete changesets** for the commits being included in the hotfix. This prevents the changeset bot from including duplicate entries in the next regular release.
Find and delete the changeset files associated with the selected commits:
```bash
ls .changeset/
```
Each changeset file in `.changeset/` corresponds to a PR. Read them to identify which ones belong to the commits you're hotfixing, then delete those files.
**Skip running `npm run install:all`** - the automation handles outdated lockfiles.
Commit with message format: `v{VERSION} Release Notes (hotfix)`
In the commit body, mention:
- This is for a hotfix release
- List the cherry-picked commits that will be included
```bash
git add CHANGELOG.md package.json .changeset/
git commit -m "v3.40.1 Release Notes (hotfix)
Hotfix release including:
- <commit1-hash>: <description>
- <commit2-hash>: <description>
"
```
Push to main:
```bash
git push origin main
```
## Step 6: Build the Hotfix on the Tag
Checkout the last release tag (detached HEAD):
```bash
LAST_TAG=$(git tag --sort=-v:refname | head -1)
git checkout $LAST_TAG
```
Cherry-pick the selected commits in order:
```bash
git cherry-pick <commit1-hash>
git cherry-pick <commit2-hash>
# ... etc
```
Finally, cherry-pick the release notes commit you just pushed to main:
```bash
# Get the hash of the release notes commit (should be HEAD of main)
RELEASE_NOTES_COMMIT=$(git rev-parse main)
git cherry-pick $RELEASE_NOTES_COMMIT
```
## Step 7: Tag and Push
After all cherry-picks are applied successfully:
```bash
# Tag the new release
git tag v{VERSION}
# Push the tag to remote
git push origin v{VERSION}
```
## Step 8: Return to Main and Summary
Return to main branch:
```bash
git checkout main
```
**Copy a Slack announcement message to clipboard** with the version and PR links for each included fix:
```
VS Code Hotfix v{VERSION} Published
- Description of fix 1 https://github.com/cline/cline/pull/{PR_NUMBER}
- Description of fix 2 https://github.com/cline/cline/pull/{PR_NUMBER}
```
Present a final summary:
- New version: v{VERSION}
- Tag pushed: yes
- Commits included: (list them)
- Slack message copied to clipboard: yes
Remind the user to:
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/publish.yml (paste `v{VERSION}` as the tag)
2. Post the Slack message to announce the hotfix
## Important Notes
- This workflow does NOT create a release branch - only tags
- The release notes commit goes to main first, then gets cherry-picked to the tag
- This keeps main's history accurate while allowing hotfix releases from tags
- If cherry-pick conflicts occur, resolve them before continuing
-1
View File
@@ -1 +0,0 @@
../../.claude/commands/hotfix-release.md
+5 -21
View File
@@ -1,6 +1,6 @@
name: Trigger Jetbrains Plugin <-> Cline Tests
on:
pull_request_target:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
@@ -22,24 +22,7 @@ jobs:
owner: cline
repositories: intellij-plugin
- name: Sanitize untrusted inputs
id: sanitize
env:
RAW_BRANCH_NAME: ${{ github.head_ref }}
RAW_PR_TITLE: ${{ github.event.pull_request.title }}
run: |
# Sanitize branch name for JSON
BRANCH_NAME_JSON=$(jq -n --arg b "$RAW_BRANCH_NAME" '$b')
echo "branch_name=$BRANCH_NAME_JSON" >> $GITHUB_OUTPUT
# Sanitize PR title for JSON
PR_TITLE_JSON=$(jq -n --arg t "$RAW_PR_TITLE" '$t')
echo "pr_title=$PR_TITLE_JSON" >> $GITHUB_OUTPUT
- name: Trigger IntelliJ Plugin Integration Test
env:
BRANCH_NAME: ${{ steps.sanitize.outputs.branch_name }}
PR_TITLE: ${{ steps.sanitize.outputs.pr_title }}
run: |
curl -X POST \
-H "Authorization: Bearer ${{ steps.app-token.outputs.token }}" \
@@ -52,10 +35,10 @@ jobs:
"event_type": "cline-pr-check",
"client_payload": {
"pr_number": "${{ github.event.number }}",
"branch_name": $BRANCH_NAME,
"branch_name": "${{ github.head_ref }}",
"action": "${{ github.event.action }}",
"sha": "${{ github.event.pull_request.head.sha }}",
"pr_title": $PR_TITLE,
"pr_title": ${{ toJSON(github.event.pull_request.title) }},
"pr_url": "${{ github.event.pull_request.html_url }}"
}
}
@@ -64,6 +47,7 @@ jobs:
- name: Log trigger details
run: |
echo "Triggered IntelliJ Plugin integration test for:"
echo " PR #${{ github.event.number }}"
echo " PR #${{ github.event.number }}: ${{ github.event.pull_request.title }}"
echo " Branch: ${{ github.head_ref }}"
echo " Action: ${{ github.event.action }}"
echo " SHA: ${{ github.event.pull_request.head.sha }}"
+3
View File
@@ -40,6 +40,9 @@ buf.yaml
.changeset/
.clinerules/
# Include specific file needed for Background Exec mode
!standalone/runtime-files/vscode/enhanced-terminal.js
# Ignore all webview-ui files except the build directory (https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/frameworks/hello-world-react-cra/.vscodeignore)
webview-ui/src/**
webview-ui/public/**
-67
View File
@@ -1,72 +1,5 @@
# Changelog
## [3.43.0]
### Added
- GLM-4.6
- kat-coder-pro
- Add parsing of env variable patterns to the mcpconfig.json
### Fixed
- TLS Proxy support issues for VSCode
- Add supportsReasoning flag to OpenAI reasoning models
- Fix thinking not available for some models in the OpenAI provider
- Fix invalid signature field issues when switching between Gemini and Anthropic providers
- Extract OpenRouter model filtering into reusable utility and use it in different model pickers
- Fix a11y for auto approve checkbox
- Improve ModelPickerModal provider list layout
### Refactored
- Migrate WhatsNewModal to new shared dialogue component
## [3.42.0]
### Added
- Expose `getAvailableSlashCommands` rpc endpoint to UI clients
- Made slash command menu and context menu accessible and screenreader-friendly
- Made expanding/collapsing UI components accessible
### Fixed
- Devstral OpenRouter model ID and routing issues
- Incorrect pricing display for Devstral model in the extension
## [3.41.0]
### Added
- OpenAI GPT-5.2
- Devstral-2512 (formerly stealth model "Microwave")
- Improvements to chat modal model picker
- Amazon Nova 2 Lite
- DeepSeek 3.2 to native tool calling allow list
- Responses API support for Codex models in OpenAI provider (requires native tool calling)
- Xmas Special Santa Cline
- Welcome screen UI enhancements
### Fixed
- Initial checkpoint commit now non-blocking for improved responsiveness in large repositories
- Gemini Vertex models erroring when thinking parameters are not supported
- Restrictive file permissions for secrets.json
- Ollama streaming requests not aborting when task is cancelled
### Refactored
- OpenAI provider to centralize temperature configuration and include missing GPT-5 model settings
- OpenAI native handler to use metadata for model capabilities
- Vertex provider to use metadata for model capabilities
## [3.40.2]
- Fix logout on network errors during token refresh (e.g., opening laptop while offline)
## [3.40.1]
- Fix cost calculation display for Anthropic API requests
## [3.40.0]
- Fix highlighted text flashing when task header is collapsed
-1
View File
@@ -70,4 +70,3 @@ Apache-2.0 - see [LICENSE](https://github.com/cline/cline/blob/main/LICENSE) for
- Report issues: [GitHub Issues](https://github.com/cline/cline/issues)
- Community: [GitHub Discussions](https://github.com/cline/cline/discussions)
- Documentation: [docs.cline.bot](https://docs.cline.bot)
- Cline CLI Architecture: [architecture.md](./architecture.md)
-292
View File
@@ -1,292 +0,0 @@
# Cline CLI Architecture
The CLI is a **standalone terminal interface** for the Cline AI coding assistant, written in Go. It provides the same autonomous coding capabilities as the VS Code extension but runs entirely in the terminal.
## High-Level Architecture
```
┌─────────────────────────────────────────────────────────────────────────┐
│ User Terminal │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ cline (Go binary) │
│ cmd/cline/main.go │
│ • Cobra CLI commands (task, auth, config, instance, etc.) │
│ • Interactive input via Bubble Tea │
│ • Streaming output with markdown rendering │
└─────────────────────────────────────────────────────────────────────────┘
│ gRPC (50052) │ starts subprocess
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ cline-core │◄────────────────►│ cline-host │
│ (Node.js) │ gRPC (51052) │ (Go binary) │
│ │ │ cmd/cline-host/main.go│
│ • AI/LLM orchestration │ │ │
│ • Tool execution │ │ • Workspace paths │
│ • Task state mgmt │ │ • File diff editing │
│ • Message handling │ │ • Clipboard access │
└─────────────────────────┘ │ • Environment info │
│ └─────────────────────────┘
│ SQLite (self-registration)
┌─────────────────────────────────────────────────────────────────────────┐
│ ~/.cline/data/locks/locks.db │
│ (Instance registry - core self-registers on startup) │
└─────────────────────────────────────────────────────────────────────────┘
```
## Entry Points (`cmd/`)
### `cmd/cline/main.go` - Main CLI
Cobra-based CLI with commands:
- **Root**: `cline [prompt]` - Start a task directly
- **task**: Create, send, view, list, pause, restore tasks
- **auth**: Authentication setup and provider configuration
- **config**: Read/write settings
- **instance**: Manage running Cline instances
- **logs**: View and clean log files
- **doctor**: System health check
### `cmd/cline-host/main.go` - Host Bridge Service
Separate gRPC server providing host environment operations to cline-core:
- Workspace paths
- File diff editing
- Clipboard access
- Shutdown coordination
---
## `pkg/cli/` Subsystems
### 1. `auth/` - Authentication System
Handles authentication with Cline service and BYO (Bring Your Own) API providers.
| File | Purpose |
| ------------------------- | ------------------------------------------------------------------------ |
| `auth_cline_provider.go` | OAuth login flow - opens browser, subscribes to auth callback stream |
| `auth_menu.go` | Interactive menu showing auth options based on current state |
| `auth_subscription.go` | gRPC stream subscription for auth status updates |
| `wizard_byo.go` | Interactive wizard for configuring BYO providers |
| `wizard_byo_bedrock.go` | AWS Bedrock-specific credential setup |
| `wizard_byo_oca.go` | Oracle Code Assist setup |
| `providers_list.go` | Retrieves configured providers from core state |
| `providers_byo.go` | Provider selection UI and field configuration |
| `models_*.go` | Model listing (static lists + dynamic fetch from OpenRouter/OpenAI/Ollama) |
**Flow**: User runs `cline auth` → Menu shows options → For BYO: wizard guides through provider/key/model selection → Config saved via gRPC to core.
---
### 2. `clerror/` - Error Handling
Parses and classifies API errors from the Cline service.
**Error Types:**
- `ErrorTypeAuth` - 401, bad API key
- `ErrorTypeBalance` - Insufficient credits
- `ErrorTypeRateLimit` - 429, quota exceeded
- `ErrorTypeNetwork` - Connection issues
- `ErrorTypeUnknown` - Catch-all
Extracts billing details (balance, spent, buy credits URL) from error responses.
---
### 3. `config/` - Configuration Management
| File | Purpose |
| --------------------- | -------------------------------------------------------------------------- |
| `manager.go` | gRPC interface for reading/writing settings via `UpdateSettingsCli` RPC |
| `settings_renderer.go`| Pretty-prints config values, censors sensitive fields (keys, secrets) |
Supports dot-notation paths: `cline config get auto-approval-settings.actions.read-files`
---
### 4. `display/` - Terminal Display System
The most complex subsystem - handles all visual output.
| File | Purpose |
| ----------------------- | -------------------------------------------------------------------- |
| `renderer.go` | Central coordinator with lipgloss styles, color methods, markdown delegation |
| `streaming.go` | Real-time streaming display with deduplication |
| `segment_streamer.go` | Streaming segments (header + body) with context-aware headers |
| `typewriter.go` | Character-by-character animation with variable delays |
| `markdown_renderer.go` | Glamour wrapper for terminal markdown rendering |
| `tool_renderer.go` | Tool operation formatting ("Cline is editing `file.ts`") |
| `tool_result_parser.go` | Parses structured tool results (file lists, search results) |
| `banner.go` | Session startup banner with version/model/workspace |
| `deduplicator.go` | MD5-based deduplication with 2-second window |
| `system_renderer.go` | Rich error/warning boxes for balance errors, auth failures |
| `ansi.go` | TTY detection, line clearing with escape codes |
---
### 5. `global/` - Global State Management
| File | Purpose |
| ------------------ | -------------------------------------------------------------------------- |
| `global.go` | Global config (paths, verbosity, output format), initialization |
| `registry.go` | Instance discovery via SQLite, health checking, default instance management|
| `cline-clients.go` | Starts cline-core + cline-host processes, port allocation, cleanup |
**Instance lifecycle:**
1. Find available port pair
2. Start `cline-host` on port+1000
3. Start `cline-core` on port
4. Wait for core to self-register in SQLite
5. Set as default if first instance
---
### 6. `handlers/` - Message Handlers
Routes incoming messages from cline-core to appropriate renderers.
| File | Purpose |
| ------------------ | --------------------------------------------------------------------- |
| `handler.go` | Handler registry with priority-based routing |
| `ask_handlers.go` | Approval requests: tool, command, followup, api_req_failed, etc. |
| `say_handlers.go` | Status messages: text, reasoning, command_output, tool, checkpoint, etc. |
Uses `DisplayContext` providing renderer access, state, and context flags (isLast, isPartial, isStreamingMode).
---
### 7. `output/` - Output Coordination
| File | Purpose |
| --------------------- | ----------------------------------------------------------------------- |
| `coordinator.go` | Coordinates streaming output with interactive input (saves/restores input state) |
| `input_model.go` | Bubble Tea model for rich input (message, approval, feedback types) |
| `slash_completion.go` | Autocomplete dropdown for slash commands |
**Key pattern:** When output needs to print while input is visible, the coordinator saves input state, clears the form, prints, then restores input.
---
### 8. `slash/` - Slash Command Registry
Central registry for commands like `/plan`, `/act`, `/cancel`:
- **CLI-local commands**: Handled directly by CLI
- **Backend commands**: Fetched from core via gRPC, filtered by `CliCompatible` flag
---
### 9. `sqlite/` - Instance Locking
Manages the distributed locking system:
- **Instance locks**: Track running Cline instances by address
- **File locks**: Coordinate file access across instances
- SQLite database created by cline-core, CLI reads/writes for discovery
---
### 10. `task/` - Task Management
| File | Purpose |
| ----------------------- | -------------------------------------------------------------------- |
| `manager.go` | Core orchestrator: create, cancel, resume, restore tasks; stream handling |
| `stream_coordinator.go` | Deduplication and turn management for dual streams |
| `input_handler.go` | Interactive input during follow mode (polling, approval detection) |
| `history_handler.go` | Direct disk access to `taskHistory.json` |
| `settings_parser.go` | Parse settings from CLI flags |
| `follow_options.go` | Configuration for follow behavior |
**Streaming:** Task manager subscribes to two gRPC streams:
1. `SubscribeToState` - Full state updates
2. `SubscribeToPartialMessage` - Streaming AI responses
---
### 11. `terminal/` - Terminal Handling
Enhanced keyboard protocol support and terminal configuration:
- Enables modifyOtherKeys and Kitty keyboard protocol
- Detects terminal type (VS Code, iTerm, Ghostty, Kitty, etc.)
- Auto-configures shift+enter keybindings for various terminals
---
### 12. `types/` - Type Definitions
| File | Purpose |
| -------------- | ----------------------------------------------------------------- |
| `messages.go` | `ClineMessage`, `AskType`, `SayType`, `ToolType` enums, proto conversion |
| `state.go` | `ConversationState` with thread-safe message access |
| `history.go` | `HistoryItem` matching taskHistory.json format |
---
### 13. `updater/` - Auto-Update
Background auto-update checking:
- 24-hour check interval (cached)
- Queries npm registry for newer versions
- Supports `latest` and `nightly` channels
- Runs `npm install -g cline` to update
---
## `pkg/common/` - Shared Types
| File | Purpose |
| --------------- | ------------------------------------------------------------ |
| `constants.go` | `SETTINGS_SUBFOLDER`, `DEFAULT_CLINE_CORE_PORT` |
| `schema.go` | SQL queries for instance/file locks |
| `types.go` | `CoreInstanceInfo`, `LockRow`, `DefaultCoreInstance` |
| `utils.go` | Port checking, health checks, address normalization, retry logic |
---
## `pkg/generated/` - Auto-Generated
| File | Purpose |
| --------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `providers.go` | Provider definitions (Anthropic, OpenAI, Bedrock, etc.) with field metadata and model specs - generated from TypeScript sources |
| `field_overrides.go` | Manual overrides for field filtering |
---
## `pkg/hostbridge/` - CLI-to-Core Bridge
This is the **reverse bridge** allowing cline-core to request host environment operations:
| File | Purpose |
| ----------------------- | ---------------------------------------------------- |
| `grpc_server.go` | Main server registering all services |
| `simple_workspace.go` | Workspace service: returns CWD as workspace path |
| `diff.go` | In-memory file diff editing with line-based operations |
| `env.go` | Clipboard access, version info, shutdown coordination |
| `window.go` | UI stubs (no-ops or console output) |
**Why this exists:** The same cline-core logic runs in VS Code and CLI. In VS Code, the "host" is the extension with editor APIs. In CLI, hostbridge emulates these capabilities with terminal-appropriate implementations.
---
## Key Design Decisions
1. **Two-process model:** `cline` CLI manages instances; `cline-core` is the actual AI engine (Node.js). This allows reusing the same core as the VS Code extension.
2. **Self-registration via SQLite:** `cline-core` registers itself in a SQLite database on startup. The CLI discovers instances by reading this database, enabling multi-instance support.
3. **Host bridge abstraction:** The `cline-host` process provides platform-specific operations (clipboard, workspace paths) via gRPC, allowing `cline-core` to remain host-agnostic.
4. **Streaming-first UI:** The CLI uses gRPC streaming to display AI responses in real-time with typewriter-style rendering.
5. **Dual stream handling:** Task manager subscribes to both state updates and partial messages, using deduplication to prevent duplicate rendering.
+1 -1
View File
@@ -92,6 +92,7 @@ func (m *Manager) ListSettings(ctx context.Context) error {
"telemetrySetting",
"planActSeparateModelsSetting",
"enableCheckpointsSetting",
"mcpMarketplaceEnabled",
"shellIntegrationTimeout",
"terminalReuseEnabled",
"mcpResponsesCollapsed",
@@ -110,7 +111,6 @@ func (m *Manager) ListSettings(ctx context.Context) error {
"dictationSettings",
"autoCondenseThreshold",
"autoApprovalSettings",
"hooksEnabled",
}
// Render each field using the renderer
+3 -2
View File
@@ -77,9 +77,10 @@ func RenderField(key string, value interface{}, censor bool) error {
case "mode", "telemetrySetting", "preferredLanguage", "customPrompt",
"defaultTerminalProfile", "mcpDisplayMode", "openaiReasoningEffort",
"planActSeparateModelsSetting", "enableCheckpointsSetting",
"terminalReuseEnabled", "mcpResponsesCollapsed", "strictPlanModeEnabled",
"mcpMarketplaceEnabled", "terminalReuseEnabled",
"mcpResponsesCollapsed", "strictPlanModeEnabled",
"useAutoCondense", "yoloModeToggled", "shellIntegrationTimeout",
"terminalOutputLineLimit", "autoCondenseThreshold", "hooksEnabled":
"terminalOutputLineLimit", "autoCondenseThreshold":
fmt.Printf("%s: %s\n", camelToKebab(key), formatValue(value, key, censor))
return nil
+3 -12
View File
@@ -161,14 +161,6 @@ func (tr *ToolRenderer) generateToolHeader(tool *types.ToolMessage, verbTense st
}
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
case string(types.ToolTypeWebSearch):
if verbTense == "wants to" {
action = "wants to search for"
} else {
action = "is searching for"
}
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
case string(types.ToolTypeListCodeDefinitionNames):
if verbTense == "wants to" {
action = "wants to list code definitions in"
@@ -215,8 +207,8 @@ func (tr *ToolRenderer) GenerateToolContentPreview(tool *types.ToolMessage) stri
previewMd := fmt.Sprintf("```\n%s\n```", preview)
return tr.renderMarkdown(previewMd)
case string(types.ToolTypeReadFile), string(types.ToolTypeWebFetch), string(types.ToolTypeWebSearch), string(types.ToolTypeFileDeleted):
// No preview for read/fetch/search operations
case string(types.ToolTypeReadFile), string(types.ToolTypeWebFetch), string(types.ToolTypeFileDeleted):
// No preview for read/fetch operations
return ""
default:
@@ -251,8 +243,7 @@ func (tr *ToolRenderer) GenerateToolContentBody(tool *types.ToolMessage) string
string(types.ToolTypeListFilesRecursive),
string(types.ToolTypeListCodeDefinitionNames),
string(types.ToolTypeSearchFiles),
string(types.ToolTypeWebFetch),
string(types.ToolTypeWebSearch):
string(types.ToolTypeWebFetch):
// Use parser for structured output
preview := toolParser.ParseToolResult(tool)
return tr.renderMarkdown(preview)
@@ -224,11 +224,6 @@ func (p *ToolResultParser) ParseWebFetch(content, url string) string {
return ""
}
// ParseWebSearch formats webSearch tool results
func (p *ToolResultParser) ParseWebSearch(content, query string) string {
return ""
}
// detectLanguage returns syntax highlighting language based on file extension
func (p *ToolResultParser) detectLanguage(ext string) string {
langMap := map[string]string{
@@ -294,8 +289,6 @@ func (p *ToolResultParser) ParseToolResult(tool *types.ToolMessage) string {
return p.ParseCodeDefinitions(tool.Content)
case "webFetch":
return p.ParseWebFetch(tool.Content, tool.Path)
case "webSearch":
return p.ParseWebSearch(tool.Content, tool.Path)
default:
return tool.Content
}
+2 -3
View File
@@ -478,9 +478,8 @@ func startClineCore(corePort, hostPort int) (*exec.Cmd, error) {
env = append(env,
fmt.Sprintf("NODE_PATH=%s", nodePath),
// These control gRPC debug logging
//"GRPC_TRACE=all",
//"GRPC_VERBOSITY=DEBUG",
"GRPC_TRACE=all",
"GRPC_VERBOSITY=DEBUG",
"NODE_ENV=development",
)
cmd.Env = env
+1 -2
View File
@@ -251,8 +251,7 @@ func determineAutoApprovalAction(msg *types.ClineMessage) (string, error) {
types.ToolTypeListFilesRecursive,
types.ToolTypeListCodeDefinitionNames,
types.ToolTypeSearchFiles,
types.ToolTypeWebFetch,
types.ToolTypeWebSearch:
types.ToolTypeWebFetch:
return "read_files", nil
case types.ToolTypeEditedExistingFile,
types.ToolTypeNewFileCreated:
-35
View File
@@ -984,33 +984,6 @@ func (m *Manager) processStateUpdate(stateUpdate *cline.State, coordinator *Stre
coordinator.MarkProcessedInCurrentTurn(msgKey)
}
case msg.Say == string(types.SayTypeMcpServerResponse):
msgKey := fmt.Sprintf("%d", msg.Timestamp)
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
fmt.Println()
m.displayMessage(msg, false, false, i)
coordinator.MarkProcessedInCurrentTurn(msgKey)
}
case msg.Say == string(types.SayTypeMcpNotification):
msgKey := fmt.Sprintf("%d", msg.Timestamp)
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
fmt.Println()
m.displayMessage(msg, false, false, i)
coordinator.MarkProcessedInCurrentTurn(msgKey)
}
case msg.Say == string(types.SayTypeUseMcpServer):
msgKey := fmt.Sprintf("%d", msg.Timestamp)
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
fmt.Println()
m.displayMessage(msg, false, false, i)
coordinator.MarkProcessedInCurrentTurn(msgKey)
}
case msg.Say == string(types.SayTypeCheckpointCreated):
msgKey := fmt.Sprintf("%d", msg.Timestamp)
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
@@ -1034,14 +1007,6 @@ func (m *Manager) processStateUpdate(stateUpdate *cline.State, coordinator *Stre
}
}
case msg.Say == string(types.SayTypeCompletionResult):
msgKey := fmt.Sprintf("%d", msg.Timestamp)
if !msg.Partial && !coordinator.IsProcessedInCurrentTurn(msgKey) {
fmt.Println()
m.displayMessage(msg, false, false, i)
coordinator.MarkProcessedInCurrentTurn(msgKey)
}
case msg.Ask == string(types.AskTypeCommandOutput):
msgKey := fmt.Sprintf("%d", msg.Timestamp)
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
-6
View File
@@ -290,12 +290,6 @@ func setSimpleField(settings *cline.Settings, key, value string) error {
return err
}
settings.ActModeAwsBedrockCustomSelected = boolPtr(val)
case "hooks_enabled":
val, err := parseBool(value)
if err != nil {
return err
}
settings.HooksEnabled = boolPtr(val)
// Integer fields
case "request_timeout_ms":
-1
View File
@@ -113,7 +113,6 @@ const (
ToolTypeListCodeDefinitionNames ToolType = "listCodeDefinitionNames"
ToolTypeSearchFiles ToolType = "searchFiles"
ToolTypeWebFetch ToolType = "webFetch"
ToolTypeWebSearch ToolType = "webSearch"
ToolTypeSummarizeTask ToolType = "summarizeTask"
)
+6 -43
View File
@@ -278,47 +278,18 @@
"pages": [
"enterprise-solutions/overview",
"enterprise-solutions/onboarding",
"enterprise-solutions/team-management/managing-members",
"enterprise-solutions/members/roles-and-permissions",
{
"group": "SaaS Provider Configuration",
"group": "Provider Remote Configuration",
"pages": [
"enterprise-solutions/configuration/remote-configuration/overview",
{
"group": "AWS Bedrock",
"pages": [
"enterprise-solutions/configuration/remote-configuration/aws-bedrock/admin-configuration",
"enterprise-solutions/configuration/remote-configuration/aws-bedrock/member-configuration"
]
},
{
"group": "LiteLLM",
"pages": [
"enterprise-solutions/configuration/remote-configuration/litellm/admin-configuration",
"enterprise-solutions/configuration/remote-configuration/litellm/member-configuration"
]
},
{
"group": "Google Vertex AI",
"pages": [
"enterprise-solutions/configuration/remote-configuration/google-vertex/admin-configuration",
"enterprise-solutions/configuration/remote-configuration/google-vertex/member-configuration"
"enterprise-solutions/provider-remote-config/aws-bedrock/admin-configuration",
"enterprise-solutions/provider-remote-config/aws-bedrock/member-configuration"
]
}
]
},
{
"group": "Control Other Cline Features",
"pages": [
"enterprise-solutions/configuration/infrastructure-configuration/control-other-cline-features/yolo-mode"
]
},
{
"group": "Monitoring",
"pages": [
"enterprise-solutions/monitoring/overview",
"enterprise-solutions/monitoring/telemetry",
"enterprise-solutions/monitoring/opentelemetry"
]
}
]
}
@@ -396,11 +367,11 @@
},
{
"source": "/enterprise-solutions/configure-AWS-Bedrock-Admin",
"destination": "/enterprise-solutions/configuration/remote-configuration/aws-bedrock/admin-configuration"
"destination": "/enterprise-solutions/provider-remote-config/aws-bedrock/admin-configuration"
},
{
"source": "/enterprise-solutions/configure-AWS-Bedrock-Member",
"destination": "/enterprise-solutions/configuration/remote-configuration/aws-bedrock/member-configuration"
"destination": "/enterprise-solutions/provider-remote-config/aws-bedrock/member-configuration"
},
{
"source": "/enterprise-solutions/configure-workOS-authkit",
@@ -409,14 +380,6 @@
{
"source": "/enterprise-solutions/Onboarding your Organization",
"destination": "/enterprise-solutions/onboarding"
},
{
"source": "/enterprise-solutions/team-management/overview",
"destination": "/enterprise-solutions/team-management/managing-members"
},
{
"source": "/enterprise-solutions/team-management/roles-and-permissions",
"destination": "/enterprise-solutions/team-management/managing-members"
}
],
"search": {
@@ -1,105 +0,0 @@
---
title: "Choosing Your Configuration Path"
sidebarTitle: "Deployment Guide"
description: "Decide between SaaS and Self-Hosted configuration for your Cline Enterprise deployment"
---
Choose the right configuration approach for your organization. Most teams start with SaaS for quick deployment, while enterprises with complex requirements opt for self-hosted infrastructure.
## Configuration Paths
<CardGroup cols={2}>
<Card title="SaaS Provider Configuration" icon="cloud" href="/enterprise-solutions/configuration/remote-configuration/overview">
### Quick Setup via Web Console
✅ No infrastructure required
✅ 5-10 minute configuration
✅ Web-based admin console
✅ Automatic updates
✅ Simplified credential management
**Best for:**
- Small to medium teams (5-50 developers)
- Quick deployment needs
- Limited DevOps resources
- Standard security requirements
- Single region deployments
</Card>
<Card title="Self-Hosted Configuration" icon="server" href="/enterprise-solutions/configuration/infrastructure-configuration/overview">
### Full Infrastructure Control
✅ Your own AWS/GCP/K8s
✅ VPC endpoints & private connectivity
✅ Multi-account setups
✅ Advanced compliance & audit
✅ GitOps workflows
**Best for:**
- Large enterprises (50+ developers)
- Complex security requirements
- Existing cloud infrastructure
- Multi-region deployments
- Custom compliance needs
</Card>
</CardGroup>
## Detailed Comparison
### Feature Comparison
| Feature | SaaS | Self-Hosted |
|---------|------|-------------|
| **Configuration** | Web UI | YAML + Helm/Kubernetes |
| **Infrastructure** | None required | Full AWS/GCP/K8s |
| **VPC Endpoints** | Basic | Full private connectivity |
| **Multi-Account** | ❌ | ✅ |
| **IAM** | Standard RBAC roles | Standard RBAC roles |
| **Compliance** | Standard | Custom frameworks |
| **GitOps** | ❌ | ✅ |
| **Maintenance** | Managed by Cline | Self-managed |
| **Updates** | Automatic (extension) | Automatic (extension) + Infrastructure control |
### Security & Compliance
| Capability | SaaS | Self-Hosted |
|------------|------|-------------|
| **Network Encryption** | HTTPS/TLS | HTTPS/TLS |
| **Network** | Public internet | Private VPC endpoints |
| **Access Control** | Standard RBAC | Standard RBAC |
| **Audit Logs** | OpenTelemetry traces | OpenTelemetry traces + Infrastructure logs |
| **Data Residency** | Cline-managed deployment | Customer-controlled deployment |
### Cost Structure
| Cost Category | SaaS | Self-Hosted |
|---------------|------|-------------|
| **Cline Subscription** | Fixed enterprise fee | Fixed enterprise fee |
| **Inference Provider Costs** | Usage-based | Usage-based |
| **Infrastructure** | ✅ None required | Kubernetes, networking, storage |
| **Personnel** | ✅ None required | DevOps team needed |
| **Total Cost Profile** | Predictable and simple | Variable based on scale |
## Migration Path
<Note>
Most organizations start with SaaS configuration for quick deployment, then migrate to self-hosted later as requirements grow. This minimizes risk and ensures your infrastructure meets actual usage patterns.
</Note>
## Getting Started
<CardGroup cols={2}>
<Card title="Start with SaaS" icon="rocket" href="/enterprise-solutions/configuration/remote-configuration/overview">
Begin with quick SaaS setup
</Card>
<Card title="Deploy Self-Hosted" icon="server" href="/enterprise-solutions/configuration/infrastructure-configuration/overview">
Plan your infrastructure deployment
</Card>
</CardGroup>
## Need Help Deciding?
- [**Contact Cline Enterprise Sales**](https://cline.bot/contact-sales) for a consultation on your specific requirements
- [**Start with SaaS**](/enterprise-solutions/configuration/remote-configuration/overview) if unsure - it's lower risk and you can always migrate later
- [**Review Self-Hosted Requirements**](/enterprise-solutions/configuration/infrastructure-configuration/overview) if you have existing infrastructure that could benefit from self-hosted deployment
@@ -1,35 +0,0 @@
---
title: "Overview"
sidebarTitle: "Overview"
description: "Configure Cline settings for your enterprise deployment"
---
This section covers configuration options for controlling Cline's behavior in enterprise deployments.
## Available Settings
<Card title="YOLO Mode" icon="rocket" href="/enterprise-solutions/configuration/infrastructure-configuration/control-other-cline-features/yolo-mode">
Control enterprise access to autonomous operation mode with complete auto-approval
</Card>
## Configuration Methods
These settings can be configured through:
### Individual Users
- Users can toggle settings in their local Cline interface
- Enterprise policies can restrict certain settings
- Changes apply immediately to new tasks
## Enterprise Controls
Administrators can enforce policies through remote configuration:
```json
{
"yoloModeAllowed": false
}
```
When `yoloModeAllowed` is set to `false`, users cannot enable YOLO Mode in their local Cline interface.
@@ -1,233 +0,0 @@
---
title: "YOLO Mode"
sidebarTitle: "YOLO Mode"
description: "Enterprise controls for YOLO Mode autonomous operation"
---
YOLO Mode enables Cline to operate with complete autonomy, auto-approving all actions without user confirmation. For Enterprise administrators, this page covers how to control access to YOLO Mode across your organization.
<Note>
For complete details about YOLO Mode functionality, risks, and best practices, see [YOLO Mode in Features](/features/yolo-mode).
</Note>
## Overview
When YOLO Mode is enabled, Cline automatically approves all operations including file changes, terminal commands, browser actions, and mode transitions. This provides maximum automation speed but removes all safety guardrails.
<Warning>
YOLO Mode is powerful but potentially dangerous. Administrators should carefully consider which teams or users should have access to this feature.
</Warning>
## Enterprise Administrator Configuration
As an Enterprise administrator, you can control whether users in your organization can enable YOLO Mode through remote configuration.
### Disabling YOLO Mode for All Users
Add the following to your remote configuration JSON:
```json
{
"yoloModeAllowed": false
}
```
When `yoloModeAllowed` is set to `false`:
- The YOLO Mode toggle is disabled in all user interfaces
- Users cannot enable YOLO Mode even in their local settings
- This policy applies immediately to all team members
- Enterprise policy takes precedence over individual preferences
### Enabling YOLO Mode for All Users
```json
{
"yoloModeAllowed": true
}
```
When `yoloModeAllowed` is set to `true` or omitted:
- Users can enable or disable YOLO Mode in their local Cline settings
- Individual users make their own decisions about using YOLO Mode
- No organizational restrictions apply
## Enterprise Policy Recommendations
### Recommended Approach
Most organizations should **disable YOLO Mode by default** for the following reasons:
<AccordionGroup>
<Accordion title="Security & Compliance" icon="shield">
YOLO Mode removes all approval gates, potentially allowing:
- Unreviewed code changes to critical systems
- Execution of commands without oversight
- Automated actions that may violate compliance policies
- Risk of data exposure through unmonitored operations
</Accordion>
<Accordion title="Code Quality Control" icon="code">
Without approval prompts:
- Changes happen too quickly to review in real-time
- Mistakes can compound before detection
- Quality gates are bypassed
- Rollback becomes more complex
</Accordion>
<Accordion title="Audit Requirements" icon="clipboard-check">
Many industries require:
- Documented approval trails for code changes
- Clear accountability for automated actions
- Traceable decision-making processes
- YOLO Mode may conflict with these requirements
</Accordion>
</AccordionGroup>
### Exceptions: When to Allow YOLO Mode
Consider enabling YOLO Mode for:
**Sandbox/Development Environments**
- Isolated testing environments
- Personal development machines
- Proof-of-concept projects
- Temporary exploratory work
**Specialized Roles**
- DevOps automation engineers (with proper monitoring)
- Research & development teams in sandboxed environments
- Teams with robust rollback and recovery procedures
**Controlled Use Cases**
- Scripted CI/CD pipelines with comprehensive logging
- Automated testing scenarios
- Demonstration or training environments
## Enterprise Considerations
### Security Implications
When YOLO Mode is enabled in your organization:
**Risk Factors:**
- All tool executions happen automatically without human review
- Potential for rapid propagation of mistakes across multiple files
- Reduced opportunity to catch security vulnerabilities before implementation
- Automated operations may bypass existing security controls
**Mitigations:**
- Implement comprehensive logging and monitoring
- Restrict YOLO Mode to non-production environments
- Require periodic security reviews for teams using YOLO Mode
- Ensure version control and rollback procedures are in place
### Monitoring Requirements
When allowing YOLO Mode in your organization, implement:
**Mandatory Monitoring:**
1. **Real-time Activity Tracking**
- Monitor which users enable YOLO Mode
- Track when YOLO Mode is active
- Log all automated actions taken
2. **Audit Trail Maintenance**
- Preserve complete history of YOLO Mode sessions
- Document what was automated and when
- Maintain records for compliance purposes
3. **Anomaly Detection**
- Alert on unusual patterns of automated actions
- Flag high-risk operations performed automatically
- Monitor for potential security incidents
### Monitoring YOLO Mode Usage
When YOLO Mode is enabled (by policy), track usage through:
**Telemetry Events:**
- Captures when users toggle YOLO Mode on/off
- Records which tasks were executed with YOLO Mode enabled
- Provides aggregate usage statistics across your organization
**Task History:**
- Task metadata indicates whether YOLO Mode was active
- Complete action logs show automated approvals
- Enables post-action review and analysis
**Audit Logs:**
- Standard logging captures all automated decisions
- Tool executions are recorded with timestamps
- Provides compliance trail for regulated environments
## Recommended Policies by Organization Size
### Small Teams (5-20 developers)
- **Default:** Disabled
- **Exceptions:** Allow for individual sandbox environments
- **Monitoring:** Basic telemetry sufficient
### Medium Organizations (20-100 developers)
- **Default:** Disabled
- **Exceptions:** Permit for designated dev/test environments only
- **Monitoring:** Required telemetry + regular audit reviews
### Large Enterprises (100+ developers)
- **Default:** Strictly disabled
- **Exceptions:** Require security approval for each use case
- **Monitoring:** Comprehensive telemetry + real-time alerting + compliance reporting
## Technical Implementation
### Configuration Management
**Centralized Control through Remote Configuration:**
```json
{
"yoloModeAllowed": false,
// Other policies...
}
```
This setting:
- Applies instantly to all connected clients
- Cannot be overridden by individual users
- Persists across Cline restarts
- Is synchronized across all team members
### Policy Enforcement
The enforcement mechanism:
1. Users authenticate with your enterprise configuration server
2. Remote configuration is downloaded and applied
3. Local UI respects enterprise policy settings
4. YOLO Mode toggle is disabled if policy forbids it
5. Users see a message explaining the enterprise restriction
## Compliance Considerations
For organizations in regulated industries:
**SOC 2 Compliance:**
- YOLO Mode may conflict with change management controls
- Document decision to allow/disallow in security policies
- Implement compensating controls if YOLO Mode is permitted
**GDPR/Data Protection:**
- Automated operations must still respect data handling policies
- Ensure YOLO Mode doesn't bypass data protection safeguards
- Maintain audit trails of automated data processing
**Industry-Specific:**
- Financial services: Generally incompatible with Reg requirements
- Healthcare: May violate HIPAA audit trail requirements
- Government: Often conflicts with approval workflow mandates
## Support & Questions
For help configuring YOLO Mode policies:
- Review [Remote Configuration Overview](/enterprise-solutions/configuration/remote-configuration/overview)
- See [Features: YOLO Mode](/features/yolo-mode) for detailed functionality
- Contact your Enterprise support representative
- Join our [Discord](https://discord.gg/cline) for community discussion
@@ -1,565 +0,0 @@
---
title: "MCP Marketplace"
sidebarTitle: "MCP Marketplace"
description: "Deploy pre-built enterprise MCP servers from the Cline marketplace with one-click configuration"
---
The MCP Marketplace provides curated, enterprise-ready integrations with popular development tools and services. All marketplace servers are built with enterprise security, compliance, and scalability in mind.
## Enterprise Marketplace Benefits
<CardGroup cols={2}>
<Card title="One-Click Deployment" icon="rocket">
Deploy complex integrations instantly with pre-configured enterprise settings.
</Card>
<Card title="Security Hardened" icon="shield-check">
All servers include enterprise security features, audit logging, and compliance controls.
</Card>
<Card title="Maintained & Updated" icon="sync">
Regular security updates and feature enhancements managed by Cline Enterprise team.
</Card>
<Card title="Enterprise Support" icon="headset">
Dedicated support channels for marketplace integration issues and customization.
</Card>
</CardGroup>
## Available Integrations
### Development Tools
<CardGroup cols={3}>
<Card title="GitHub Enterprise" icon="github">
Repository management, issue tracking, PR workflows, and code analysis
</Card>
<Card title="GitLab Enterprise" icon="gitlab">
Project management, CI/CD pipelines, merge requests, and security scanning
</Card>
<Card title="Bitbucket Enterprise" icon="bitbucket">
Source code management, build pipelines, and deployment automation
</Card>
</CardGroup>
### Project Management
<CardGroup cols={3}>
<Card title="Jira Enterprise" icon="jira">
Issue tracking, sprint management, custom fields, and workflow automation
</Card>
<Card title="Azure DevOps" icon="microsoft">
Work items, boards, repos, pipelines, and test management
</Card>
<Card title="Linear" icon="linear">
Issue tracking, project planning, and development workflow integration
</Card>
</CardGroup>
### Communication & Collaboration
<CardGroup cols={3}>
<Card title="Slack Enterprise Grid" icon="slack">
Notifications, bot interactions, file sharing, and workflow automation
</Card>
<Card title="Microsoft Teams" icon="microsoft-teams">
Chat notifications, meeting integration, and collaborative workflows
</Card>
<Card title="Discord" icon="discord">
Community management, bot interactions, and developer notifications
</Card>
</CardGroup>
### Cloud Services
<CardGroup cols={3}>
<Card title="AWS Services" icon="aws">
EC2, S3, Lambda, RDS, CloudWatch, and other AWS service integrations
</Card>
<Card title="Google Cloud" icon="google-cloud">
Compute Engine, Cloud Storage, BigQuery, and GCP service management
</Card>
<Card title="Azure Services" icon="azure">
Virtual Machines, Storage Accounts, Functions, and Azure resource management
</Card>
</CardGroup>
## Installing Marketplace Servers
### Via Cline Enterprise Dashboard
1. **Access Marketplace**: Navigate to `Settings > Enterprise > MCP Marketplace`
2. **Browse Integrations**: Filter by category, popularity, or search by name
3. **Review Details**: Check compatibility, permissions, and configuration requirements
4. **Install**: Click "Install" and configure required settings
5. **Deploy**: Approve deployment to your selected environment
### Via Configuration File
Install marketplace servers through enterprise configuration:
```yaml
# enterprise-mcp-config.yaml
mcp:
marketplace_servers:
- name: "github-enterprise"
package: "@cline/mcp-github-enterprise"
version: "2.1.0"
environment: "production"
config:
github:
base_url: "https://github.company.com/api/v3"
token: "${GITHUB_ENTERPRISE_TOKEN}"
organization: "company"
features:
issue_management: true
pull_request_automation: true
code_analysis: true
security_scanning: true
permissions:
repositories: "read-write"
issues: "write"
pull_requests: "write"
compliance:
audit_logging: true
data_retention_days: 365
encryption_at_rest: true
- name: "jira-enterprise"
package: "@cline/mcp-jira-enterprise"
version: "1.8.3"
environment: "production"
config:
jira:
base_url: "https://company.atlassian.net"
username: "${JIRA_USERNAME}"
api_token: "${JIRA_API_TOKEN}"
projects:
- key: "DEV"
permissions: ["read", "write", "transition"]
- key: "OPS"
permissions: ["read", "comment"]
compliance:
field_encryption: ["description", "comments"]
audit_trail: true
```
### Via CLI
Deploy using the Cline Enterprise CLI:
```bash
# Install GitHub Enterprise integration
cline-enterprise mcp install github-enterprise \
--version 2.1.0 \
--config-file github-config.yaml \
--environment production
# Install Slack Enterprise Grid integration
cline-enterprise mcp install slack-enterprise-grid \
--version 1.5.2 \
--config workspace_id=T1234567890 \
--config bot_token=${SLACK_BOT_TOKEN} \
--environment production
# List installed marketplace servers
cline-enterprise mcp list --environment production
# Check server status
cline-enterprise mcp status github-enterprise --environment production
```
## Configuration Examples
### GitHub Enterprise Integration
```yaml
# github-enterprise-config.yaml
github:
base_url: "https://github.company.com/api/v3"
token: "${GITHUB_ENTERPRISE_TOKEN}"
organization: "company"
# Repository access controls
repositories:
allowed_patterns:
- "company/*"
- "internal/*"
blocked_patterns:
- "*/secrets"
- "*/private-keys"
# Feature configuration
features:
issue_management:
enabled: true
auto_assign: true
labels:
- "ai-generated"
- "cline-task"
pull_requests:
enabled: true
auto_review_request: true
required_approvals: 2
enforce_branch_protection: true
code_analysis:
enabled: true
languages: ["typescript", "python", "go", "rust"]
security_scan: true
# Security and compliance
security:
webhook_secret: "${GITHUB_WEBHOOK_SECRET}"
rate_limiting:
requests_per_hour: 5000
burst_limit: 100
ip_whitelist:
- "10.0.0.0/8"
- "192.168.0.0/16"
audit:
log_level: "INFO"
include_payloads: false
retention_days: 365
destinations: ["datadog", "splunk"]
```
### Jira Enterprise Integration
```yaml
# jira-enterprise-config.yaml
jira:
base_url: "https://company.atlassian.net"
username: "${JIRA_USERNAME}"
api_token: "${JIRA_API_TOKEN}"
# Project access configuration
projects:
- key: "DEV"
name: "Development"
permissions: ["read", "write", "transition", "assign"]
issue_types: ["Story", "Bug", "Task", "Subtask"]
- key: "OPS"
name: "Operations"
permissions: ["read", "comment", "watch"]
# Custom field mappings
custom_fields:
story_points: "customfield_10002"
epic_link: "customfield_10014"
sprint: "customfield_10020"
# Workflow automation
automation:
auto_transition:
enabled: true
rules:
- from_status: "To Do"
to_status: "In Progress"
condition: "assignee_changed"
auto_assign:
enabled: true
rules:
- issue_type: "Bug"
component: "Frontend"
assignee: "frontend-team-lead"
# Security and compliance
security:
encrypt_fields: ["description", "comment"]
mask_sensitive_data: true
audit_changes: true
compliance:
gdpr_compliant: true
data_retention_policy: "365_days"
audit_log_retention: "7_years"
```
### Slack Enterprise Grid Integration
```yaml
# slack-enterprise-config.yaml
slack:
workspace_id: "T1234567890"
bot_token: "${SLACK_BOT_TOKEN}"
signing_secret: "${SLACK_SIGNING_SECRET}"
# Channel management
channels:
notifications:
- name: "#dev-alerts"
types: ["deployments", "errors", "security"]
- name: "#ai-activity"
types: ["cline-tasks", "completions"]
private_channels:
- name: "#security-incidents"
members: ["security-team"]
types: ["security-alerts", "compliance-issues"]
# Bot behavior
bot:
display_name: "Cline Enterprise"
default_channel: "#general"
response_delay_ms: 1000
commands:
- command: "/cline-status"
description: "Check Cline Enterprise status"
permission: "all"
- command: "/cline-deploy"
description: "Trigger deployment"
permission: "admin"
# Enterprise features
enterprise:
app_approval_required: true
data_residency: "US"
compliance_export: true
dlp:
enabled: true
scan_messages: true
block_sensitive_data: true
# Security settings
security:
require_app_approval: true
audit_api_calls: true
encrypt_messages: true
retain_audit_logs_days: 2555 # 7 years
```
## Enterprise Management
### Multi-Environment Deployment
Deploy marketplace servers across environments:
```yaml
# environments-config.yaml
environments:
development:
marketplace_servers:
- github-enterprise:
version: "2.1.0-beta"
config_override:
github:
base_url: "https://github-dev.company.com/api/v3"
organization: "company-dev"
staging:
marketplace_servers:
- github-enterprise:
version: "2.1.0-rc1"
config_override:
github:
base_url: "https://github-staging.company.com/api/v3"
organization: "company-staging"
production:
marketplace_servers:
- github-enterprise:
version: "2.1.0"
config_override:
github:
base_url: "https://github.company.com/api/v3"
organization: "company"
```
### Version Management
Control marketplace server versions:
```bash
# List available versions
cline-enterprise mcp versions github-enterprise
# Upgrade to latest version
cline-enterprise mcp upgrade github-enterprise --version 2.2.0 --environment staging
# Rollback to previous version
cline-enterprise mcp rollback github-enterprise --version 2.1.0 --environment staging
# Pin to specific version (disable auto-updates)
cline-enterprise mcp pin github-enterprise --version 2.1.0
```
### Health Monitoring
Monitor marketplace server health:
```yaml
# monitoring-config.yaml
monitoring:
marketplace_servers:
health_checks:
interval_seconds: 30
timeout_seconds: 10
metrics:
- server_status
- request_latency
- error_rate
- resource_usage
alerts:
- name: "marketplace-server-down"
condition: "server_status != 1"
severity: "critical"
- name: "high-error-rate"
condition: "error_rate > 0.05"
severity: "warning"
- name: "performance-degradation"
condition: "request_latency > 5s"
severity: "warning"
```
## Security & Compliance
### Enterprise Security Features
All marketplace servers include:
- **Authentication Integration**: SSO, SAML, OAuth2 support
- **Authorization Controls**: RBAC and fine-grained permissions
- **Audit Logging**: Comprehensive activity tracking
- **Data Encryption**: At-rest and in-transit encryption
- **Network Security**: VPN, IP whitelisting, private endpoints
- **Compliance**: SOC2, GDPR, HIPAA compliance frameworks
### Data Governance
Configure data handling policies:
```yaml
# data-governance-config.yaml
data_governance:
classification:
public:
retention_days: 90
backup_required: false
internal:
retention_days: 365
backup_required: true
encryption_required: false
confidential:
retention_days: 2555 # 7 years
backup_required: true
encryption_required: true
audit_access: true
restricted:
retention_days: 2555
backup_required: true
encryption_required: true
audit_access: true
approval_required: true
privacy:
pii_detection: true
pii_masking: true
gdpr_compliance: true
data_subject_requests: true
compliance:
frameworks: ["SOC2", "GDPR", "CCPA", "HIPAA"]
audit_frequency: "quarterly"
certification_renewal: "annual"
```
## Best Practices
### Installation
1. **Review Permissions**: Always review required permissions before installation
2. **Test in Staging**: Deploy to staging environment first
3. **Configuration Validation**: Validate configuration files before deployment
4. **Backup Current State**: Create configuration backups before changes
5. **Monitor Deployment**: Watch health metrics during rollout
### Configuration
1. **Environment Separation**: Use different configurations per environment
2. **Secret Management**: Store sensitive data in secure secret stores
3. **Version Pinning**: Pin versions for production deployments
4. **Access Controls**: Implement least-privilege access policies
5. **Regular Updates**: Schedule regular security and feature updates
### Monitoring
1. **Health Checks**: Monitor server health continuously
2. **Performance Metrics**: Track latency and throughput
3. **Error Tracking**: Alert on error rates and failure patterns
4. **Resource Usage**: Monitor CPU, memory, and network usage
5. **Audit Reviews**: Regular review of audit logs and access patterns
## Troubleshooting
### Common Issues
**Installation Failures**:
```bash
# Check marketplace connectivity
cline-enterprise mcp marketplace-status
# Verify authentication
cline-enterprise auth verify --service marketplace
# Check installation logs
cline-enterprise logs mcp-installer --lines 100
```
**Configuration Errors**:
```bash
# Validate configuration
cline-enterprise mcp validate-config --file config.yaml
# Test connectivity
cline-enterprise mcp test-connection github-enterprise --environment staging
# Check server status
cline-enterprise mcp status --all
```
**Performance Issues**:
```bash
# Check server metrics
cline-enterprise mcp metrics github-enterprise --duration 1h
# View recent error logs
cline-enterprise logs github-enterprise --level error --lines 50
```
## Support
For marketplace server issues:
- **Documentation**: Check server-specific documentation in the dashboard
- **Community**: Join the Cline Enterprise community forum
- **Support Tickets**: Create support tickets for critical issues
- **Professional Services**: Engage professional services for custom configurations
Enterprise customers have access to dedicated support channels with SLA guarantees.
@@ -1,571 +0,0 @@
---
title: "MCP Integration"
sidebarTitle: "Overview"
description: "Configure Model Context Protocol (MCP) servers and marketplace integrations for enterprise Cline deployments"
---
Model Context Protocol (MCP) provides standardized communication between AI models and external data sources, tools, and services. Enterprise MCP integration allows you to securely connect Cline to your organization's systems while maintaining governance and compliance.
## Enterprise MCP Benefits
<CardGroup cols={2}>
<Card title="Extensible Architecture" icon="puzzle-piece">
Connect to unlimited external tools, databases, APIs, and services through standardized MCP servers.
</Card>
<Card title="Enterprise Security" icon="shield-alt">
Secure authentication, authorization, and audit trails for all MCP server communications.
</Card>
<Card title="Centralized Management" icon="network-wired">
Manage and deploy MCP servers enterprise-wide with version control and configuration management.
</Card>
<Card title="Compliance Ready" icon="clipboard-check">
Built-in logging, monitoring, and data governance for regulatory compliance requirements.
</Card>
</CardGroup>
## MCP Architecture Overview
```mermaid
graph TB
A[Cline Enterprise] --> B[MCP Hub]
B --> C[MCP Marketplace]
B --> D[Remote MCP Servers]
B --> E[Internal MCP Servers]
C --> F[GitHub Integration]
C --> G[Slack Integration]
C --> H[Jira Integration]
D --> I[Custom APIs]
D --> J[Databases]
D --> K[Cloud Services]
E --> L[Internal Tools]
E --> M[Legacy Systems]
E --> N[Security Systems]
O[Enterprise Admin] --> B
P[Audit Logging] --> B
Q[Authentication] --> B
```
## Core Components
<CardGroup cols={2}>
<Card title="MCP Marketplace" icon="store" href="/enterprise-solutions/configuration/infrastructure-configuration/mcp/mcp-marketplace">
Pre-built, enterprise-ready MCP servers for popular tools and services with one-click deployment.
</Card>
<Card title="Remote MCP Servers" icon="server" href="/enterprise-solutions/configuration/infrastructure-configuration/mcp/remote-mcp-servers">
Deploy and manage custom MCP servers across your infrastructure with centralized configuration.
</Card>
</CardGroup>
## Enterprise Configuration
### Basic MCP Hub Setup
Configure the central MCP hub for your enterprise deployment:
```yaml
# mcp-hub-config.yaml
mcp:
hub:
enabled: true
port: 8080
authentication:
method: "enterprise-sso"
jwt_secret: "${MCP_JWT_SECRET}"
# Server discovery
discovery:
methods: ["marketplace", "remote", "local"]
marketplace_url: "https://mcp.cline.bot/marketplace"
# Security settings
security:
enforce_tls: true
allowed_origins: ["https://*.company.com"]
rate_limiting:
requests_per_minute: 1000
burst_size: 100
# Audit and compliance
audit:
enabled: true
log_level: "INFO"
destinations: ["file", "syslog", "datadog"]
retention_days: 90
```
### Multi-Environment Configuration
Deploy MCP configurations across environments:
<Tabs>
<Tab title="Development">
```yaml
# mcp-dev-config.yaml
mcp:
environment: "development"
servers:
- name: "github-dev"
type: "marketplace"
package: "@cline/mcp-github"
version: "latest"
config:
github_token: "${GITHUB_DEV_TOKEN}"
org: "company-dev"
- name: "local-db"
type: "remote"
url: "http://localhost:3001"
auth:
type: "api-key"
key: "${DEV_DB_API_KEY}"
policies:
allow_experimental: true
auto_update: true
rate_limits:
relaxed: true
```
</Tab>
<Tab title="Production">
```yaml
# mcp-prod-config.yaml
mcp:
environment: "production"
servers:
- name: "github-prod"
type: "marketplace"
package: "@cline/mcp-github"
version: "1.2.3" # Pinned version
config:
github_token: "${GITHUB_PROD_TOKEN}"
org: "company"
- name: "crm-integration"
type: "remote"
url: "https://mcp-crm.internal.company.com"
auth:
type: "mtls"
cert_path: "/certs/mcp-client.pem"
key_path: "/certs/mcp-client-key.pem"
- name: "security-scanner"
type: "remote"
url: "https://security-mcp.company.com"
auth:
type: "oauth2"
client_id: "${SECURITY_CLIENT_ID}"
client_secret: "${SECURITY_CLIENT_SECRET}"
policies:
allow_experimental: false
auto_update: false
strict_versioning: true
monitoring:
metrics: true
health_checks: true
alert_on_failure: true
```
</Tab>
</Tabs>
## Server Management
### Lifecycle Management
Manage MCP server deployments with GitOps:
```yaml
# mcp-server-manifest.yaml
apiVersion: mcp.cline.bot/v1
kind: MCPServer
metadata:
name: custom-api-server
namespace: cline-enterprise
spec:
image: company/custom-mcp-server:v1.0.0
replicas: 3
config:
api_endpoint: "https://api.internal.company.com"
timeout: 30s
retry_attempts: 3
auth:
type: service-account
service_account: mcp-custom-api
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
monitoring:
enabled: true
metrics_port: 9090
health_endpoint: "/health"
security:
network_policy: strict
pod_security_standard: restricted
```
### Configuration Management
Use Helm charts for enterprise MCP deployments:
```yaml
# values-prod.yaml
mcp:
hub:
replicaCount: 3
image:
repository: cline/mcp-hub-enterprise
tag: "1.5.2"
servers:
marketplace:
enabled: true
catalog_url: "https://enterprise-catalog.company.com"
custom:
- name: "salesforce"
enabled: true
image: "company/mcp-salesforce:1.0.0"
config:
instance_url: "https://company.my.salesforce.com"
- name: "jira"
enabled: true
image: "company/mcp-jira:2.1.0"
config:
base_url: "https://company.atlassian.net"
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: mcp.company.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: mcp-tls
hosts:
- mcp.company.com
```
## Security & Governance
### Authentication & Authorization
Configure enterprise authentication for MCP servers:
```yaml
# mcp-auth-config.yaml
authentication:
providers:
- name: "enterprise-sso"
type: "oidc"
issuer: "https://sso.company.com"
client_id: "${SSO_CLIENT_ID}"
client_secret: "${SSO_CLIENT_SECRET}"
- name: "service-accounts"
type: "jwt"
signing_key: "${SERVICE_ACCOUNT_KEY}"
authorization:
policies:
- name: "developers"
subjects: ["group:developers"]
resources: ["mcp:servers:read", "mcp:servers:execute"]
- name: "admins"
subjects: ["group:mcp-admins"]
resources: ["mcp:*"]
- name: "security-team"
subjects: ["group:security"]
resources: ["mcp:audit:*", "mcp:servers:security-*"]
rbac:
enabled: true
default_role: "viewer"
```
### Network Security
Implement network policies for MCP communications:
```yaml
# mcp-network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mcp-server-policy
namespace: cline-enterprise
spec:
podSelector:
matchLabels:
app: mcp-server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: cline-enterprise
- podSelector:
matchLabels:
app: cline-core
ports:
- protocol: TCP
port: 8080
egress:
# Allow DNS
- to: []
ports:
- protocol: UDP
port: 53
# Allow HTTPS to external APIs
- to: []
ports:
- protocol: TCP
port: 443
```
## Monitoring & Observability
### Metrics Collection
Configure comprehensive MCP monitoring:
```yaml
# mcp-monitoring.yaml
monitoring:
metrics:
enabled: true
interval: 30s
collectors:
- name: "server-health"
metrics:
- mcp_server_status
- mcp_server_response_time
- mcp_server_error_rate
- name: "hub-performance"
metrics:
- mcp_hub_requests_total
- mcp_hub_request_duration
- mcp_hub_active_connections
- name: "resource-usage"
metrics:
- mcp_memory_usage
- mcp_cpu_usage
- mcp_network_io
alerts:
- name: "server-down"
condition: "mcp_server_status == 0"
severity: "critical"
notification_channels: ["pagerduty", "slack"]
- name: "high-error-rate"
condition: "mcp_server_error_rate > 0.05"
severity: "warning"
notification_channels: ["slack"]
- name: "performance-degradation"
condition: "mcp_server_response_time > 5s"
severity: "warning"
notification_channels: ["email"]
```
### Audit Logging
Implement comprehensive audit trails:
```json
{
"timestamp": "2024-01-15T10:30:00Z",
"event_type": "mcp_server_call",
"user_id": "john.doe@company.com",
"session_id": "sess_abc123",
"server_name": "github-prod",
"method": "github.create_issue",
"request": {
"repository": "company/project",
"title": "Bug fix required",
"sensitive_data_detected": false
},
"response": {
"status": "success",
"issue_id": "12345",
"duration_ms": 234
},
"compliance": {
"data_classification": "internal",
"retention_required": true,
"pii_detected": false
}
}
```
## Custom MCP Server Development
### Development Framework
Create custom MCP servers using the enterprise SDK:
```typescript
// custom-mcp-server.ts
import { MCPServer, Tool, Resource } from '@cline/mcp-enterprise-sdk';
class CustomAPIServer extends MCPServer {
constructor() {
super({
name: 'custom-api-server',
version: '1.0.0',
description: 'Custom API integration server'
});
this.addTool(new DatabaseQueryTool());
this.addResource(new UserDataResource());
}
}
class DatabaseQueryTool implements Tool {
name = 'query_database';
description = 'Query the company database';
async execute(params: any) {
// Implement database query logic
const result = await this.database.query(params.sql);
// Audit log the query
await this.auditLog({
action: 'database_query',
query: params.sql,
user: params.user_id,
results_count: result.length
});
return result;
}
async validate(params: any): Promise<boolean> {
// Implement query validation
return params.sql && !this.containsMaliciousSQL(params.sql);
}
}
```
### Deployment Pipeline
Automate MCP server deployments:
```yaml
# .github/workflows/deploy-mcp-server.yml
name: Deploy MCP Server
on:
push:
branches: [main]
paths: ['mcp-servers/**']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build MCP Server
run: |
docker build -t company/mcp-server:${{ github.sha }} .
docker push company/mcp-server:${{ github.sha }}
- name: Deploy to Staging
run: |
helm upgrade mcp-server-staging ./helm-chart \
--set image.tag=${{ github.sha }} \
--namespace mcp-staging
- name: Run Integration Tests
run: |
kubectl wait --for=condition=ready pod -l app=mcp-server -n mcp-staging
npm run test:integration
- name: Deploy to Production
if: success()
run: |
helm upgrade mcp-server-prod ./helm-chart \
--set image.tag=${{ github.sha }} \
--namespace mcp-prod
```
## Best Practices
### Security
1. **Authentication**: Always require authentication for MCP servers
2. **Encryption**: Use TLS for all MCP communications
3. **Validation**: Validate all inputs and sanitize outputs
4. **Least Privilege**: Grant minimal required permissions
5. **Audit**: Log all MCP server interactions
### Performance
1. **Caching**: Implement response caching where appropriate
2. **Connection Pooling**: Reuse connections to external services
3. **Async Operations**: Use non-blocking operations for I/O
4. **Resource Limits**: Set appropriate CPU and memory limits
5. **Load Balancing**: Scale MCP servers based on demand
### Reliability
1. **Health Checks**: Implement comprehensive health endpoints
2. **Circuit Breakers**: Fail fast when external services are down
3. **Retry Logic**: Implement exponential backoff for failures
4. **Graceful Degradation**: Provide fallback behavior
5. **Monitoring**: Set up proactive alerting and monitoring
## Production Checklist
Before deploying MCP servers to production:
- [ ] Security review completed
- [ ] Authentication and authorization configured
- [ ] Network policies implemented
- [ ] Monitoring and alerting set up
- [ ] Audit logging enabled
- [ ] Resource limits configured
- [ ] Health checks implemented
- [ ] Integration tests passing
- [ ] Disaster recovery plan documented
- [ ] Compliance requirements validated
## Getting Started
Ready to implement enterprise MCP integration? Start with:
1. [MCP Marketplace](/enterprise-solutions/configuration/infrastructure-configuration/mcp/mcp-marketplace) - Deploy pre-built integrations
2. [Remote MCP Servers](/enterprise-solutions/configuration/infrastructure-configuration/mcp/remote-mcp-servers) - Configure custom servers
3. Review our [MCP Development Guide](/mcp/mcp-overview) for building custom integrations
@@ -1,95 +0,0 @@
---
title: "Self-Hosted Configuration"
sidebarTitle: "Overview"
description: "Deploy and configure Cline on your own infrastructure with enterprise-grade security and compliance"
---
<Warning>
**Self-Hosted Configuration Path**
This section is for enterprises deploying **self-hosted Cline infrastructure** with complex security, compliance, and multi-environment requirements. Configuration is done through YAML files, Kubernetes/Helm deployments, and infrastructure-as-code.
**Looking for simple setup?** See [SaaS Provider Configuration](/enterprise-solutions/configuration/remote-configuration/overview) for quick configuration through the app.cline.bot admin console - no infrastructure deployment required, just web-based settings.
</Warning>
Self-Hosted Configuration provides centralized control over all aspects of your Cline deployment on your own infrastructure, from AI providers to custom workflows. This section covers how to configure, manage, and optimize your enterprise Cline installation with advanced security, compliance, and operational features.
## Configuration Categories
<CardGroup cols={2}>
<Card title="Providers" icon="cloud" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/overview">
Configure AI providers including AWS Bedrock, LiteLLM, and Google Vertex AI with enterprise-grade security and governance.
</Card>
<Card title="MCP Integration" icon="plug" href="/enterprise-solutions/configuration/infrastructure-configuration/mcp/overview">
Manage Model Context Protocol servers, marketplace integrations, and remote MCP server configurations.
</Card>
<Card title="Rules Engine" icon="shield-check" href="/enterprise-solutions/configuration/infrastructure-configuration/rules">
Define and enforce enterprise governance rules, security policies, and compliance requirements.
</Card>
<Card title="Workflows" icon="workflow" href="/enterprise-solutions/configuration/infrastructure-configuration/workflows">
Create automated workflows for development processes, approval chains, and integration pipelines.
</Card>
</CardGroup>
## Advanced Controls
<CardGroup cols={2}>
<Card title="Control Other Cline Features" icon="toggles" href="/enterprise-solutions/configuration/infrastructure-configuration/control-other-cline-features/overview">
Enable or disable specific Cline features across your organization with granular permission controls.
</Card>
<Card title="Monitoring" icon="chart-line" href="/enterprise-solutions/monitoring/overview">
Configure OpenTelemetry integration for comprehensive monitoring, logging, and analytics.
</Card>
</CardGroup>
## Getting Started
1. **Assessment**: Review your current infrastructure and integration requirements
2. **Provider Setup**: Configure your preferred AI providers with enterprise credentials
3. **Security Configuration**: Implement rules and access controls
4. **Monitoring Setup**: Enable telemetry and monitoring for operational visibility
5. **User Onboarding**: Deploy configurations to your development teams
## Enterprise Architecture Considerations
### Security & Compliance
- **Zero Trust Architecture**: All configurations support zero-trust security models
- **Audit Logging**: Complete audit trails for all configuration changes
- **Role-Based Access**: Granular permissions for different administrative roles
- **Data Sovereignty**: Keep sensitive data within your infrastructure boundaries
### Scalability & Performance
- **Multi-Region Support**: Deploy configurations across multiple geographic regions
- **Load Balancing**: Distribute AI provider requests across multiple endpoints
- **Caching Strategies**: Optimize performance with intelligent caching
- **Rate Limiting**: Prevent abuse with configurable rate limits
### Integration & Automation
- **GitOps Integration**: Version control your configurations alongside code
- **CI/CD Pipeline Integration**: Automate configuration deployment
- **Webhook Support**: React to configuration changes with custom automation
- **API-First Design**: Programmatically manage all configurations
## Configuration Management
All enterprise configurations support:
- **Version Control**: Track changes with full revision history
- **Environment Promotion**: Deploy configurations from dev → staging → production
- **Rollback Capabilities**: Quickly revert problematic configurations
- **Configuration Validation**: Automated testing of configuration changes
- **Drift Detection**: Monitor and alert on configuration drift
## Next Steps
Ready to configure your enterprise deployment? Start with:
1. [Provider Configuration](/enterprise-solutions/configuration/infrastructure-configuration/providers/overview) - Set up your AI providers
2. [Security Rules](/enterprise-solutions/configuration/infrastructure-configuration/rules) - Implement governance policies
3. [Monitoring Setup](/enterprise-solutions/monitoring/overview) - Enable operational visibility
For hands-on configuration assistance, contact your Cline Enterprise support team or refer to our implementation guides.
@@ -1,182 +0,0 @@
---
title: "AWS Bedrock Configuration"
sidebarTitle: "AWS Bedrock"
description: "Configure AWS Bedrock for your Cline deployment"
---
<Info>
**Configuration Path: Self-Hosted**
This guide covers Bedrock configuration for self-hosted deployments. For simple web-based setup, see [AWS Bedrock SaaS Configuration](/enterprise-solutions/configuration/remote-configuration/aws-bedrock/admin-configuration).
</Info>
Configure Cline to use AWS Bedrock for enterprise access to Claude and other foundation models through Amazon's managed service.
## Configuration Format
Configure Bedrock through your remote configuration JSON using the `providerSettings.AwsBedrock` section:
```json
{
"providerSettings": {
"AwsBedrock": {
"models": [
{
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
"name": "Claude 3.5 Sonnet"
}
],
"awsRegion": "us-east-1"
}
}
}
```
## Configuration Fields
| Field | Type | Description | Required |
|-------|------|-------------|----------|
| `models` | Array | List of model configurations | Yes |
| `awsRegion` | String | AWS region (e.g., `us-east-1`) | Yes |
| `awsUseCrossRegionInference` | Boolean | Enable cross-region inference | No |
| `awsUseGlobalInference` | Boolean | Enable global inference routing | No |
| `awsBedrockUsePromptCache` | Boolean | Enable prompt caching | No |
| `awsBedrockEndpoint` | String | Custom Bedrock endpoint URL | No |
| `customModels` | Array | Custom model configurations | No |
### Model Configuration
Each model in the `models` array requires:
```json
{
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
"name": "Claude 3.5 Sonnet",
"info": {
"maxTokens": 8192,
"contextWindow": 200000,
"supportsImages": true,
"supportsPromptCache": true
}
}
```
## Common Model IDs
| Model ID | Description | Context Window |
|----------|-------------|----------------|
| `anthropic.claude-3-5-sonnet-20241022-v2:0` | Latest Claude Sonnet | 200K tokens |
| `anthropic.claude-3-5-haiku-20241022-v1:0` | Latest Claude Haiku | 200K tokens |
| `anthropic.claude-3-opus-20240229-v1:0` | Claude Opus | 200K tokens |
<Note>
Model availability varies by region. See [AWS Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/models-regions.html) for region-specific model availability.
</Note>
## Example Configurations
### Basic Configuration
```json
{
"providerSettings": {
"AwsBedrock": {
"models": [
{
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
"name": "Claude 3.5 Sonnet"
}
],
"awsRegion": "us-east-1"
}
}
}
```
### With Prompt Caching
```json
{
"providerSettings": {
"AwsBedrock": {
"models": [
{
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
"name": "Claude 3.5 Sonnet"
}
],
"awsRegion": "us-east-1",
"awsBedrockUsePromptCache": true
}
}
}
```
### Multiple Models
```json
{
"providerSettings": {
"AwsBedrock": {
"models": [
{
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
"name": "Claude 3.5 Sonnet"
},
{
"id": "anthropic.claude-3-5-haiku-20241022-v1:0",
"name": "Claude 3.5 Haiku"
}
],
"awsRegion": "us-east-1"
}
}
}
```
## Prerequisites
Before configuring Cline to use Bedrock, you need:
1. **AWS Account** with Bedrock access enabled
2. **IAM Permissions** for Bedrock API calls (`bedrock:InvokeModel`, `bedrock:InvokeModelWithResponseStream`)
3. **Model Access** enabled for desired models in the Bedrock console
4. **AWS Credentials** configured (IAM role, access keys, or AWS profile)
<Tip>
For AWS account setup and IAM configuration, see the [AWS Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html).
</Tip>
## Troubleshooting
**"Access Denied" Errors**
Ensure your AWS credentials have the required Bedrock permissions. See [AWS IAM documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html) for permission requirements.
**"Model Not Found" Errors**
Verify model access is enabled in the AWS Bedrock console and the model is available in your configured region.
**High Latency**
Consider using a region closer to your users or enabling cross-region inference for better performance.
## Related Resources
<CardGroup cols={2}>
<Card title="AWS Bedrock Docs" icon="book" href="https://docs.aws.amazon.com/bedrock/">
Complete AWS Bedrock documentation
</Card>
<Card title="Model Access" icon="key" href="https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html">
How to enable model access
</Card>
<Card title="IAM Permissions" icon="shield" href="https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html">
Required IAM permissions
</Card>
<Card title="Pricing" icon="dollar-sign" href="https://aws.amazon.com/bedrock/pricing/">
AWS Bedrock pricing details
</Card>
</CardGroup>
@@ -1,254 +0,0 @@
---
title: "Custom Provider Configuration"
sidebarTitle: "Custom Providers"
description: "Configure custom OpenAI-compatible providers for your Cline deployment"
---
<Info>
**Configuration Path: Self-Hosted**
This guide covers custom provider configuration for self-hosted deployments.
</Info>
Configure Cline to use any OpenAI-compatible API provider, including Azure OpenAI, self-hosted inference servers, and other third-party services.
## What are Custom Providers?
Custom providers include any API that implements the OpenAI API format:
- **Azure OpenAI Service**: Microsoft's managed OpenAI models
- **vLLM**: Self-hosted inference server
- **Ollama**: Local model runner
- **Text Generation Inference (TGI)**: Hugging Face's inference server
- **LocalAI**: Local OpenAI API replacement
- **Other OpenAI-compatible APIs**: Any custom implementation
## Configuration Format
Configure custom providers through your remote configuration JSON using the `providerSettings.OpenAiCompatible` section:
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "gpt-4-turbo",
"name": "GPT-4 Turbo"
}
],
"openAiBaseUrl": "https://your-api.company.com/v1"
}
}
}
```
## Configuration Fields
| Field | Type | Description | Required |
|-------|------|-------------|----------|
| `models` | Array | List of model configurations | Yes |
| `openAiBaseUrl` | String | API endpoint base URL | Yes |
| `openAiApiKey` | String | API key for authentication | No |
| `openAiModelId` | String | Default model identifier | No |
### Azure OpenAI Specific Fields
For Azure OpenAI, additional fields are available:
| Field | Type | Description |
|-------|------|-------------|
| `azureApiVersion` | String | Azure API version (e.g., `2024-02-15-preview`) |
## Example Configurations
### Azure OpenAI
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "gpt-4-turbo",
"name": "GPT-4 Turbo"
}
],
"openAiBaseUrl": "https://your-resource.openai.azure.com/openai/deployments/gpt-4-turbo",
"openAiApiKey": "your-azure-api-key",
"azureApiVersion": "2024-02-15-preview"
}
}
}
```
### Self-Hosted vLLM
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "meta-llama/Llama-2-70b-chat-hf",
"name": "Llama 2 70B"
}
],
"openAiBaseUrl": "http://vllm.company.com:8000/v1"
}
}
}
```
### Local Ollama
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "codellama",
"name": "Code Llama"
}
],
"openAiBaseUrl": "http://localhost:11434/v1"
}
}
}
```
### Text Generation Inference (TGI)
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "mistralai/Mistral-7B-Instruct-v0.2",
"name": "Mistral 7B Instruct"
}
],
"openAiBaseUrl": "http://tgi.company.com:8080/v1",
"openAiApiKey": "your-tgi-api-key"
}
}
}
```
### LocalAI
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "gpt-3.5-turbo",
"name": "Local GPT-3.5"
}
],
"openAiBaseUrl": "http://localhost:8080/v1"
}
}
}
```
### Internal Network (No Auth)
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "custom-model",
"name": "Custom Model"
}
],
"openAiBaseUrl": "http://internal.api:8000/v1"
}
}
}
```
## Model Configuration
Each model requires basic information:
```json
{
"id": "model-identifier",
"name": "Display Name",
"info": {
"maxTokens": 4096,
"contextWindow": 128000,
"supportsImages": true,
"supportsPromptCache": false
}
}
```
## Prerequisites
Before configuring a custom provider, you need:
1. **API Endpoint**: URL of your OpenAI-compatible API
2. **API Key** (if required): Authentication credentials
3. **Model IDs**: Names of available models
4. **Network Access**: Connectivity from where Cline is being used
## Troubleshooting
**Connection Errors**
Verify the endpoint is accessible:
```bash
curl https://your-api.company.com/v1/models
```
**Authentication Errors**
Test authentication with your API key:
```bash
curl -H "Authorization: Bearer your-api-key" \
https://your-api.company.com/v1/models
```
**Model Not Found**
Ensure the model ID in your configuration matches what the API expects. Check available models:
```bash
curl -H "Authorization: Bearer your-api-key" \
https://your-api.company.com/v1/models
```
**Timeout Issues**
If responses are slow:
- Check network latency
- Verify server has adequate resources
- Consider using faster models
## Provider Documentation
For setup and deployment of these services, see their official documentation:
<CardGroup cols={2}>
<Card title="Azure OpenAI" icon="microsoft" href="https://learn.microsoft.com/en-us/azure/ai-services/openai/">
Microsoft's managed OpenAI service
</Card>
<Card title="vLLM" icon="server" href="https://docs.vllm.ai/">
High-performance inference engine
</Card>
<Card title="Ollama" icon="download" href="https://ollama.ai/">
Run models locally
</Card>
<Card title="Text Generation Inference" icon="code" href="https://huggingface.co/docs/text-generation-inference/">
Hugging Face inference server
</Card>
</CardGroup>
@@ -1,185 +0,0 @@
---
title: "Google Vertex AI Configuration"
sidebarTitle: "Google Vertex"
description: "Configure Google Vertex AI for your Cline deployment"
---
<Info>
**Configuration Path: Self-Hosted**
This guide covers Vertex AI configuration for self-hosted deployments. For simple web-based setup, see [Google Vertex SaaS Configuration](/enterprise-solutions/configuration/remote-configuration/google-vertex/admin-configuration).
</Info>
Configure Cline to use Google Vertex AI for enterprise access to Gemini and other Google AI models through Google Cloud Platform.
## Configuration Format
Configure Vertex AI through your remote configuration JSON using the `providerSettings.Vertex` section:
```json
{
"providerSettings": {
"Vertex": {
"models": [
{
"id": "claude-3-5-sonnet-v2@20241022",
"name": "Claude 3.5 Sonnet"
}
],
"vertexProjectId": "my-project-id",
"vertexRegion": "us-central1"
}
}
}
```
## Configuration Fields
| Field | Type | Description | Required |
|-------|------|-------------|----------|
| `models` | Array | List of model configurations | Yes |
| `vertexProjectId` | String | Google Cloud project ID | Yes |
| `vertexRegion` | String | GCP region (e.g., `us-central1`) | Yes |
### Model Configuration
Each model in the `models` array requires:
```json
{
"id": "claude-3-5-sonnet-v2@20241022",
"name": "Claude 3.5 Sonnet",
"info": {
"maxTokens": 8192,
"contextWindow": 200000,
"supportsImages": true,
"supportsPromptCache": true
}
}
```
## Common Model IDs
| Model ID | Description | Context Window |
|----------|-------------|----------------|
| `claude-3-5-sonnet-v2@20241022` | Claude 3.5 Sonnet | 200K tokens |
| `claude-3-5-haiku@20241022` | Claude 3.5 Haiku | 200K tokens |
| `claude-3-opus@20240229` | Claude 3 Opus | 200K tokens |
| `gemini-2.0-flash-exp` | Gemini Flash (experimental) | 1M tokens |
| `gemini-1.5-pro-002` | Gemini Pro | 2M tokens |
| `gemini-1.5-flash-002` | Gemini Flash | 1M tokens |
<Note>
Model availability varies by region. See [Vertex AI documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/learn/models) for region-specific model availability.
</Note>
## Example Configurations
### Basic Configuration
```json
{
"providerSettings": {
"Vertex": {
"models": [
{
"id": "claude-3-5-sonnet-v2@20241022",
"name": "Claude 3.5 Sonnet"
}
],
"vertexProjectId": "my-company-prod",
"vertexRegion": "us-central1"
}
}
}
```
### Multiple Models
```json
{
"providerSettings": {
"Vertex": {
"models": [
{
"id": "claude-3-5-sonnet-v2@20241022",
"name": "Claude 3.5 Sonnet"
},
{
"id": "gemini-1.5-pro-002",
"name": "Gemini Pro"
}
],
"vertexProjectId": "my-company-prod",
"vertexRegion": "us-central1"
}
}
}
```
### With Extended Thinking
```json
{
"providerSettings": {
"Vertex": {
"models": [
{
"id": "claude-3-5-sonnet-v2@20241022",
"name": "Claude 3.5 Sonnet",
"thinkingBudgetTokens": 1600
}
],
"vertexProjectId": "my-company-prod",
"vertexRegion": "us-central1"
}
}
}
```
## Prerequisites
Before configuring Cline to use Vertex AI, you need:
1. **Google Cloud Project** with Vertex AI API enabled
2. **Service Account** with Vertex AI User role (`roles/aiplatform.user`)
3. **Service Account Credentials** configured for authentication
4. **Model Access** verified in your project and region
<Tip>
For Google Cloud setup and authentication configuration, see the [Vertex AI documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/quickstart-multimodal).
</Tip>
## Troubleshooting
**"Permission Denied" Errors**
Ensure your service account has the required Vertex AI permissions. See [Google Cloud IAM documentation](https://cloud.google.com/vertex-ai/docs/general/access-control) for permission requirements.
**"API Not Enabled" Errors**
Verify the Vertex AI API is enabled in your Google Cloud project.
**"Model Not Found" Errors**
Check that the model is available in your configured region and that your project has access to it.
## Related Resources
<CardGroup cols={2}>
<Card title="Vertex AI Docs" icon="book" href="https://cloud.google.com/vertex-ai/docs">
Complete Vertex AI documentation
</Card>
<Card title="Service Accounts" icon="key" href="https://cloud.google.com/iam/docs/service-accounts">
Service account best practices
</Card>
<Card title="Model Guide" icon="brain" href="https://cloud.google.com/vertex-ai/docs/generative-ai/learn/models">
Available models and features
</Card>
<Card title="Pricing" icon="dollar-sign" href="https://cloud.google.com/vertex-ai/pricing">
Vertex AI pricing details
</Card>
</CardGroup>
@@ -1,215 +0,0 @@
---
title: "LiteLLM Configuration"
sidebarTitle: "LiteLLM"
description: "Configure LiteLLM proxy for your Cline deployment"
---
<Info>
**Configuration Path: Self-Hosted**
This guide covers LiteLLM configuration for self-hosted deployments. For web-based setup, see [LiteLLM SaaS Configuration](/enterprise-solutions/configuration/remote-configuration/litellm/admin-configuration).
</Info>
Configure Cline to use an existing LiteLLM proxy for unified access to multiple AI models through a single API endpoint.
## What is LiteLLM?
[LiteLLM](https://github.com/BerriAI/litellm) is an open-source proxy that provides a unified OpenAI-compatible API for accessing 100+ AI models from different providers. Cline connects to your deployed LiteLLM instance.
<Note>
LiteLLM is a separate service you deploy and manage. This guide covers how to configure Cline to connect to an existing LiteLLM deployment.
</Note>
## Configuration Format
Configure LiteLLM through your remote configuration JSON using the `providerSettings.OpenAiCompatible` section:
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "gpt-4-turbo",
"name": "GPT-4 Turbo"
}
],
"openAiBaseUrl": "https://litellm.yourcompany.com/v1"
}
}
}
```
## Configuration Fields
| Field | Type | Description | Required |
|-------|------|-------------|----------|
| `models` | Array | List of model configurations | Yes |
| `openAiBaseUrl` | String | LiteLLM proxy endpoint URL | Yes |
| `openAiApiKey` | String | API key for authentication | No |
### Model Configuration
Each model in the `models` array requires:
```json
{
"id": "gpt-4-turbo",
"name": "GPT-4 Turbo",
"info": {
"maxTokens": 4096,
"contextWindow": 128000,
"supportsImages": true
}
}
```
<Note>
Model IDs must match the model names configured in your LiteLLM proxy deployment.
</Note>
## Example Configurations
### Basic Configuration
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "gpt-4-turbo",
"name": "GPT-4 Turbo"
}
],
"openAiBaseUrl": "https://litellm.company.com/v1"
}
}
}
```
### With Authentication
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "gpt-4-turbo",
"name": "GPT-4 Turbo"
}
],
"openAiBaseUrl": "https://litellm.company.com/v1",
"openAiApiKey": "sk-your-litellm-key"
}
}
}
```
### Multiple Models
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "gpt-4-turbo",
"name": "GPT-4 Turbo"
},
{
"id": "claude-3-5-sonnet",
"name": "Claude 3.5 Sonnet"
},
{
"id": "gemini-pro",
"name": "Gemini Pro"
}
],
"openAiBaseUrl": "https://litellm.company.com/v1",
"openAiApiKey": "sk-your-litellm-key"
}
}
}
```
### Internal Network (No Auth)
```json
{
"providerSettings": {
"OpenAiCompatible": {
"models": [
{
"id": "gpt-4-turbo",
"name": "GPT-4 Turbo"
}
],
"openAiBaseUrl": "http://litellm.internal:4000/v1"
}
}
}
```
## Prerequisites
Before configuring Cline to use LiteLLM, you need:
1. **LiteLLM Proxy** deployed and accessible
2. **LiteLLM Configuration** with desired models enabled
3. **API Key** (if authentication is enabled)
4. **Network Access** from where Cline is being used
<Tip>
For LiteLLM deployment and configuration, see the [LiteLLM documentation](https://docs.litellm.ai/docs/proxy/quick_start).
</Tip>
## Troubleshooting
**Connection Errors**
Verify the LiteLLM proxy is running and accessible:
```bash
curl https://litellm.yourcompany.com/health
```
**Authentication Errors**
Check your API key is valid:
```bash
curl -H "Authorization: Bearer sk-your-key" \
https://litellm.yourcompany.com/v1/models
```
**Model Not Found**
Verify the model is configured in your LiteLLM deployment. Model IDs in Cline's config must match the model names in LiteLLM's configuration.
## Benefits of Using LiteLLM
- **Multi-Provider Access**: Connect to multiple AI providers through one endpoint
- **Load Balancing**: Distribute requests across providers automatically
- **Fallback Support**: Automatic retry with different models on failure
- **Cost Tracking**: Monitor usage and costs across all models
- **Rate Limiting**: Control usage at the proxy level
## Related Resources
<CardGroup cols={2}>
<Card title="LiteLLM Docs" icon="book" href="https://docs.litellm.ai/">
Complete LiteLLM documentation
</Card>
<Card title="LiteLLM GitHub" icon="github" href="https://github.com/BerriAI/litellm">
Source code and deployment examples
</Card>
<Card title="Proxy Setup" icon="server" href="https://docs.litellm.ai/docs/proxy/quick_start">
LiteLLM proxy deployment guide
</Card>
<Card title="Supported Providers" icon="list" href="https://docs.litellm.ai/docs/providers">
List of supported AI providers
</Card>
</CardGroup>
@@ -1,144 +0,0 @@
---
title: "AI Provider Configuration"
sidebarTitle: "Overview"
description: "Configure AI provider settings for your Cline deployment"
---
<Info>
**Configuration Path: Self-Hosted**
This section covers provider configuration for self-hosted deployments. For web-based configuration through app.cline.bot, see [SaaS Provider Configuration](/enterprise-solutions/configuration/remote-configuration/overview).
</Info>
Configure which AI providers your team can use and manage provider credentials centrally. Cline supports major AI providers with enterprise-grade authentication options.
## Supported Providers
<CardGroup cols={2}>
<Card title="AWS Bedrock" icon="aws" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/aws-bedrock">
Amazon's managed service for Claude and other foundation models
</Card>
<Card title="Google Vertex AI" icon="google" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/google-vertex">
Google Cloud's AI platform with Gemini and PaLM models
</Card>
<Card title="LiteLLM" icon="zap" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/litellm">
Universal proxy for accessing 100+ AI models through a unified API
</Card>
<Card title="Custom Providers" icon="plug" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/custom">
OpenAI-compatible APIs and self-hosted models
</Card>
</CardGroup>
## What is Provider Configuration?
Provider configuration in Cline allows administrators to:
1. **Manage Credentials Centrally**: Store API keys and authentication details in one place
2. **Control Model Access**: Specify which models teams can use
3. **Enforce Provider Usage**: Direct all team members to approved providers
## How It Works
Provider settings are configured through your remote configuration JSON file:
```json
{
"providerSettings": {
"provider": "bedrock",
"bedrockRegion": "us-east-1",
"bedrockServiceRole": "arn:aws:iam::..."
}
}
```
When configured, these settings:
- Apply to all team members automatically
- Override individual user settings
- Ensure consistent provider usage across the team
## Configuration Options
### Provider Selection
Choose from supported providers:
- **bedrock**: Use AWS Bedrock
- **vertex**: Use Google Vertex AI
- **openai**: Use OpenAI API
- **azure**: Use Azure OpenAI
- **litellm**: Use a LiteLLM proxy
### Authentication
Each provider supports different authentication methods:
**AWS Bedrock:**
- IAM roles with cross-account access
- Access keys (not recommended for production)
**Google Vertex AI:**
- Service account JSON keys
- Workload Identity (for GKE deployments)
**OpenAI/Azure:**
- API keys
**LiteLLM:**
- Endpoint URL + API key
## Example Configurations
### AWS Bedrock with IAM Role
```json
{
"providerSettings": {
"provider": "bedrock",
"bedrockRegion": "us-east-1",
"bedrockServiceRole": "arn:aws:iam::123456789012:role/ClineBedrockRole"
}
}
```
### Google Vertex AI
```json
{
"providerSettings": {
"provider": "vertex",
"vertexProject": "my-project-id",
"vertexRegion": "us-central1"
}
}
```
### LiteLLM Proxy
```json
{
"providerSettings": {
"provider": "litellm",
"litellmBaseUrl": "https://litellm.company.com",
"litellmApiKey": "sk-..."
}
}
```
## Next Steps
<CardGroup cols={2}>
<Card title="Configure AWS Bedrock" icon="aws" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/aws-bedrock">
Set up AWS Bedrock integration
</Card>
<Card title="Configure Google Vertex" icon="google" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/google-vertex">
Set up Google Vertex AI integration
</Card>
<Card title="Configure LiteLLM" icon="zap" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/litellm">
Set up LiteLLM proxy integration
</Card>
<Card title="Configure Custom Provider" icon="plug" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/custom">
Set up custom OpenAI-compatible provider
</Card>
</CardGroup>
@@ -1,239 +0,0 @@
---
title: "Rules"
sidebarTitle: "Rules"
description: "Custom instruction files that guide Cline's behavior in your enterprise deployment"
---
Rules are custom instruction files that provide Cline with guidelines about your coding preferences, standards, and best practices. These instructions get added to Cline's context when working on tasks.
## What are Rules?
Rules are simple markdown files stored in a `.clinerules/` directory that contain your team's conventions, preferences, and guidelines. They help Cline understand your:
- Coding style and conventions
- Preferred libraries and frameworks
- Architectural patterns
- Testing strategies
- Documentation standards
- Communication preferences
<Tip>
Rules are just `.md` files - no complex configuration needed!
</Tip>
## Quick Example
Here's a simple rule file that guides TypeScript development:
```markdown
# TypeScript Conventions
## Code Style
- Use 2-space indentation
- Prefer `const` over `let`
- Always use explicit return types for functions
- Use named exports instead of default exports
## Testing
- Write unit tests for all utility functions
- Use Vitest as the testing framework
- Aim for 80%+ code coverage
## Dependencies
- Prefer native TypeScript features over external libraries
- Use Zod for runtime type validation
- Use date-fns for date manipulation
```
## Creating Rules
<Tabs>
<Tab title="Using /newrule Command">
The easiest way to create a rule is with the `/newrule` command:
1. During a conversation with Cline, type `/newrule`
2. Cline will analyze your conversation and preferences
3. It creates an appropriately named `.md` file in `.clinerules/`
**Example:**
```
/newrule
Based on our conversation, create a rule for React component structure
```
</Tab>
<Tab title="Manual Creation">
You can also create rule files manually:
1. Create a `.clinerules/` directory in your repository root
2. Add markdown files with your guidelines
3. Use descriptive names like `react-patterns.md` or `api-conventions.md`
**File structure:**
```
your-repo/
├── .clinerules/
│ ├── typescript-style.md
│ ├── testing-standards.md
│ └── code-review-checklist.md
└── src/
```
</Tab>
</Tabs>
## Global vs Workspace Rules
<CardGroup cols={2}>
<Card title="Workspace Rules" icon="folder">
**Location:** `.clinerules/` in your repository
**Scope:** Specific to that project
**Use for:** Project-specific conventions and patterns
</Card>
<Card title="Global Rules" icon="globe">
**Location:** `Documents/Cline/` directory
**Scope:** All your projects
**Use for:** Personal preferences that apply everywhere
</Card>
</CardGroup>
## Managing Rules
### Toggling Rules
You can enable or disable individual rule files:
1. Click the rules icon in Cline's interface
2. Toggle rules on/off as needed
3. Changes apply immediately to new tasks
<Note>
Disabling a rule removes it from Cline's context, but keeps the file intact. You can re-enable it anytime.
</Note>
### Enterprise Remote Rules
<Info>
Enterprise deployments can configure **remote global rules** that apply to all team members. These are managed through your infrastructure configuration and cannot be toggled off by individual developers.
See [Self-Hosted Configuration](/enterprise-solutions/configuration/infrastructure-configuration/overview) for details on remote rules.
</Info>
## Compatible Formats
Cline also respects rules from other AI coding tools:
| File/Directory | Tool | Location |
|----------------|------|----------|
| `.cursorrules` | Cursor | Workspace root (single file) |
| `.cursor/rules/` | Cursor | Workspace directory (`.mdc` files) |
| `.windsurfrules` | Windsurf | Workspace root (single file) |
| `AGENTS.md` | Various | Workspace root + recursive search |
<Note>
**AGENTS.md behavior:** Cline only searches for nested `AGENTS.md` files recursively if a top-level `AGENTS.md` exists in your workspace root. If found, all `AGENTS.md` files are combined with their relative paths as headers.
</Note>
These files work the same way as `.clinerules/` files and can be toggled on/off independently.
## Best Practices
<AccordionGroup>
<Accordion title="Keep Rules Focused" icon="bullseye">
Each rule file should focus on one topic:
- ✅ `typescript-conventions.md`
- ✅ `react-component-structure.md`
- ❌ `everything-about-our-codebase.md`
</Accordion>
<Accordion title="Be Specific, Not Generic" icon="crosshairs">
Base rules on actual team preferences, not assumptions:
- ✅ "We use React Query for server state management"
- ❌ "Use best practices for state management"
</Accordion>
<Accordion title="Update Rules as Projects Evolve" icon="rotate">
Review and update rules periodically:
- When adopting new technologies
- After major architectural changes
- When team conventions evolve
</Accordion>
<Accordion title="Don't Overdo It" icon="gauge-simple-high">
Too many rules can overwhelm Cline's context:
- Start with 3-5 essential rules
- Add more only when truly needed
- Remove outdated rules promptly
</Accordion>
</AccordionGroup>
## Example Rule Files
<AccordionGroup>
<Accordion title="API Design Standards" icon="code">
```markdown
# API Design Standards
## REST Conventions
- Use plural nouns for endpoints (`/users`, not `/user`)
- Use HTTP methods semantically (GET, POST, PUT, DELETE)
- Return appropriate status codes
## Response Format
\`\`\`typescript
{
data: T,
error?: string,
metadata?: {
page: number,
total: number
}
}
\`\`\`
## Error Handling
- Always return error messages in `error` field
- Use 4xx for client errors, 5xx for server errors
- Include request ID in error responses
```
</Accordion>
<Accordion title="Testing Requirements" icon="vial">
```markdown
# Testing Requirements
## Test Organization
- Place tests next to source files (`Button.test.tsx`)
- Use `describe` blocks to group related tests
- Write descriptive test names
## Coverage Requirements
- Unit tests for all utility functions
- Integration tests for API endpoints
- E2E tests for critical user flows
- Minimum 80% coverage for new code
## Mocking Strategy
- Mock external API calls
- Use test fixtures for complex data
- Prefer dependency injection for testability
```
</Accordion>
</AccordionGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Workflows" icon="diagram-project" href="/enterprise-solutions/configuration/infrastructure-configuration/workflows">
Combine rules with automated workflows
</Card>
<Card title="Remote Configuration" icon="cloud" href="/enterprise-solutions/configuration/remote-configuration/overview">
Deploy global rules for your team
</Card>
</CardGroup>
@@ -1,324 +0,0 @@
---
title: "Workflows"
sidebarTitle: "Workflows"
description: "Reusable instruction sets that can be invoked on-demand via slash commands"
---
Workflows are markdown files containing reusable instructions that you can invoke on-demand using slash commands. Think of them as "rules you can call when needed" rather than always-active guidelines.
## What are Workflows?
Workflows are similar to [Rules](/enterprise-solutions/configuration/infrastructure-configuration/rules), but with one key difference:
<CardGroup cols={2}>
<Card title="Rules" icon="book">
**Always Active**
Automatically applied to every task when toggled on
Example: Coding standards, style guides
</Card>
<Card title="Workflows" icon="diagram-project">
**On-Demand**
Invoked only when you use the slash command
Example: Deployment checklists, review processes
</Card>
</CardGroup>
<Tip>
Workflows are just markdown files, no complex configuration needed!
</Tip>
## Quick Example
Here's a simple deployment workflow:
**File:** `.clinerules/workflows/deploy.md`
```markdown
# Deployment Workflow
Before deploying to production, ensure:
## Pre-Deployment Checklist
1. All tests passing (unit, integration, e2e)
2. Code review approved by 2+ engineers
3. Staging environment tested successfully
4. Database migrations reviewed
5. Rollback plan documented
## Deployment Steps
1. Create deployment branch from main
2. Run final test suite
3. Deploy to production
4. Monitor error rates for 30 minutes
5. Verify key user flows
## Post-Deployment
1. Update deployment log
2. Notify team in #deployments channel
3. Monitor metrics for 24 hours
```
**Usage:**
```
/deploy
I'm ready to deploy the new authentication feature
```
When invoked, Cline adds the workflow instructions to its context for that specific task.
## Creating Workflows
<Tabs>
<Tab title="Manual Creation">
Create workflow files in the `.clinerules/workflows/` directory:
1. Create `.clinerules/workflows/` in your repository root
2. Add markdown files with your workflow instructions
3. Use descriptive names matching your slash command
**File structure:**
```
your-repo/
├── .clinerules/
│ └── workflows/
│ ├── deploy.md
│ ├── code-review.md
│ └── bug-triage.md
└── src/
```
</Tab>
<Tab title="Slash Command">
You can also create workflows during a conversation:
1. Have a conversation about a process you want to codify
2. Type `/newrule` and specify it should be a workflow
3. Cline creates the workflow file in `.clinerules/workflows/`
<Note>
The `/newrule` command can create both rules and workflows - just specify your intent clearly.
</Note>
</Tab>
</Tabs>
## Using Workflows
### Invoking Workflows
Simply type `/` followed by the workflow filename (without `.md`):
```
/deploy
/code-review
/bug-triage
```
The workflow instructions are added to Cline's context for the current task only.
### Workflow Naming
- Use lowercase with hyphens: `deploy.md`, `code-review.md`
- Keep names short and memorable
- Name should indicate the workflow's purpose
<Warning>
Workflow filenames become slash commands, so choose names that are easy to type and remember.
</Warning>
## Global vs Workspace Workflows
<CardGroup cols={2}>
<Card title="Workspace Workflows" icon="folder">
**Location:** `.clinerules/workflows/` in your repository
**Scope:** Specific to that project
**Use for:** Project-specific processes and checklists
</Card>
<Card title="Global Workflows" icon="globe">
**Location:** `Documents/Cline/Workflows/` directory
**Scope:** All your projects
**Use for:** Personal workflows that apply everywhere
</Card>
</CardGroup>
<Info>
**Precedence:** Local workflows override global workflows if they have the same name.
</Info>
## Managing Workflows
### Toggling Workflows
You can enable or disable workflows:
1. Click the rules icon in Cline's interface
2. Switch to the "Workflows" tab
3. Toggle workflows on/off as needed
<Note>
Disabling a workflow prevents it from being invoked, but keeps the file intact. The slash command won't work until you re-enable it.
</Note>
### Enterprise Remote Workflows
<Info>
Enterprise deployments can configure **remote global workflows** that are available to all team members. These are managed through your infrastructure configuration.
See [Self-Hosted Configuration](/enterprise-solutions/configuration/infrastructure-configuration/overview) for details on remote workflows.
</Info>
## Example Workflows
<AccordionGroup>
<Accordion title="Code Review Workflow" icon="code-review">
```markdown
# Code Review Workflow
## Pre-Review Checklist
- [ ] Code follows project style guide
- [ ] All tests pass locally
- [ ] No console.log or debugging code
- [ ] Comments explain "why" not "what"
- [ ] PR description is clear and complete
## Review Focus Areas
1. **Architecture**: Does this fit our existing patterns?
2. **Security**: Any potential vulnerabilities?
3. **Performance**: Any obvious bottlenecks?
4. **Testing**: Are edge cases covered?
5. **Documentation**: Is it clear how to use new features?
## Review Response
- Address all feedback within 24 hours
- Mark conversations as resolved when addressed
- Re-request review after major changes
```
</Accordion>
<Accordion title="Bug Triage Workflow" icon="bug">
```markdown
# Bug Triage Workflow
## Information Gathering
1. Reproduce the bug in local environment
2. Identify affected versions/environments
3. Check if similar issues exist
4. Gather error logs and stack traces
## Priority Assessment
**P0 (Critical)**: Production down, data loss, security breach
**P1 (High)**: Major feature broken, significant user impact
**P2 (Medium)**: Minor feature broken, workaround available
**P3 (Low)**: Cosmetic issue, minimal impact
## Create Ticket
- Use template: "Bug Report"
- Add reproduction steps
- Include screenshots/videos if applicable
- Tag with affected component
- Assign priority label
## Next Steps
- P0/P1: Immediate fix required
- P2: Schedule for current sprint
- P3: Add to backlog
```
</Accordion>
<Accordion title="Feature Planning Workflow" icon="lightbulb">
```markdown
# Feature Planning Workflow
## Requirements Gathering
1. Define the user problem we're solving
2. List success criteria (measurable)
3. Identify edge cases and constraints
4. Document technical dependencies
## Design Considerations
1. How does this fit existing architecture?
2. What data models are needed?
3. What API changes are required?
4. How will this impact performance?
## Implementation Plan
1. Break into smaller, shippable pieces
2. Identify which pieces can be done in parallel
3. Note any feature flags needed
4. Plan for backwards compatibility
## Testing Strategy
1. What unit tests are needed?
2. What integration tests are needed?
3. How will we test edge cases?
4. What manual testing is required?
```
</Accordion>
</AccordionGroup>
## Best Practices
<AccordionGroup>
<Accordion title="Keep Workflows Action-Oriented" icon="list-check">
Workflows should contain **actionable steps**, not general advice:
- ✅ "Run `npm test` and verify all tests pass"
- ❌ "Make sure testing is done properly"
</Accordion>
<Accordion title="Use Checklists" icon="square-check">
Format workflows as checklists when possible:
- Easy to follow step-by-step
- Clear progress tracking
- Reduces missed steps
</Accordion>
<Accordion title="Include Context" icon="circle-info">
Add **why** behind each step:
```markdown
1. Check staging environment first
(Catching issues in staging prevents production incidents)
```
</Accordion>
<Accordion title="Version as Code" icon="code-branch">
Workflows live in your repository:
- Track changes in git
- Review updates in PRs
- Maintain history of process evolution
</Accordion>
</AccordionGroup>
## Workflows vs Rules: When to Use Each
| Use Rules When | Use Workflows When |
|----------------|-------------------|
| Guidance should apply to every task | Process is invoked occasionally |
| Standards that rarely change | Checklist for specific scenarios |
| Always-on coding conventions | On-demand deployment processes |
| General coding style | Specific review procedures |
**Example:**
- **Rule**: "Use TypeScript strict mode and explicit return types"
- **Workflow**: "Follow these 10 steps when deploying to production"
## Next Steps
<CardGroup cols={2}>
<Card title="Rules" icon="book" href="/enterprise-solutions/configuration/infrastructure-configuration/rules">
Learn about always-active rules
</Card>
<Card title="Remote Configuration" icon="cloud" href="/enterprise-solutions/configuration/remote-configuration/overview">
Deploy global workflows for your team
</Card>
</CardGroup>
@@ -1,97 +0,0 @@
---
title: "Configuration Overview"
sidebarTitle: "Overview"
description: "Understanding enterprise configuration options for inference providers and system settings"
---
Cline offers two distinct approaches to configure inference providers and system settings for your organization. Understanding the difference between these approaches will help you choose the right configuration method for your needs.
## Configuration Types
<Info>
**Need help choosing?** See the [Deployment Guide](/enterprise-solutions/configuration/choosing-your-deployment) for a detailed comparison and decision tree.
</Info>
<CardGroup cols={2}>
<Card title="SaaS Provider Configuration" icon="cloud" href="/enterprise-solutions/configuration/remote-configuration/overview">
**Simple cloud-based setup**
Configure inference providers through the Cline [admin console](https://app.cline.bot/dashboard). Ideal for quick organizational deployment with minimal infrastructure requirements.
</Card>
<Card title="Self-Hosted Configuration" icon="server" href="/enterprise-solutions/configuration/infrastructure-configuration/overview">
**Advanced enterprise setup**
Deep infrastructure integration with VPC endpoints, multi-account support, compliance features, and custom workflows on your own infrastructure.
</Card>
</CardGroup>
## Choosing the Right Configuration
### Use SaaS Configuration When:
- **Quick Setup**: You need to get your team up and running quickly
- **Centralized Management**: You want simple, cloud-based provider management
- **Standard Requirements**: Your organization has typical security and compliance needs
- **Small to Medium Teams**: You're managing dozens to hundreds of users
### Use Self-Hosted Configuration When:
- **Enterprise Security**: You need advanced security features and compliance controls
- **Complex Infrastructure**: You have existing AWS/GCP infrastructure to integrate with
- **Custom Workflows**: You need custom rules, workflows, and automation
- **Large Organizations**: You're managing hundreds to thousands of users
- **Air-Gapped Environments**: You need on-premises or restricted network deployment
## Configuration Comparison
| Feature | SaaS Configuration | Self-Hosted Configuration |
|---------|-------------------|---------------------------|
| **Setup Complexity** | Simple | Advanced |
| **Deployment Time** | Minutes | Days to Weeks |
| **Infrastructure Required** | None | AWS/GCP/Azure |
| **Compliance Features** | Basic | Advanced |
| **Custom Rules** | No | Yes |
| **Multi-Account Support** | No | Yes |
| **VPC Integration** | No | Yes |
| **Cost** | Lower | Higher |
## Getting Started
<Steps>
<Step title="Evaluate Your Requirements">
Review your organization's security, compliance, and infrastructure requirements to determine which configuration approach fits your needs.
</Step>
<Step title="Choose Your Path">
Select either SaaS Configuration for simple setup or Self-Hosted Configuration for advanced enterprise features. Use the [Deployment Guide](/enterprise-solutions/configuration/choosing-your-deployment) if you need help deciding.
</Step>
<Step title="Follow Configuration Guide">
Complete the setup process using the detailed guides for your chosen configuration type.
</Step>
<Step title="Onboard Team Members">
Once configured, team members can connect using the provider-specific member guides.
</Step>
</Steps>
---
## Available Providers
Both configuration approaches support the same core inference providers:
<CardGroup cols={3}>
<Card title="AWS Bedrock" icon="aws">
Enterprise AI models with AWS infrastructure integration and security features.
</Card>
<Card title="LiteLLM" icon="layer-group">
Unified proxy for accessing 100+ AI models through a single interface.
</Card>
<Card title="Google Vertex AI" icon="google">
Google Cloud's AI platform with advanced ML capabilities and global infrastructure.
</Card>
</CardGroup>
The main difference lies in how these providers are configured and managed within your organization's infrastructure and security requirements.
@@ -1,112 +0,0 @@
---
title: "Configure Google Vertex AI Provider (Admin)"
sidebarTitle: "Configure Google Vertex (Admin)"
description: "This guide explains how administrators configure Google Vertex AI as the organization-wide LLM provider for Cline."
---
As an administrator, you can add Google Vertex AI as the organization-wide LLM provider for all Cline users through the hosted admin console. This centralized approach ensures consistent access to Google's Gemini models while maintaining your organization's project boundaries and regional settings.
## Before You Begin
To get started with setting up Google Vertex AI 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.
**Google Cloud Project with Vertex AI enabled**
You need a Google Cloud project with the Vertex AI API enabled and appropriate models accessible.
<Note>
If you haven't set up Google Cloud or Vertex AI yet, work with your cloud team to enable the Vertex AI API and ensure necessary quotas are configured.
</Note>
**Project configuration details**
You'll need your Google Cloud project ID and preferred region for Vertex AI model access.
<Tip>
Service accounts should have the minimum IAM permissions needed for Vertex AI access to follow security best practices.
</Tip>
## 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 Google Vertex AI as the API Provider">
Open the **API Provider** dropdown menu and select **Google Vertex AI**. This will open the Vertex AI configuration panel where you'll configure all your organization-wide settings.
</Step>
<Step title="Configure Vertex AI Settings">
The configuration panel includes settings that control how Vertex AI works for your organization:
<AccordionGroup>
<Accordion title="Project ID (required)">
Enter your Google Cloud project ID where Vertex AI is enabled. This project will be used for all AI model requests from your organization members.
<Tip>
Use a dedicated project for AI workloads to better track usage and costs. Ensure the project has sufficient quotas for your team's expected usage.
</Tip>
</Accordion>
<Accordion title="Region (required)">
Select the Google Cloud region where your Vertex AI models should be accessed. Common options include `us-central1`, `us-east4`, or `europe-west4`.
[View Google Cloud Regions](https://cloud.google.com/docs/geography-and-regions)
<Note>
Choose a region close to your team's location for optimal performance. Some models may not be available in all regions.
</Note>
</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 Google Vertex AI 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 "Google Vertex AI" 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 Vertex AI as a provider
4. Verify that Gemini models are available in the model dropdown
## 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 and that your Google Cloud project has Vertex AI API enabled.
**Project access errors**
Verify the project ID is correct and that Vertex AI API is enabled. Check that the project has appropriate billing configured and hasn't exceeded quotas.
**Regional availability issues**
Confirm the selected region supports the Gemini models you want to use. Some newer models may only be available in specific regions.
**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 project or region later**
You can update these settings at any time. Members will need to ensure their local Google Cloud credentials have access to the new project/region.
For further details, consult the [Google Cloud Vertex AI Documentation](https://cloud.google.com/vertex-ai/docs) and coordinate with your internal cloud team.
@@ -1,177 +0,0 @@
---
title: "Configure Google Vertex AI in VS Code (Members)"
sidebarTitle: "Configure Google Vertex (Member)"
description: "Guide for engineers connecting to their organization's Google Vertex AI setup through VS Code after admin setup"
---
As a team member, you can connect your local development environment to your organization's Google Vertex AI setup. This guide walks you through configuring your Google Cloud credentials in VS Code so you can start using Vertex AI models through your organization's configured project and regional settings. 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 Google Vertex AI 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>
**Google Cloud credentials with Vertex AI access**
You need Google Cloud credentials that have permission to access Vertex AI in your organization's configured project and region.
<Note>
If you're unsure which method to use, check with your administrator or IT team about how your organization has configured Google Cloud access.
</Note>
## 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 `vertex_ai/gemini-pro` or similar)
</Step>
<Step title="Select Your Authentication Method">
Choose one of the following credential methods to authenticate with Google Vertex AI:
<AccordionGroup>
<Accordion title="Service Account Key">
Use a service account JSON key file for Vertex AI access.
[Learn more about Service Account Keys](https://cloud.google.com/iam/docs/service-accounts)
1. Select the **Service Account Key** authentication method
2. Upload or paste your service account JSON key content
3. The key should have `aiplatform.user` or similar Vertex AI permissions
4. These credentials are stored locally and used only by the VS Code extension
</Accordion>
<Accordion title="Google Cloud SDK">
Use the Google Cloud SDK installed on your machine with your authenticated account.
[Learn more about Google Cloud SDK](https://cloud.google.com/sdk/docs/install)
1. Select the **Google Cloud SDK** authentication method
2. Ensure you've authenticated with `gcloud auth login`
3. Verify your account has access to the organization's Vertex AI project
4. Cline will use your default Google Cloud credentials automatically
</Accordion>
<Accordion title="Application Default Credentials">
Use Google Cloud's application default credentials (ADC) chain.
1. Select the **Application Default Credentials** method
2. Ensure ADC is properly configured in your environment
3. This works well for environments where Google Cloud credentials are managed centrally
4. Cline will automatically detect credentials from your environment
</Accordion>
</AccordionGroup>
<Note>
The Google Cloud Project ID and Region are preconfigured by your administrator and do 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 (for Gemini Pro Vision and similar models)
- ✓ Supports multimodal inputs
- ✓ Supports function calling (for supported models)
The project ID and region settings 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 Vertex AI project and region.
<Tip>
**Testing Recommendation**
Try a simple test like "Hello" first to verify basic connectivity, then test multimodal capabilities if needed by sharing an image.
</Tip>
</Step>
</Steps>
## Model Usage
### Available Model Families
The models available through your organization's Vertex AI setup typically include:
**Gemini Models:**
- **Gemini Pro**: Advanced reasoning, code generation, and multimodal capabilities
- **Gemini Pro Vision**: Image understanding and visual question answering
- **Gemini Ultra**: Most capable model for complex reasoning tasks
**PaLM Models:**
- **PaLM 2 for Text**: Text generation and completion
- **PaLM 2 for Chat**: Conversational AI interactions
- **Codey**: Specialized for code generation and explanation
**Specialized Models:**
- **Text Embedding**: For semantic search and similarity tasks
- **Custom Models**: Your organization's fine-tuned variants (if available)
### Model Selection Strategy
Choose models based on your development needs:
- **General tasks**: Use Gemini Pro for most text and reasoning tasks
- **Visual content**: Use Gemini Pro Vision when working with images
- **Code-heavy work**: Use Codey models for programming tasks
- **Complex reasoning**: Use Gemini Ultra for sophisticated problem-solving
- **Embedding tasks**: Use Text Embedding models for semantic operations
### Multimodal Capabilities
Take advantage of Vertex AI's multimodal features:
- **Image Analysis**: Upload images directly in Cline for analysis
- **Visual Question Answering**: Ask questions about images
- **Code Screenshots**: Get explanations of code from screenshots
- **Document Processing**: Analyze charts, graphs, and visual data
## Troubleshooting
**Google Vertex AI not available as provider option**
Confirm you're signed into the correct Cline organization. Verify your administrator has saved the Vertex AI configuration and that you have the latest version of the Cline extension.
**Authentication errors ("Access Denied" or "Invalid Credentials")**
Verify your chosen credential method has the necessary IAM permissions to access Vertex AI in the configured project and region. Required permissions include `aiplatform.endpoints.predict` and `aiplatform.models.predict`.
**Project access errors**
Ask your administrator to confirm which Google Cloud project is configured for your organization. Ensure your Google Cloud credentials have access to that specific project.
**Regional access errors**
Verify your credentials have access to Vertex AI in the configured region. Some models may not be available in all regions, so confirm with your administrator about the selected region.
**Google Cloud SDK authentication issues**
Ensure Google Cloud SDK is properly installed and authenticated:
```bash
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
gcloud auth application-default login
```
**Service account key errors**
Verify the service account key is valid and hasn't expired. Check that the service account has the proper Vertex AI permissions in your organization's project. Ensure the JSON key file is properly formatted and contains all required fields.
**Model access errors or "model not found"**
Some models may not be enabled in your organization's project or region. Contact your administrator if specific models are not available. Verify that your organization has enabled the models you're trying to use in the Google Cloud Console.
## Security Best Practices
When configuring your Google Cloud credentials, follow these security guidelines:
- Use service accounts with minimal required permissions for Vertex AI access
- Rotate service account keys regularly (every 90 days recommended)
- Never store credentials in code or version control
- Use Google Cloud SDK where possible for better credential management
- Consider using Workload Identity for containerized development environments
- Report any suspicious activity or unauthorized access attempts
Your organization administrator controls which models and regions are available. The extension will automatically display available models based on your project's configuration and regional availability.
For more information about Google Cloud authentication and Vertex AI permissions, refer to the [Google Cloud IAM Documentation](https://cloud.google.com/iam/docs) and coordinate with your organization's cloud administrator.
@@ -1,120 +0,0 @@
---
title: "Configure LiteLLM Provider (Admin)"
sidebarTitle: "Configure LiteLLM (Admin)"
description: "This guide explains how administrators configure LiteLLM as the organization-wide LLM provider for Cline."
---
As an administrator, you can add LiteLLM as the organization-wide LLM provider for all Cline users through the hosted admin console. This centralized approach provides unified access to multiple AI models through your LiteLLM proxy interface.
## Before You Begin
To get started with setting up LiteLLM 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>
**LiteLLM proxy instance running**
You need a deployed LiteLLM proxy that your team can access. This can be self-hosted or managed through a cloud provider.
<Note>
If you haven't deployed LiteLLM yet, work with your infrastructure team to set up a LiteLLM proxy instance.
</Note>
**LiteLLM endpoint details**
You'll need the base URL of your LiteLLM proxy and optionally a master key if your deployment requires authentication.
<Tip>
Ensure your LiteLLM proxy is accessible from your team's development environments and has the models you want to make available configured.
</Tip>
## 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 LiteLLM as the API Provider">
Open the **API Provider** dropdown menu and select **LiteLLM**. This will open the LiteLLM configuration panel where you'll configure all your organization-wide settings.
</Step>
<Step title="Configure LiteLLM Settings">
The configuration panel includes settings that control how LiteLLM works for your organization:
<AccordionGroup>
<Accordion title="Base URL (required)">
Enter your LiteLLM proxy endpoint URL. This should be the full URL where your LiteLLM proxy is accessible, such as `https://litellm.yourcompany.com` or `http://your-proxy:4000`.
<Tip>
Use HTTPS endpoints in production for security. Make sure the URL is accessible from your team's development environments.
</Tip>
</Accordion>
<Accordion title="Master Key (optional)">
If your LiteLLM proxy requires authentication, enter the master key here. This will be used to authenticate requests from all organization members.
<Note>
**Centralized API Key Management**: By configuring the Master Key at the organization level, you enable centralized API key management. Organization members won't need to manage their own individual API keys - access is fully managed through this centralized configuration.
</Note>
<Warning>
The master key provides full access to your LiteLLM proxy. Only enter this if your proxy requires authentication and you want centralized key management.
</Warning>
</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 LiteLLM 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 "LiteLLM" 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 LiteLLM as a provider
4. Verify that the configured models are available in the model dropdown
## 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 and that your LiteLLM proxy is accessible from their network.
**Connection errors to LiteLLM proxy**
Verify the Base URL is correct and accessible. Check that any firewalls or security groups allow access from your team's IP addresses or development environments.
**Authentication failures**
If using a master key, verify it's correctly entered and has proper permissions in your LiteLLM deployment. Check the LiteLLM proxy logs for authentication errors.
**Models not available**
Confirm the models are properly configured in your LiteLLM proxy deployment. The available models depend on how your LiteLLM proxy is configured.
**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 endpoint or key later**
You can update these settings at any time. Changes take effect immediately for all organization members.
For further details about LiteLLM deployment and configuration, consult the [LiteLLM Documentation](https://docs.litellm.ai/) and coordinate with your infrastructure team.
@@ -1,168 +0,0 @@
---
title: "Configure LiteLLM in VS Code (Members)"
sidebarTitle: "Configure LiteLLM (Member)"
description: "Guide for engineers connecting to their organization's LiteLLM proxy through VS Code after admin setup"
---
As a team member, you can connect your local development environment to your organization's LiteLLM proxy setup. This guide walks you through configuring your connection in VS Code so you can start using multiple AI models through your organization's unified proxy interface. 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 LiteLLM proxy, 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>
**Access credentials for your organization's LiteLLM proxy**
You need credentials to access your organization's LiteLLM proxy. This might be an API key, or the proxy might be configured for open access within your network.
<Note>
If you're unsure about the credentials needed, check with your administrator or IT team about how to access your organization's LiteLLM proxy.
</Note>
## 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 `LiteLLM` or show a specific model name)
</Step>
<Step title="Configure LiteLLM Connection">
The LiteLLM configuration options depend on how your organization has set up the proxy:
<AccordionGroup>
<Accordion title="API Key Authentication">
If your organization requires API key authentication:
1. Select or confirm the **LiteLLM** provider is selected
2. Enter your assigned API key in the **API Key** field
3. The base URL should already be configured by your administrator
4. Click **Save** to store your credentials
<Tip>
API keys are stored locally in VS Code and are only used by the Cline extension.
</Tip>
</Accordion>
<Accordion title="Open Access (No Authentication)">
If your LiteLLM proxy is configured for open access within your network:
1. Select or confirm the **LiteLLM** provider is selected
2. Leave the API key field empty
3. The extension will connect directly to the configured proxy endpoint
4. No additional authentication is required
<Info>
Open access is common when the LiteLLM proxy is deployed within a secure network environment.
</Info>
</Accordion>
<Accordion title="Custom Configuration">
If your organization uses custom authentication or specific connection parameters:
1. Follow any custom instructions provided by your administrator
2. Contact your IT team if you encounter connection issues
3. Additional configuration may be needed outside of VS Code
<Note>
Custom configurations might require specific network settings or additional authentication steps.
</Note>
</Accordion>
</AccordionGroup>
</Step>
<Step title="Select Available Models">
Once connected, you'll see the models available through your organization's LiteLLM proxy:
- View available models in the model dropdown
- Models are determined by your administrator's proxy configuration
- You can switch between models for different types of tasks
- Some models may be restricted based on your access level
<Tip>
**Model Selection**
Choose models based on your task requirements:
- **Fast models** (like GPT-3.5-turbo) for quick responses
- **Powerful models** (like GPT-4) for complex reasoning
- **Specialized models** for code generation or specific domains
</Tip>
</Step>
<Step title="Test the Connection">
Send a test message in Cline to verify your connection works correctly with the LiteLLM proxy.
<Tip>
**Testing Recommendation**
Test the connection in plan mode first to verify everything works correctly before using it for actual development tasks.
</Tip>
</Step>
</Steps>
## Model Usage
### Available Model Categories
The models available through your LiteLLM proxy typically include:
**Text Generation Models:**
- OpenAI GPT-4, GPT-3.5-turbo variants
- Anthropic Claude 3 Sonnet, Haiku, Opus
- Open source models like Llama 2, Mistral
**Code-Specific Models:**
- OpenAI GPT-4 for code
- CodeLlama variants
- Specialized code completion models
**Multimodal Models:**
- GPT-4 Vision for image analysis
- Claude 3 models with vision capabilities
### Model Selection Strategy
Choose models based on your development needs:
- **Quick iterations**: Use faster, cost-effective models
- **Complex problems**: Use more powerful models
- **Code-heavy tasks**: Use code-specialized models
- **Visual content**: Use multimodal models when working with images
## Troubleshooting
**LiteLLM not available as provider option**
Confirm you're signed into the correct Cline organization. Verify your administrator has saved the LiteLLM configuration and that you have the latest version of the Cline extension.
**Connection errors or timeouts**
Verify your network can reach the LiteLLM proxy endpoint. Check with your IT team about firewall rules or VPN requirements. Ensure the proxy endpoint is accessible from your development environment.
**Authentication failures**
If using API key authentication, verify the key is correctly entered and hasn't expired. Contact your administrator to confirm your key is active and has the proper permissions.
**Models not loading or are limited**
The available models depend on your organization's LiteLLM configuration. Contact your administrator if you need access to specific models or if expected models aren't available.
**Slow response times**
Response times depend on the models being used and proxy load. Try switching to faster models for routine tasks. Contact your administrator if performance is consistently poor.
**Error messages from specific models**
Some models may be temporarily unavailable or have specific limitations. Try alternative models or contact your administrator if specific models are consistently failing.
## Security Best Practices
When working with your organization's LiteLLM proxy:
- Keep your API credentials secure and don't share them
- Use appropriate models for the sensitivity of your data
- Follow your organization's usage guidelines
- Report any suspicious activity or unauthorized access attempts
- Regularly update the Cline extension for security patches
Your organization administrator controls which models are available and usage policies. The extension will automatically display available models based on your proxy configuration and access level.
@@ -1,102 +0,0 @@
---
title: "SaaS Provider Configuration"
sidebarTitle: "Overview"
description: "Configure inference providers through the Cline hosted admin console for centralized organization management"
---
SaaS Provider Configuration allows administrators to centrally configure inference providers for their entire organization through the Cline hosted admin console. This approach ensures consistent provider access, security policies, and cost management across all team members without requiring individual developer setup or infrastructure deployment.
## How Remote Configuration Works
Remote configuration operates through Cline's hosted service at [app.cline.bot](https://app.cline.bot), where administrators can:
<CardGroup cols={2}>
<Card title="Centralized Setup" icon="gear">
Configure providers once for the entire organization through the web-based admin console.
</Card>
<Card title="Automatic Enforcement" icon="shield-check">
Team members automatically receive the configured provider settings when signed into their organization.
</Card>
<Card title="Simplified Onboarding" icon="user-plus">
New team members get instant access to inference providers without complex individual configuration.
</Card>
<Card title="Consistent Experience" icon="users">
Ensure all team members use the same models, regions, and settings organization-wide.
</Card>
</CardGroup>
## Supported Providers
Cline supports remote configuration for the following inference providers:
| Provider | Use Case | Configuration | Member Setup |
|----------|----------|---------------|--------------|
| **Cline** | Organizations using Cline's native provider with centralized API key management | API provider selection, model access | No individual API keys needed - fully managed by organization |
| **Amazon Bedrock** | Organizations using AWS infrastructure | Region selection, VPC endpoints, cross-region inference, prompt caching | AWS credential configuration in VS Code |
| **LiteLLM** | Organizations requiring multi-model access through a unified proxy | Proxy endpoint, authentication, model routing | API key or endpoint configuration in VS Code (or centralized with Master Key) |
| **Google Vertex AI** | Organizations using Google Cloud Platform | Project ID, region selection, model access | Service account or credential configuration in VS Code |
## Configuration Process
The typical remote configuration process follows these steps:
<Steps>
<Step title="Administrator Setup">
Access the Cline admin console and configure the desired inference provider with organization-wide settings.
</Step>
<Step title="Automatic Distribution">
Provider configuration is automatically distributed to all organization members signed into Cline.
</Step>
<Step title="Member Credential Setup">
Team members add their individual credentials (API keys, AWS profiles, etc.) to connect to the configured provider.
</Step>
<Step title="Immediate Access">
Once credentials are configured, members can immediately start using the inference provider through Cline.
</Step>
</Steps>
## Benefits of Remote Configuration
### **For Administrators**
- **Centralized Control**: Manage all provider settings from one location
- **Security Compliance**: Ensure consistent security policies across the organization
- **Easy Updates**: Change provider settings organization-wide instantly
### **For Team Members**
- **Simplified Setup**: No need to research provider configuration options
- **Consistent Experience**: Same models and features available to everyone
- **Quick Onboarding**: Get started immediately with pre-configured providers
- **Focus on Development**: Spend time coding instead of configuring inference providers
## Getting Started
To get started with provider remote configuration:
1. **Choose Your Provider**: Select the inference provider that best fits your organization's needs and existing infrastructure
2. **Admin Configuration**: Follow the provider-specific admin configuration guide
3. **Member Onboarding**: Have team members complete the provider-specific member configuration
4. **Start Developing**: Begin using Cline with centrally managed inference provider access
Select your provider below to begin the configuration process:
<CardGroup cols={3}>
<Card title="Amazon Bedrock" icon="aws" href="/enterprise-solutions/configuration/remote-configuration/aws-bedrock/admin-configuration">
AWS-based AI models with enterprise security and compliance features.
</Card>
<Card title="LiteLLM" icon="layer-group" href="/enterprise-solutions/configuration/remote-configuration/litellm/admin-configuration">
Unified proxy for accessing 100+ AI models through a single interface.
</Card>
<Card title="Google Vertex AI" icon="google" href="/enterprise-solutions/configuration/remote-configuration/google-vertex/admin-configuration">
Google Cloud's AI platform with advanced ML capabilities and global infrastructure.
</Card>
</CardGroup>
@@ -0,0 +1,63 @@
---
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.
@@ -0,0 +1,63 @@
---
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.
@@ -0,0 +1,27 @@
---
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>
@@ -0,0 +1,83 @@
---
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.
@@ -1,266 +0,0 @@
---
title: "OpenTelemetry Integration"
sidebarTitle: "OpenTelemetry"
description: "Export Cline telemetry to your observability platform using OpenTelemetry Protocol (OTLP)"
---
Cline includes opt-in OpenTelemetry support for exporting metrics and logs to your own observability infrastructure using the OpenTelemetry Protocol (OTLP).
<Note>
OpenTelemetry integration is **optional** and intended for advanced users with existing observability infrastructure. Most users won't need this feature.
</Note>
## What is OpenTelemetry?
[OpenTelemetry](https://opentelemetry.io/) is an industry-standard observability framework that provides a unified way to collect and export telemetry data (metrics, logs, and traces).
Cline's OpenTelemetry support allows you to:
- Export telemetry to your own systems
- Integrate with observability platforms like Datadog, New Relic, Grafana Cloud, etc.
- Maintain full control over your monitoring data
- Use your organization's existing monitoring infrastructure
## Supported Features
Cline supports OpenTelemetry's **OTLP (OpenTelemetry Protocol)** export with:
<CardGroup cols={2}>
<Card title="Metrics Export" icon="chart-bar">
Export metrics about Cline usage, performance, and errors
</Card>
<Card title="Logs Export" icon="file-lines">
Export structured logs for debugging and analysis
</Card>
</CardGroup>
### Export Formats
Cline supports three OTLP export protocols:
- **gRPC** (default, recommended)
- **HTTP/protobuf**
- **HTTP/JSON**
### Export Destinations
You can export to:
- **Console** (for testing)
- **OTLP endpoint** (your own collector or observability platform)
## Configuration
OpenTelemetry is configured using environment variables before launching Cline.
### Basic Setup
Enable OpenTelemetry and configure an OTLP endpoint:
```bash
# Enable OpenTelemetry
export OTEL_TELEMETRY_ENABLED=1
# Configure metrics and logs export
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
# Set your OTLP endpoint
export OTEL_EXPORTER_OTLP_ENDPOINT=https://your-collector:4317
# Optional: Set protocol (default is grpc)
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
```
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `OTEL_TELEMETRY_ENABLED` | Enable OpenTelemetry (`1` or `true`) | Disabled |
| `OTEL_METRICS_EXPORTER` | Metrics exporter type (`console`, `otlp`, or both) | None |
| `OTEL_LOGS_EXPORTER` | Logs exporter type (`console`, `otlp`, or both) | None |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP endpoint URL | None |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | Protocol (`grpc`, `http/protobuf`, `http/json`) | `grpc` |
| `OTEL_EXPORTER_OTLP_INSECURE` | Allow insecure connections | `false` |
| `OTEL_EXPORTER_OTLP_HEADERS` | Custom headers (comma-separated `key=value` pairs) | None |
### Advanced Configuration
**Separate endpoints for metrics and logs:**
```bash
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metrics-collector:4317
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs-collector:4317
```
**Custom headers for authentication:**
```bash
export OTEL_EXPORTER_OTLP_HEADERS="api-key=your-key,x-custom-header=value"
```
**Multiple exporters (console + OTLP):**
```bash
export OTEL_METRICS_EXPORTER=console,otlp
export OTEL_LOGS_EXPORTER=console,otlp
```
**Export intervals:**
```bash
# Metrics export interval in milliseconds (default: 60000)
export OTEL_METRIC_EXPORT_INTERVAL=30000
# Logs batch size and timeout
export OTEL_LOG_BATCH_SIZE=512
export OTEL_LOG_BATCH_TIMEOUT=5000
export OTEL_LOG_MAX_QUEUE_SIZE=2048
```
## Integration Examples
### Datadog
Export to Datadog using their OTLP endpoint:
```bash
export OTEL_TELEMETRY_ENABLED=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=https://api.datadoghq.com
export OTEL_EXPORTER_OTLP_HEADERS="dd-api-key=YOUR_DD_API_KEY"
```
### New Relic
Export to New Relic:
```bash
export OTEL_TELEMETRY_ENABLED=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net:4317
export OTEL_EXPORTER_OTLP_HEADERS="api-key=YOUR_NEW_RELIC_LICENSE_KEY"
```
### Grafana Cloud
Export to Grafana Cloud:
```bash
export OTEL_TELEMETRY_ENABLED=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic YOUR_BASE64_CREDENTIALS"
```
## Testing Configuration
Test your configuration with console output before sending to a real endpoint:
```bash
# Enable console output to see what data would be exported
export OTEL_TELEMETRY_ENABLED=1
export OTEL_METRICS_EXPORTER=console
export OTEL_LOGS_EXPORTER=console
```
Then launch Cline and check the console output for metrics and logs.
## Troubleshooting
### No Data Being Exported
1. **Verify OpenTelemetry is enabled:**
```bash
echo $OTEL_TELEMETRY_ENABLED
```
Should output `1` or `true`
2. **Check exporters are configured:**
```bash
echo $OTEL_METRICS_EXPORTER
echo $OTEL_LOGS_EXPORTER
```
3. **Test with console exporter first:**
```bash
export OTEL_METRICS_EXPORTER=console
export OTEL_LOGS_EXPORTER=console
```
### Connection Errors
1. **Verify endpoint is accessible:**
```bash
curl -v https://your-otlp-endpoint:4317
```
2. **Check if insecure mode is needed:**
```bash
export OTEL_EXPORTER_OTLP_INSECURE=true
```
3. **Verify authentication headers:**
Double-check your API keys and authentication headers are correct
### Debug Mode
Enable debug logging to see detailed OpenTelemetry information:
```bash
export TEL_DEBUG_DIAGNOSTICS=true
```
This will output detailed information about:
- Configuration being used
- Exporters being created
- Connection attempts
- Export successes/failures
## What Gets Exported
When Opentelemetry is enabled, Cline exports:
### Metrics
- Feature usage counts
- Task execution metrics
- Error rates and types
- Performance measurements
### Logs
- System events
- Error logs with context
- Operational information
<Warning>
Exported data is already anonymous and doesn't include code content, file paths, or sensitive information. However, you're responsible for securing the data once exported to your systems.
</Warning>
## Limitations
Current OpenTelemetry support in Cline:
- ✅ OTLP metrics export (console, gRPC, HTTP)
- ✅ OTLP logs export (console, gRPC, HTTP)
- ✅ Basic configuration via environment variables
- ❌ Distributed tracing (not yet implemented)
- ❌ Custom instrumentation API (not yet exposed)
- ❌ Sampling configuration (uses defaults)
## Best Practices
1. **Test First**: Always test with console exporter before sending to production
2. **Secure Credentials**: Never hardcode API keys; use secure environment variable management
3. **Monitor Costs**: Be aware of data ingestion costs with your observability platform
4. **Start Simple**: Begin with metrics only, add logs if needed
5. **Use Compression**: OTLP supports compression; check if your endpoint requires it
## Next Steps
<CardGroup cols={2}>
<Card title="Cline Telemetry" icon="chart-simple" href="/enterprise-solutions/monitoring/telemetry">
Configure simple built-in telemetry
</Card>
<Card title="OpenTelemetry Docs" icon="book" href="https://opentelemetry.io/docs/">
Learn more about OpenTelemetry
</Card>
</CardGroup>
@@ -1,111 +0,0 @@
---
title: "Enterprise Monitoring"
sidebarTitle: "Overview"
description: "Optional telemetry and observability for your Cline deployment"
---
Cline includes optional monitoring capabilities for organizations that want to track usage and integrate with their observability infrastructure.
## Monitoring Options
<CardGroup cols={2}>
<Card title="Cline Telemetry" icon="chart-simple" href="/enterprise-solutions/monitoring/telemetry">
Built-in anonymous usage tracking that helps improve Cline (opt-in)
</Card>
<Card title="OpenTelemetry" icon="chart-line" href="/enterprise-solutions/monitoring/opentelemetry">
Export metrics and logs to your own observability backends (advanced)
</Card>
</CardGroup>
## Cline Telemetry
Cline includes opt-in telemetry for anonymous usage tracking:
- Feature usage patterns
- Task completion rates
- Error occurrences
- Performance metrics
Users can enable or disable telemetry in Cline settings. All data is anonymous and does not include code content, file paths, or sensitive information.
See [Cline Telemetry](/enterprise-solutions/monitoring/telemetry) for configuration details.
## OpenTelemetry Integration
For advanced monitoring needs, Cline supports OpenTelemetry's OTLP (OpenTelemetry Protocol) for exporting metrics and logs to your own infrastructure.
This allows you to:
- Export telemetry to your existing observability platforms
- Integrate with tools like Datadog, New Relic, or Grafana Cloud
- Maintain full control over your monitoring data
- Aggregate metrics across your organization
<Note>
OpenTelemetry integration is **optional** and requires additional configuration. Most users don't need this feature.
</Note>
See [OpenTelemetry](/enterprise-solutions/monitoring/opentelemetry) for setup instructions.
## Use Cases
### When to Use Cline Telemetry
- You want to help improve Cline through anonymous usage data
- No additional setup required
- Suitable for most users
### When to Use OpenTelemetry
- You need granular metrics in your own systems
- You're integrating with existing observability infrastructure
- You want detailed logs and metrics for debugging
- You need custom dashboards or alerting
## Getting Started
<Steps>
<Step title="Choose Your Approach">
Decide whether basic telemetry or OpenTelemetry integration fits your needs
</Step>
<Step title="Enable Telemetry">
For basic telemetry, enable it in Cline settings. For OpenTelemetry, see the configuration guide.
</Step>
<Step title="Verify Data Collection">
Confirm telemetry is being collected as expected
</Step>
</Steps>
## Privacy & Security
All Cline monitoring features are designed with privacy in mind:
<CardGroup cols={2}>
<Card title="Anonymous" icon="user-secret">
No personal information collected
</Card>
<Card title="Optional" icon="toggle-on">
Users can disable at any time
</Card>
<Card title="Local First" icon="laptop">
Code never leaves your machine
</Card>
<Card title="Transparent" icon="code">
Open source - see what's collected
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Configure Telemetry" icon="gear" href="/enterprise-solutions/monitoring/telemetry">
Set up basic telemetry settings
</Card>
<Card title="OpenTelemetry Setup" icon="chart-line" href="/enterprise-solutions/monitoring/opentelemetry">
Advanced monitoring with OpenTelemetry
</Card>
</CardGroup>
@@ -1,133 +0,0 @@
---
title: "Cline Telemetry"
sidebarTitle: "Cline Telemetry"
description: "Configure usage analytics and event tracking"
---
Cline includes telemetry to help understand usage patterns and improve the product. Users can control whether to share this data.
## What is Cline Telemetry?
Telemetry captures anonymous usage events such as:
- Features used (which tools, commands, workflows)
- Task completion rates
- Error occurrences
- Performance metrics
<Info>
All telemetry data is **anonymous** and does not include code content, file contents, or other sensitive information.
</Info>
## User Controls
### Enabling/Disabling Cline Telemetry
Individual users can control telemetry through Cline settings:
1. Open Cline settings
2. Find "Cline Telemetry" toggle
3. Enable or disable as preferred
Changes take effect immediately.
### What Gets Collected
When telemetry is enabled, Cline captures:
<AccordionGroup>
<Accordion title="Feature Usage" icon="cursor-click">
- Tools executed (e.g., read_file, execute_command)
- Slash commands used
- Workflows triggered
- Settings changed
</Accordion>
<Accordion title="Task Metrics" icon="tasks">
- Task started/completed events
- Mode switches (Plan/Act)
- Checkpoint usage
- Task duration
</Accordion>
<Accordion title="Error Events" icon="triangle-exclamation">
- API failures
- Tool execution errors
- System errors
- Error types and frequencies
</Accordion>
</AccordionGroup>
### What Doesn't Get Collected
Cline Telemetry **never** includes:
- Your code or file contents
- File paths or names
- Command arguments or parameters
- Conversation content
- Personal information
- API keys or credentials
## Enterprise Configuration
Administrators can set default telemetry state through remote configuration:
```json
{
"telemetryEnabled": true
}
```
<Note>
Even with enterprise configuration, individual users can still disable Cline Telemetry in their local settings.
</Note>
## Advanced Monitoring
For organizations needing detailed monitoring, Cline supports optional OpenTelemetry integration to export telemetry data to your own observability systems.
See [Enterprise Monitoring](/enterprise-solutions/monitoring/overview) for details on available monitoring options.
## Privacy
Cline's telemetry is designed with privacy in mind:
<CardGroup cols={2}>
<Card title="Anonymous" icon="user-secret">
No personal information is collected
</Card>
<Card title="Optional" icon="toggle-on">
Users can disable at any time
</Card>
<Card title="Local First" icon="laptop">
Code never leaves your machine
</Card>
<Card title="Transparent" icon="eye">
Open source - see exactly what's collected
</Card>
</CardGroup>
## Why Telemetry Matters
Anonymous usage data helps:
- **Identify bugs**: Discover issues affecting users
- **Prioritize features**: Focus on most-used capabilities
- **Improve performance**: Find and fix slow operations
- **Enhance reliability**: Track and reduce error rates
## Related
<CardGroup cols={2}>
<Card title="OpenTelemetry" icon="chart-line" href="/enterprise-solutions/monitoring/opentelemetry">
Enterprise monitoring and observability
</Card>
<Card title="Privacy" icon="shield" href="/more-info/telemetry">
Full telemetry documentation
</Card>
</CardGroup>
+1 -1
View File
@@ -51,7 +51,7 @@ User roles are mapped automatically from your IdP:
- **Member** in IdP → **Member** role in Cline
<Info>
For what each role can access, see the [Roles and Permissions](/enterprise-solutions/team-management/managing-members) page.
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:
+5 -5
View File
@@ -1,7 +1,7 @@
---
title: "Cline Enterprise"
sidebarTitle: "Overview"
description: "Enterprise security, governance, and observability for the coding agent millions of developers trust"
description: "Enterprise security, governance, and observability for the coding agent 3 million developers trust"
---
Cline Enterprise brings centralized governance to the same open-source architecture that millions of developers already use. Your code stays in your environment, you use your own inference at your negotiated rates, and you get the security and observability capabilities that platform teams need for org-wide deployment.
@@ -57,10 +57,10 @@ Platform teams need central control when thousands of developers use AI. Individ
Enterprise governance provides:
- **SSO authentication**: Corporate credentials instead of personal API keys
- **Role-based access control**: Three-tier hierarchy (Member/Admin/Owner) with organization-scoped permissions
- **Role-based access control**: Fine-grained permissions per team and project
- **Model and tool controls**: Govern which models and tools each team accesses
- **Remote configuration**: Manage settings for all developers from one dashboard
- **Usage tracking and observability**: OpenTelemetry integration for monitoring usage, costs, and performance with selective audit logging for administrative operations
- **Full audit logging**: Every AI interaction tracked with detailed logs
Configure once, deploy everywhere. Developers work how they prefer while you maintain control.
@@ -77,7 +77,7 @@ The same observability standards you require for production systems.
## Deployment
Cline Enterprise connects securely to your infrastructure. Deploy in cloud environments. Configure to work with your existing security policies and compliance requirements.
Cline Enterprise connects securely to your infrastructure. Deploy in cloud environments, on-premises, or air-gapped networks. Configure to work with your existing security policies and compliance requirements.
Rolling out to your organization:
1. Configure Cline Core to connect to your infrastructure
@@ -87,7 +87,7 @@ Rolling out to your organization:
## Next Steps
- Review security architecture
- Review [security architecture](/enterprise-solutions/security-concerns)
- Configure [cloud provider setup](/provider-config/aws-bedrock/api-key) (AWS Bedrock, Vertex AI, Azure)
- Set up [MCP servers](/mcp/mcp-overview) for custom tooling
- Add [custom instructions](/features/cline-rules) for your codebase
@@ -4,8 +4,7 @@ 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 through the hosted admin console. This centralized approach ensures consistent access to Amazon's AI models while maintaining your organization's security and compliance requirements through region controls and basic configuration options.
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
@@ -14,6 +13,9 @@ To get started with setting up AWS Bedrock as your organization's LLM provider,
**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.
@@ -1,317 +0,0 @@
---
title: "Managing Members"
sidebarTitle: "Managing Members"
description: "Complete guide to managing team members, roles, and permissions in your Cline Enterprise organization"
---
Effective member management is essential for maintaining security and enabling your team to work productively. This guide covers everything you need to know about roles, permissions, and day-to-day member administration.
## Understanding Roles
Choose the right role for each team member to balance security with productivity. Here's what each role is designed for:
<CardGroup cols={3}>
<Card title="Owner" icon="crown" color="#9D4EDD">
**Primary account holder**
Unrestricted access to all settings including billing, security, and ownership transfer. Keep this limited to 1-2 key leaders.
</Card>
<Card title="Admin" icon="user-gear" color="#7209B7">
**Team leads & IT managers**
Can manage users and configure providers. Ideal for trusted managers who need operational control without billing access.
</Card>
<Card title="Member" icon="user" color="#560BAD">
**Developers & contributors**
Can use Cline with shared resources but cannot change settings. The safest default for most team members.
</Card>
</CardGroup>
## Permissions Matrix
Understand exactly what each role can do with this comprehensive permissions breakdown:
| 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 | ❌ | ❌ | ✅ |
<Note>
**Quick Reference:** Most users should be **Members**. Grant **Admin** only to those managing users or configs. Reserve **Owner** for 1-2 account leaders.
</Note>
## Member Management Tasks
<Tabs>
<Tab title="Adding Members">
### Inviting New Team Members
1. **Navigate to Members**
- Go to your organization dashboard at app.cline.bot
- Click on "Members" in the sidebar
2. **Send Invitation**
- Click "Invite Member"
- Enter the user's email address (must be from your verified domain)
- Select the appropriate role (Member, Admin, or Owner)
- Click "Send Invite"
3. **Invitation Status**
- Invited users will receive an email with a join link
- Pending invitations show in your member list with "Pending" status
- Each pending invitation holds one seat from your license
<Tip>
**Bulk Invitations:** Need to add multiple users? Contact support@cline.bot for assistance with bulk invite CSV imports.
</Tip>
</Tab>
<Tab title="Editing Roles">
### Changing Member Permissions
1. **Locate the Member**
- Navigate to the Members page
- Find the user you want to modify
2. **Change Role**
- Click the dropdown next to their current role
- Select the new role from the menu
- Confirm the change
3. **Effective Immediately**
- Role changes take effect instantly
- The user may need to sign out and back in to see updated permissions
<Warning>
**Admin to Member:** Downgrading an Admin to Member will immediately revoke their ability to manage users and configurations. Ensure they no longer need these permissions.
</Warning>
</Tab>
<Tab title="Removing Members">
### Offboarding Team Members
1. **Access Member List**
- Navigate to your organization's Members page
- Locate the user to remove
2. **Remove User**
- Click the menu icon (⋮) next to their name
- Select "Remove from Organization"
- Confirm the removal
3. **Immediate Effects**
- User loses access to the organization immediately
- Their seat is freed and can be assigned to someone else
- Audit logs are preserved for compliance
<Info>
**Data Retention:** Removing a member does not delete their historical activity logs. All audit trails remain intact for compliance purposes.
</Info>
</Tab>
<Tab title="Revoking Invites">
### Canceling Pending Invitations
If an invited user hasn't accepted yet, you can revoke the invitation:
1. Find the pending invitation in your Members list
2. Click "Revoke Invitation"
3. The seat is immediately freed for another user
This is useful when:
- The wrong email was used
- The user no longer needs access
- You need to reassign the seat urgently
</Tab>
</Tabs>
## Identity & Access Requirements
For users to successfully join your organization, two conditions must be met:
<Steps>
<Step title="Verified Identity Provider">
Your organization must use a verified **Identity Provider (IDP)** such as:
- Microsoft Entra ID (Azure AD)
- Okta
- Google Workspace
- AWS IAM Identity Center
Users must authenticate through your IDP to access the organization.
</Step>
<Step title="Domain Verification">
Your organization must have a **verified domain**. You'll need to verify ownership of your domain through your domain provider (e.g., Google, Microsoft, Cloudflare).
Only users with email addresses from verified domains can join.
</Step>
</Steps>
<Note>
These requirements ensure that only authenticated users from your company can access your Cline organization, preventing unauthorized access.
</Note>
## Seat Management
Understanding how seats work helps you manage your license effectively:
<AccordionGroup>
<Accordion title="How Seats Are Calculated" icon="chair">
- Each user (Owner, Admin, or Member) consumes **one seat**
- Pending invitations also hold one seat
- Removing a member or revoking an invite immediately frees the seat
- Your license determines the maximum number of seats available
</Accordion>
<Accordion title="When Seats Are Used" icon="user-plus">
A seat is consumed when:
- You send an invitation (marked as "pending")
- An invited user accepts and joins
- An existing user is granted access through SSO
</Accordion>
<Accordion title="Freeing Up Seats" icon="user-minus">
To free a seat:
- Remove an active member from the organization
- Revoke a pending invitation
- Wait for a pending invite to expire (if configured)
</Accordion>
<Accordion title="Upgrading Your License" icon="arrow-up">
Need more seats?
- **Teams Plan:** Contact your account manager or visit app.cline.bot/settings/billing to upgrade your license.
- **Enterprise Plan:** Includes unlimited seats with no per-user restrictions.
</Accordion>
</AccordionGroup>
## Security Best Practices
Follow these guidelines to maintain a secure organization:
<CardGroup cols={2}>
<Card title="Principle of Least Privilege" icon="shield-check">
Always assign the minimum role necessary. Most users should be Members. Only grant Admin or Owner privileges when required for job duties.
</Card>
<Card title="Limit Owner Roles" icon="user-lock">
Keep Owners to 1-2 key individuals who manage billing and security. This centralization prevents accidental or malicious changes to critical settings.
</Card>
<Card title="Regular Audits" icon="clipboard-check">
Review your member list quarterly. Remove inactive users promptly and verify that Admin/Owner roles are still appropriate for each user.
</Card>
<Card title="Offboarding Process" icon="door-open">
Create a standard offboarding checklist: remove from Cline, revoke IDP access, document in audit log, and reassign any critical responsibilities.
</Card>
</CardGroup>
<Warning>
**Owner Accountability:** Since Owners control billing and can transfer ownership, choose these individuals carefully and document the selection in your organization's security policies.
</Warning>
## Advanced Scenarios
<AccordionGroup>
<Accordion title="Transferring Ownership" icon="exchange">
Only the current Owner can transfer ownership:
1. Navigate to Organization Settings
2. Go to the "Ownership" section
3. Select the new Owner from the member list
4. Confirm the transfer with your authentication
5. The new Owner receives immediate control
**Important:** This action cannot be undone by the previous Owner. The new Owner must initiate a reverse transfer if needed.
</Accordion>
<Accordion title="Managing Multiple Admins" icon="users-gear">
When you have multiple Admins:
- Document each Admin's area of responsibility
- Use audit logs to track configuration changes
- Consider creating rotation schedules for large teams
- Establish escalation paths for Owner-level decisions
</Accordion>
<Accordion title="Temporary Access" icon="clock">
For contractors or temporary staff:
- Create them as Members with expiration calendar reminders
- Document their access period in your internal systems
- Set calendar reminders to remove them when the contract ends
- Consider using time-limited IDP accounts if your IDP supports it
</Accordion>
</AccordionGroup>
## Troubleshooting
<AccordionGroup>
<Accordion title="User Can't Accept Invitation" icon="circle-exclamation">
**Common causes:**
- Email domain doesn't match verified domain
- User's IDP access hasn't been granted yet
- Invitation link expired
**Solution:** Verify domain verification is complete and resend the invitation.
</Accordion>
<Accordion title="Can't Remove an Admin" icon="user-slash">
**Cause:** Only Owners can remove Admins.
**Solution:** Ask an Owner to perform the removal, or if you need to remove your organization's sole Owner, contact support@cline.bot.
</Accordion>
<Accordion title="Out of Seats" icon="triangle-exclamation">
**When you've reached your license limit:**
- Remove inactive members to free seats
- Revoke pending invitations that are no longer needed
- Upgrade your license to add more seats
</Accordion>
</AccordionGroup>
## Next Steps
Now that you understand member management, proceed with configuring your organization:
<CardGroup cols={2}>
<Card
title="Configure Providers"
icon="plug"
href="/enterprise-solutions/configuration/choosing-your-deployment"
>
Set up API providers for your team to use
</Card>
<Card
title="Monitor Usage"
icon="chart-line"
href="/enterprise-solutions/monitoring/overview"
>
Track team activity and resource consumption
</Card>
</CardGroup>
<Tip>
**Getting Started Fast?** The quickest path is: 1) Invite your team as Members, 2) Configure one API provider, 3) Let your team start using Cline. You can refine roles and settings later.
</Tip>
+3 -8
View File
@@ -3,13 +3,11 @@ title: "Explain Changes"
sidebarTitle: "Explain Changes"
---
<Note>
This feature is only available in **VS Code**. The diff view with inline comments requires VS Code's native diff capabilities.
</Note>
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
@@ -23,9 +21,6 @@ Explain Changes is an AI-powered code review feature that adds inline explanatio
## How It Works
<Note>
Explain Changes requires **[Checkpoints](/features/checkpoints)** to be enabled. Make sure to enable checkpoints in your Cline settings before using this feature.
</Note>
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 -1
View File
@@ -432,6 +432,6 @@ Hooks have a 30 second timeout. As long as your hook completes within this time,
Cline searches for hooks in this order:
1. Project-specific: `.clinerules/hooks/` in workspace root
2. User-global: `~/Documents/Cline/Hooks/`
2. User-global: `~/Documents/Cline/Rules/Hooks/`
Project-specific hooks override global hooks with the same name.
+1 -1
View File
@@ -47,7 +47,7 @@ The interface shows you all available hook types and existing hooks organized by
Hooks are automatically organized by location in the interface:
**Global Hooks** - Apply to all workspaces:
- Stored in `~/Documents/Cline/Hooks/`
- Stored in `~/Documents/Cline/Rules/Hooks/`
- Perfect for personal coding standards and universal rules
**Project-Specific Hooks** - Apply only to current project:
@@ -2,9 +2,6 @@
title: "Explain Changes Command"
sidebarTitle: "/explain-changes"
---
<Note>
This command is only available in **VS Code**. The diff view with inline comments requires VS Code's native diff capabilities.
</Note>
`/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.
+1 -1
View File
@@ -10,7 +10,7 @@ description: "Get Cline up and running in your favorite IDE with these simple in
## Before You Begin
<CardGroup cols={1}>
<Card title="Create Your Account" icon="user-plus" href="https://app.cline.bot/login">
<Card title="Create Your Account" icon="user-plus" href="https://app.cline.bot/signup">
Sign up for a **free Cline account** to get:
- Access to multiple AI models including stealth models
- Seamless setup without managing API keys
+1 -1
View File
@@ -29,7 +29,7 @@ Cline is an open source AI coding agent that brings frontier AI models directly
Master Cline's powerful features and optimize your workflow
</Card>
<Card title="Enterprise" icon="building" href="/enterprise-solutions/overview">
<Card title="Enterprise" icon="building" href="/enterprise-solutions/security-concerns">
Deploy Cline in your organization with confidence
</Card>
</CardGroup>
-1
View File
@@ -38,7 +38,6 @@ For the most updated pricing, please visit: https://www.baseten.co/products/mode
- `deepseek-ai/DeepSeek-R1-0528` - Latest revision of DeepSeek's reasoning model (163K context) - \$2.55/\$5.95 per 1M tokens
- `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
- `deepseek-ai/DeepSeek-V3.2` - Fast general-purpose with enhanced reasoning (163K context) - \$0.77/\$0.77 per 1M tokens
### Production-First Architecture
+1 -1
View File
@@ -125,7 +125,7 @@ const copyWasmFiles = {
const buildEnvVars = {
"import.meta.url": "_importMetaUrl",
"process.env.IS_STANDALONE": JSON.stringify(standalone ? "true" : "false"),
"process.env.IS_STANDALONE": JSON.stringify(standalone),
}
if (production) {
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "claude-dev",
"version": "3.43.0",
"version": "3.39.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-dev",
"version": "3.43.0",
"version": "3.39.2",
"license": "Apache-2.0",
"dependencies": {
"@anthropic-ai/sdk": "^0.37.0",
+1 -1
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.43.0",
"version": "3.40.0",
"icon": "assets/icons/icon.png",
"engines": {
"vscode": "^1.84.0"
-2
View File
@@ -113,7 +113,6 @@ message UsageTransaction {
int32 prompt_tokens = 10;
int32 total_tokens = 11;
string user_id = 12;
string operation = 13;
}
message PaymentTransaction {
@@ -136,5 +135,4 @@ message OrganizationUsageTransaction {
int32 prompt_tokens = 10;
int32 total_tokens = 11;
string user_id = 12;
string operation = 13;
}
+3 -41
View File
@@ -77,45 +77,7 @@ message TaskCompleteData {
// Data for PreCompact hook
message PreCompactData {
// Task identification
string task_id = 1;
string ulid = 2;
// Context size information
int64 context_size = 3; // Number of messages in API conversation history
// Compaction strategy indicating how conversation history is managed:
// * auto-condense: AI-powered compression using summarize_task tool
// * standard-truncation-firstpair: Keep only the original task (used during auto-condense)
// * standard-truncation-lasthalf: Keep first pair + most recent 50% of conversation
// * standard-truncation-lastquarter: Keep first pair + most recent 25% of conversation (aggressive)
string compaction_strategy = 4;
// API request tracking
int64 previous_api_req_index = 5; // Index of last API request in clineMessages
// Token usage data from last API request
int64 tokens_in = 6;
int64 tokens_out = 7;
int64 tokens_in_cache = 8;
int64 tokens_out_cache = 9;
// Truncation information (if applicable)
int32 deleted_range_start = 10; // Start index of deleted conversation range
int32 deleted_range_end = 11; // End index of deleted conversation range
// Context JSON file path
// Path to a temporary JSON file containing the full API conversation history
// The file contains an array of message objects with role and content
// Hooks can read this file to analyze conversation contents before compaction
// This file will be automatically cleaned up after the hook completes
string context_json_path = 12;
// Context raw/formatted file path
// Path to a temporary text file containing the complete context window sent to the LLM
// This includes the system prompt, environment details, conversation history, and all formatting
// Represents the actual input the LLM receives (format varies by provider)
// Use this to analyze total context size, overhead, and exactly what the model sees
// This file will be automatically cleaned up after the hook completes
string context_raw_path = 13;
int64 context_size = 1;
int32 messages_to_compact = 2;
string compaction_strategy = 3;
}
-13
View File
@@ -103,7 +103,6 @@ message OpenRouterModelInfo {
optional string name = 13;
optional double temperature = 14;
optional bool supports_reasoning = 15;
optional ApiFormat api_format = 16;
}
// Shared response message for model information
@@ -378,8 +377,6 @@ message OcaModelInfo {
optional string banner = 16;
// Canonical model identifier as reported by OCA
string model_name = 17;
// The API format used by this model
optional ApiFormat api_format = 18;
}
// Aggregated OCA model catalog keyed by model identifier
@@ -434,14 +431,6 @@ enum ApiProvider {
NOUSRESEARCH = 39;
}
enum ApiFormat {
ANTHROPIC_CHAT = 0;
GEMINI_CHAT = 1;
OPENAI_CHAT = 2;
R1_CHAT = 3;
OPENAI_RESPONSES = 4;
}
// Model info for OpenAI-compatible models
message OpenAiCompatibleModelInfo {
optional int64 max_tokens = 1;
@@ -458,7 +447,6 @@ message OpenAiCompatibleModelInfo {
repeated ModelTier tiers = 12;
optional double temperature = 13;
optional bool is_r1_format_required = 14;
optional ApiFormat api_format = 15;
}
// Model info for LiteLLM models
@@ -476,7 +464,6 @@ message LiteLLMModelInfo {
optional string description = 11;
repeated ModelTier tiers = 12;
optional double temperature = 13;
optional ApiFormat api_format = 14;
}
// Main ApiConfiguration message
+1 -17
View File
@@ -8,25 +8,9 @@ option go_package = "github.com/cline/grpc-go/cline";
option java_multiple_files = true;
option java_package = "bot.cline.proto";
// SlashService provides methods for managing slash commands
// SlashService provides methods for managing slash
service SlashService {
// Sends button click message
rpc reportBug(StringRequest) returns (Empty);
rpc condense(StringRequest) returns (Empty);
// Get available slash commands for autocomplete (used by CLI)
rpc getAvailableSlashCommands(EmptyRequest) returns (SlashCommandsResponse);
}
// Slash command definition for autocomplete
message SlashCommandInfo {
string name = 1; // Command name without slash, e.g., "newtask", "smol"
string description = 2; // Human-readable description
string section = 3; // "default", "custom", or "cli"
bool cli_compatible = 4; // false for VS Code-only commands like explain-changes
}
// Response containing all available slash commands
message SlashCommandsResponse {
repeated SlashCommandInfo commands = 1;
}
-4
View File
@@ -227,8 +227,6 @@ message Settings {
optional OpenAiCompatibleModelInfo plan_mode_aihubmix_model_info = 131;
optional string act_mode_aihubmix_model_id = 132;
optional OpenAiCompatibleModelInfo act_mode_aihubmix_model_info = 133;
optional bool cline_web_tools_enabled = 134;
optional bool hooks_enabled = 135;
}
message DictationSettings {
@@ -367,8 +365,6 @@ message UpdateSettingsRequest {
optional string cline_env = 31;
optional bool native_tool_call_enabled = 32;
optional OnboardingModelGroup onboarding_models = 33;
optional bool cline_web_tools_enabled = 34;
optional bool enable_parallel_tool_calling = 35;
}
message UpdateTerminalConnectionTimeoutRequest {
-4
View File
@@ -2,7 +2,6 @@ import * as vscode from "vscode"
import {
cleanupMcpMarketplaceCatalogFromGlobalState,
migrateCustomInstructionsToGlobalRules,
migrateHooksEnabledToBoolean,
migrateTaskHistoryToFile,
migrateWelcomeViewCompleted,
migrateWorkspaceToGlobalStorage,
@@ -63,9 +62,6 @@ export async function initialize(context: vscode.ExtensionContext): Promise<Webv
// Ensure taskHistory.json exists and migrate legacy state (runs once)
await migrateTaskHistoryToFile(context)
// Migrate hooksEnabled from ClineFeatureSetting to boolean (one-time cleanup)
await migrateHooksEnabledToBoolean(context)
// Clean up MCP marketplace catalog from global state (moved to disk cache)
await cleanupMcpMarketplaceCatalogFromGlobalState(context)
-3
View File
@@ -51,7 +51,6 @@ export interface ApiHandler {
createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: ClineTool[], useResponseApi?: boolean): ApiStream
getModel(): ApiHandlerModel
getApiStreamUsage?(): Promise<ApiStreamUsageChunk | undefined>
abort?(): void
}
export interface ApiHandlerModel {
@@ -180,8 +179,6 @@ function createHandlerForProvider(
openAiNativeApiKey: options.openAiNativeApiKey,
reasoningEffort: mode === "plan" ? options.planModeReasoningEffort : options.actModeReasoningEffort,
apiModelId: mode === "plan" ? options.planModeApiModelId : options.actModeApiModelId,
thinkingBudgetTokens:
mode === "plan" ? options.planModeThinkingBudgetTokens : options.actModeThinkingBudgetTokens,
})
case "deepseek":
return new DeepSeekHandler({
+1 -1
View File
@@ -121,7 +121,7 @@ export class ClaudeCodeHandler implements ApiHandler {
function: {
id: content.id,
name: content.name,
arguments: JSON.stringify(content.input),
arguments: content.input,
},
},
}
+1 -1
View File
@@ -199,7 +199,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"].includes(this.getModel().id)) {
if (["x-ai/grok-code-fast-1", "minimax/minimax-m2", "stealth/microwave"].includes(this.getModel().id)) {
totalCost = 0
}
+20 -21
View File
@@ -16,6 +16,9 @@ import { RetriableError, withRetry } from "../retry"
import { convertAnthropicMessageToGemini } from "../transform/gemini-format"
import { ApiStream } from "../transform/stream"
// Define a default TTL for the cache (e.g., 15 minutes in seconds)
const _DEFAULT_CACHE_TTL_SECONDS = 900
const rateLimitPatterns = [/got status: 429/i, /429 Too Many Requests/i, /rate limit exceeded/i, /too many requests/i]
interface GeminiHandlerOptions extends CommonApiHandlerOptions {
@@ -117,17 +120,15 @@ export class GeminiHandler implements ApiHandler {
const _thinkingBudget = this.options.thinkingBudgetTokens ?? 0
const maxBudget = info.thinkingConfig?.maxBudget ?? 24576
const thinkingBudget = Math.min(_thinkingBudget, maxBudget)
// When ThinkingLevel is defined, thinking budget cannot be zero
// When ThinkingLevel is defineded, thinking budget cannot be zero
// and only level is used to control thinking behavior.
// Only set thinkingLevel for models that support it
let thinkingLevel: ThinkingLevel | undefined
if (info.thinkingConfig?.supportsThinkingLevel) {
const level = this.options.thinkingLevel || info.thinkingConfig.geminiThinkingLevel
if (level === "high") {
thinkingLevel = ThinkingLevel.HIGH
} else if (level === "low") {
thinkingLevel = ThinkingLevel.LOW
}
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
}
// Set up base generation config
@@ -140,18 +141,16 @@ export class GeminiHandler implements ApiHandler {
temperature: info.temperature ?? 1,
}
// Add thinking config only if the model supports it
if (info.thinkingConfig) {
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,
}
// 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,
}
// Generate content using the configured parameters
+2 -18
View File
@@ -30,25 +30,9 @@ export class MistralHandler implements ApiHandler {
}
try {
// Create HTTP client with custom fetch for proxy support
// The Mistral SDK's HTTPClient passes a Request object to the fetcher,
// but we need to extract the URL and init options to pass to our fetch wrapper
// which properly handles proxy configuration in standalone mode (JetBrains/CLI)
const httpClient = new HTTPClient({
fetcher: async (input: RequestInfo | URL, init?: RequestInit) => {
// Handle both string/URL and Request object inputs
if (input instanceof Request) {
return fetch(input.url, {
method: input.method,
headers: input.headers,
body: input.body,
redirect: input.redirect,
signal: input.signal,
// duplex is required when sending a body stream in Node.js/undici
duplex: input.body ? "half" : undefined,
...init,
} as RequestInit)
}
return fetch(input, init)
fetcher: (request) => {
return fetch(request)
},
})
-4
View File
@@ -121,8 +121,4 @@ export class OllamaHandler implements ApiHandler {
},
}
}
abort(): void {
this.client?.abort()
}
}
+118 -65
View File
@@ -1,18 +1,10 @@
import {
ModelInfo,
OpenAiCompatibleModelInfo,
OpenAiNativeModelId,
openAiNativeDefaultModelId,
openAiNativeModels,
} from "@shared/api"
import { ModelInfo, OpenAiNativeModelId, openAiNativeDefaultModelId, openAiNativeModels } from "@shared/api"
import { calculateApiCostOpenAI } from "@utils/cost"
import OpenAI from "openai"
import type { ChatCompletionReasoningEffort, ChatCompletionTool } from "openai/resources/chat/completions"
import { Logger } from "@/services/logging/Logger"
import { ClineStorageMessage } from "@/shared/messages/content"
import { fetch } from "@/shared/net"
import { ApiFormat } from "@/shared/proto/cline/models"
import { isGPT5ModelFamily } from "@/utils/model-utils"
import { ApiHandler, CommonApiHandlerOptions } from "../"
import { withRetry } from "../retry"
import { convertToOpenAiMessages } from "../transform/openai-format"
@@ -23,7 +15,6 @@ import { getOpenAIToolParams, ToolCallProcessor } from "../transform/tool-call-p
interface OpenAiNativeHandlerOptions extends CommonApiHandlerOptions {
openAiNativeApiKey?: string
reasoningEffort?: string
thinkingBudgetTokens?: number
apiModelId?: string
}
@@ -70,12 +61,13 @@ export class OpenAiNativeHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: ChatCompletionTool[]): ApiStream {
// Responses API requires tool format to be set to OPENAI_RESPONSES with native tools calling enabled
if (this.getModel()?.info?.apiFormat === ApiFormat.OPENAI_RESPONSES) {
if (!tools?.length) {
throw new Error("Native Tool Call must be enabled in your setting for OpenAI Responses API")
}
async *createMessage(
systemPrompt: string,
messages: ClineStorageMessage[],
tools?: ChatCompletionTool[],
useResponseFormat = false,
): ApiStream {
if (useResponseFormat) {
yield* this.createResponseStream(systemPrompt, messages, tools)
} else {
yield* this.createCompletionStream(systemPrompt, messages, tools)
@@ -91,57 +83,119 @@ export class OpenAiNativeHandler implements ApiHandler {
const model = this.getModel()
const toolCallProcessor = new ToolCallProcessor()
// Handle o1 models separately as they don't support streaming
if (model.info.supportsStreaming === false) {
const response = await client.chat.completions.create({
model: model.id,
messages: [{ role: "user", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
})
yield {
type: "text",
text: response.choices[0]?.message.content || "",
}
yield* this.yieldUsage(model.info, response.usage)
return
}
const systemRole = model.info.systemRole ?? "system"
const includeReasoning = this.options.thinkingBudgetTokens && model.info.supportsReasoningEffort
const includeTools = model.info.supportsTools ?? true
const reasoningEffort = includeReasoning
? (this.options.reasoningEffort as ChatCompletionReasoningEffort) || "medium"
: undefined
const stream = await client.chat.completions.create({
model: model.id,
messages: [{ role: systemRole, content: systemPrompt }, ...convertToOpenAiMessages(messages)],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: reasoningEffort,
...(model.info.temperature !== undefined ? { temperature: model.info.temperature } : {}),
...(includeTools ? getOpenAIToolParams(tools, isGPT5ModelFamily(model.id)) : {}),
})
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta
if (delta?.content) {
switch (model.id) {
case "o1":
case "o1-preview":
case "o1-mini": {
// o1 doesn't support streaming, non-1 temp, or system prompt
const response = await client.chat.completions.create({
model: model.id,
messages: [{ role: "user", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
})
yield {
type: "text",
text: delta.content,
text: response.choices[0]?.message.content || "",
}
}
if (delta?.tool_calls) {
try {
yield* toolCallProcessor.processToolCallDeltas(delta.tool_calls)
} catch (error) {
console.error("Error processing tool call delta:", error, delta.tool_calls)
yield* this.yieldUsage(model.info, response.usage)
break
}
case "o4-mini":
case "o3":
case "o3-mini": {
const stream = await client.chat.completions.create({
model: model.id,
messages: [{ role: "developer", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: (this.options.reasoningEffort as ChatCompletionReasoningEffort) || "medium",
})
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta
if (delta?.content) {
yield {
type: "text",
text: delta.content,
}
}
if (chunk.usage) {
// Only last chunk contains usage
yield* this.yieldUsage(model.info, chunk.usage)
}
}
break
}
case "gpt-5-2025-08-07":
case "gpt-5-mini-2025-08-07":
case "gpt-5-nano-2025-08-07":
case "gpt-5.1-2025-11-13":
case "gpt-5.1-chat-latest":
case "gpt-5.1": {
const stream = await client.chat.completions.create({
model: model.id,
temperature: 1,
messages: [{ role: "developer", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
stream: true,
stream_options: { include_usage: true },
reasoning_effort: (this.options.reasoningEffort as ChatCompletionReasoningEffort) || "medium",
...getOpenAIToolParams(tools),
})
if (chunk.usage) {
// Only last chunk contains usage
yield* this.yieldUsage(model.info, chunk.usage)
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta
if (delta?.content) {
yield {
type: "text",
text: delta.content,
}
}
if (delta?.tool_calls) {
try {
yield* toolCallProcessor.processToolCallDeltas(delta.tool_calls)
} catch (error) {
console.error("Error processing tool call delta:", error, delta.tool_calls)
}
}
if (chunk.usage) {
// Only last chunk contains usage - stream is ending
yield* this.yieldUsage(model.info, chunk.usage)
}
}
break
}
default: {
const stream = await client.chat.completions.create({
model: model.id,
// max_completion_tokens: this.getModel().info.maxTokens,
temperature: 0,
messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)],
stream: true,
stream_options: { include_usage: true },
...getOpenAIToolParams(tools),
})
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta
if (delta?.content) {
yield {
type: "text",
text: delta.content,
}
}
if (delta?.tool_calls) {
yield* toolCallProcessor.processToolCallDeltas(delta.tool_calls)
}
if (chunk.usage) {
// Only last chunk contains usage - stream is ending
yield* this.yieldUsage(model.info, chunk.usage)
}
}
}
}
}
@@ -149,7 +203,7 @@ export class OpenAiNativeHandler implements ApiHandler {
private async *createResponseStream(
systemPrompt: string,
messages: ClineStorageMessage[],
tools: ChatCompletionTool[],
tools?: ChatCompletionTool[],
): ApiStream {
const client = this.ensureClient()
const model = this.getModel()
@@ -348,16 +402,15 @@ export class OpenAiNativeHandler implements ApiHandler {
}
}
getModel(): { id: OpenAiNativeModelId; info: OpenAiCompatibleModelInfo } {
getModel(): { id: OpenAiNativeModelId; info: ModelInfo } {
const modelId = this.options.apiModelId
if (modelId && modelId in openAiNativeModels) {
const id = modelId as OpenAiNativeModelId
const info = openAiNativeModels[id]
return { id, info: { ...info } }
return { id, info: openAiNativeModels[id] }
}
return {
id: openAiNativeDefaultModelId,
info: { ...openAiNativeModels[openAiNativeDefaultModelId] },
info: openAiNativeModels[openAiNativeDefaultModelId],
}
}
}
+72 -29
View File
@@ -83,41 +83,84 @@ export class VertexHandler implements ApiHandler {
// Claude implementation
const budget_tokens = this.options.thinkingBudgetTokens || 0
// Use model metadata to determine if reasoning should be enabled
const reasoningOn = (model.info.supportsReasoning ?? false) && budget_tokens !== 0
const reasoningOn = !!(
(modelId.includes("3-7") ||
modelId.includes("sonnet-4") ||
modelId.includes("opus-4") ||
modelId.includes("haiku-4-5")) &&
budget_tokens !== 0
)
// Tools are available only when native tools are enabled.
const nativeToolsOn = tools?.length ? tools?.length > 0 : false
const anthropicMessages = sanitizeAnthropicMessages(messages, model.info.supportsPromptCache ?? false)
let stream
const stream = await clientAnthropic.beta.messages.create(
{
model: modelId,
max_tokens: model.info.maxTokens || 8192,
thinking: reasoningOn ? { type: "enabled", budget_tokens: budget_tokens } : undefined,
temperature: reasoningOn ? undefined : 0,
system: [
switch (modelId) {
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-3-7-sonnet@20250219":
case "claude-3-5-sonnet-v2@20241022":
case "claude-3-5-sonnet@20240620":
case "claude-3-5-haiku@20241022":
case "claude-3-opus@20240229":
case "claude-3-haiku@20240307": {
const anthropicMessages = sanitizeAnthropicMessages(messages, true)
stream = await clientAnthropic.beta.messages.create(
{
text: systemPrompt,
type: "text",
cache_control: model.info.supportsPromptCache ? { type: "ephemeral" } : undefined,
model: modelId,
max_tokens: model.info.maxTokens || 8192,
thinking: reasoningOn ? { type: "enabled", budget_tokens: budget_tokens } : undefined,
temperature: reasoningOn ? undefined : 0,
system: [
{
text: systemPrompt,
type: "text",
cache_control: { type: "ephemeral" },
},
],
messages: anthropicMessages,
stream: true,
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.
// - any: tells Claude that it must use one of the provided tools, but doesnt force a particular tool.
// NOTE: Forcing tool use when tools are provided will result in error when thinking is also enabled.
tool_choice: nativeToolsOn && !reasoningOn ? { type: "any" } : undefined,
},
],
messages: anthropicMessages,
stream: true,
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.
// - any: tells Claude that it must use one of the provided tools, but doesnt force a particular tool.
// NOTE: Forcing tool use when tools are provided will result in error when thinking is also enabled.
tool_choice: nativeToolsOn && !reasoningOn ? { type: "any" } : undefined,
},
{
headers: {},
},
)
{
headers: {},
},
)
break
}
default: {
stream = await clientAnthropic.beta.messages.create({
model: modelId,
max_tokens: model.info.maxTokens || 8192,
temperature: 0,
system: [
{
text: systemPrompt,
type: "text",
},
],
messages: sanitizeAnthropicMessages(messages, false),
stream: true,
tools: tools?.length ? (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.
// - any: tells Claude that it must use one of the provided tools, but doesnt force a particular tool.
tool_choice: tools ? { type: "any" } : undefined,
})
break
}
}
const lastStartedToolCall = { id: "", name: "", arguments: "" }
+1 -10
View File
@@ -2,14 +2,6 @@ import { Anthropic } from "@anthropic-ai/sdk"
import { Content, GenerateContentResponse, Part } from "@google/genai"
import { ClineStorageMessage } from "@/shared/messages/content"
// Source: https://ai.google.dev/gemini-api/docs/thought-signatures#faqs
// While injecting custom function call blocks into the request is strongly discouraged,
// in cases where it can't be avoided, e.g. providing information to the model on function
// calls and responses that were executed deterministically by the client, or transferring a
// trace from a different model that does not include thought signatures, you can set the following dummy signatures of either
// "context_engineering_is_the_way_to_go" or "skip_thought_signature_validator" in the thought signature field to skip validation.
const GEMINI_DUMMY_THOUGHT_SIGNATURE = "skip_thought_signature_validator"
export function convertAnthropicContentToGemini(content: string | ClineStorageMessage["content"]): Part[] {
if (typeof content === "string") {
return [{ text: content }]
@@ -35,8 +27,7 @@ export function convertAnthropicContentToGemini(content: string | ClineStorageMe
name: block.name,
args: block.input as Record<string, unknown>,
},
// Thought signature is required, so provide a dummy one if not present
thoughtSignature: block.signature || GEMINI_DUMMY_THOUGHT_SIGNATURE,
thoughtSignature: block.signature,
}
case "tool_result":
return {
+1 -1
View File
@@ -187,7 +187,7 @@ export async function createOpenRouterStream(
openRouterProviderSorting = undefined
}
// Skip reasoning for models that don't support it (e.g., devstral, grok-4)
// Skip reasoning for models that don't support it (e.g., microwave, grok-4)
const includeReasoning = !shouldSkipReasoningForModel(model.id)
// @ts-ignore-next-line
@@ -74,12 +74,12 @@ export class ToolCallProcessor {
}
}
export function getOpenAIToolParams(tools?: OpenAITool[], enableParallelToolCalls: boolean = false) {
export function getOpenAIToolParams(tools?: OpenAITool[]) {
return tools?.length
? {
tools,
tool_choice: tools ? ("auto" as ChatCompletionToolChoiceOption) : undefined,
parallel_tool_calls: enableParallelToolCalls ? true : false,
parallel_tool_calls: tools ? false : undefined, // Set to false to force single tool calls
}
: {
tools: undefined,
-4
View File
@@ -24,10 +24,6 @@ export const toolParamNames = [
"url",
"coordinate",
"text",
"query",
"allowed_domains",
"blocked_domains",
"prompt",
"server_name",
"tool_name",
"arguments",
+5 -5
View File
@@ -16,10 +16,10 @@ interface TaskReconstructionResult {
/**
* Reconstructs task history from existing task folders
* @param showNotifications Whether to show user-facing notifications and dialogs
* @param isManuallyCalled Whether the function was called manually by the user through command palette
* @returns Reconstruction result or null if cancelled
*/
export async function reconstructTaskHistory(showNotifications = true): Promise<TaskReconstructionResult | null> {
export async function reconstructTaskHistory(isManuallyCalled = true): Promise<TaskReconstructionResult | null> {
try {
// Show confirmation dialog using HostProvider
const proceed = await HostProvider.window.showMessage({
@@ -35,7 +35,7 @@ export async function reconstructTaskHistory(showNotifications = true): Promise<
return null
}
if (showNotifications) {
if (isManuallyCalled) {
// Show initial progress message
HostProvider.window.showMessage({
type: ShowMessageType.INFORMATION,
@@ -46,7 +46,7 @@ export async function reconstructTaskHistory(showNotifications = true): Promise<
const result = await performTaskHistoryReconstruction()
// Show results
if (showNotifications) {
if (isManuallyCalled) {
if (result.errors.length > 0) {
const errorMessage = `Reconstruction completed with warnings:\n- Reconstructed: ${result.reconstructedTasks} tasks\n- Skipped: ${result.skippedTasks} tasks\n- Errors: ${result.errors.length}\n\nFirst few errors:\n${result.errors.slice(0, 3).join("\n")}`
@@ -65,7 +65,7 @@ export async function reconstructTaskHistory(showNotifications = true): Promise<
return result
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
if (showNotifications) {
if (isManuallyCalled) {
HostProvider.window.showMessage({
type: ShowMessageType.ERROR,
message: `Failed to reconstruct task history: ${errorMessage}`,
@@ -0,0 +1,102 @@
import { Anthropic } from "@anthropic-ai/sdk"
import { ApiHandler } from "@core/api"
import { ClineApiReqInfo, ClineMessage } from "@shared/ExtensionMessage"
import { getContextWindowInfo } from "./context-window-utils"
class ContextManager {
getNewContextMessagesAndMetadata(
apiConversationHistory: Anthropic.Messages.MessageParam[],
clineMessages: ClineMessage[],
api: ApiHandler,
conversationHistoryDeletedRange: [number, number] | undefined,
previousApiReqIndex: number,
) {
let updatedConversationHistoryDeletedRange = false
// If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request
if (previousApiReqIndex >= 0) {
const previousRequest = clineMessages[previousApiReqIndex]
if (previousRequest && previousRequest.text) {
const { tokensIn, tokensOut, cacheWrites, cacheReads }: ClineApiReqInfo = JSON.parse(previousRequest.text)
const totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0)
const { maxAllowedSize } = getContextWindowInfo(api)
// This is the most reliable way to know when we're close to hitting the context window.
if (totalTokens >= maxAllowedSize) {
// Since the user may switch between models with different context windows, truncating half may not be enough (ie if switching from claude 200k to deepseek 64k, half truncation will only remove 100k tokens, but we need to remove much more)
// So if totalTokens/2 is greater than maxAllowedSize, we truncate 3/4 instead of 1/2
// FIXME: truncating the conversation in a way that is optimal for prompt caching AND takes into account multi-context window complexity is something we need to improve
const keep = totalTokens / 2 > maxAllowedSize ? "quarter" : "half"
// NOTE: it's okay that we overwriteConversationHistory in resume task since we're only ever removing the last user message and not anything in the middle which would affect this range
conversationHistoryDeletedRange = this.getNextTruncationRange(
apiConversationHistory,
conversationHistoryDeletedRange,
keep,
)
updatedConversationHistoryDeletedRange = true
}
}
}
// conversationHistoryDeletedRange is updated only when we're close to hitting the context window, so we don't continuously break the prompt cache
const truncatedConversationHistory = this.getTruncatedMessages(apiConversationHistory, conversationHistoryDeletedRange)
return {
conversationHistoryDeletedRange: conversationHistoryDeletedRange,
updatedConversationHistoryDeletedRange: updatedConversationHistoryDeletedRange,
truncatedConversationHistory: truncatedConversationHistory,
}
}
public getNextTruncationRange(
apiMessages: Anthropic.Messages.MessageParam[],
currentDeletedRange: [number, number] | undefined,
keep: "half" | "quarter",
): [number, number] {
// Since we always keep the first message, currentDeletedRange[0] will always be 1 (for now until we have a smarter truncation algorithm)
const rangeStartIndex = 1
const startOfRest = currentDeletedRange ? currentDeletedRange[1] + 1 : 1
let messagesToRemove: number
if (keep === "half") {
// Remove half of remaining user-assistant pairs
// We first calculate half of the messages then divide by 2 to get the number of pairs.
// After flooring, we multiply by 2 to get the number of messages.
// Note that this will also always be an even number.
messagesToRemove = Math.floor((apiMessages.length - startOfRest) / 4) * 2 // Keep even number
} else {
// Remove 3/4 of remaining user-assistant pairs
// We calculate 3/4ths of the messages then divide by 2 to get the number of pairs.
// After flooring, we multiply by 2 to get the number of messages.
// Note that this will also always be an even number.
messagesToRemove = Math.floor(((apiMessages.length - startOfRest) * 3) / 4 / 2) * 2
}
let rangeEndIndex = startOfRest + messagesToRemove - 1
// Make sure the last message being removed is a user message, so that the next message after the initial task message is an assistant message. This preservers the user-assistant-user-assistant structure.
// NOTE: anthropic format messages are always user-assistant-user-assistant, while openai format messages can have multiple user messages in a row (we use anthropic format throughout cline)
if (apiMessages[rangeEndIndex].role !== "user") {
rangeEndIndex -= 1
}
// this is an inclusive range that will be removed from the conversation history
return [rangeStartIndex, rangeEndIndex]
}
public getTruncatedMessages(
messages: Anthropic.Messages.MessageParam[],
deletedRange: [number, number] | undefined,
): Anthropic.Messages.MessageParam[] {
if (!deletedRange) {
return messages
}
const [start, end] = deletedRange
// the range is inclusive - both start and end indices and everything in between will be removed from the final result.
// NOTE: if you try to console log these, don't forget that logging a reference to an array may not provide the same result as logging a slice() snapshot of that array at that exact moment. The following DOES in fact include the latest assistant message.
return [...messages.slice(0, start), ...messages.slice(end + 1)]
}
}
@@ -55,44 +55,6 @@ export class ContextManager {
this.contextHistoryUpdates = new Map()
}
/**
* Extracts text from a content block, handling both regular text blocks and tool_result wrappers.
* For tool_result blocks, extracts text from content[0] (native tool calling format).
* @returns The text content, or null if no text could be extracted
*/
private getTextFromBlock(block: Anthropic.Messages.ContentBlockParam): string | null {
if (block.type === "text") {
return block.text
}
if (block.type === "tool_result" && Array.isArray(block.content)) {
const inner = block.content[0]
if (inner && "type" in inner && inner.type === "text") {
return inner.text
}
}
return null
}
/**
* Sets text in a content block, handling both regular text blocks and tool_result wrappers.
* For tool_result blocks, sets text in content[0] (native tool calling format).
* @returns true if text was set successfully, false otherwise
*/
private setTextInBlock(block: Anthropic.Messages.ContentBlockParam, text: string): boolean {
if (block.type === "text") {
block.text = text
return true
}
if (block.type === "tool_result" && Array.isArray(block.content)) {
const inner = block.content[0]
if (inner && "type" in inner && inner.type === "text") {
inner.text = text
return true
}
}
return false
}
/**
* public function for loading contextHistoryUpdates from disk, if it exists
*/
@@ -528,8 +490,8 @@ export class ContextManager {
if (Array.isArray(message.content)) {
const block = message.content[blockIndex]
if (block) {
this.setTextInBlock(block, latestChange[2][0])
if (block && block.type === "text") {
block.text = latestChange[2][0]
}
}
}
@@ -826,29 +788,22 @@ export class ContextManager {
const message = apiMessages[i]
if (message.role === "user" && Array.isArray(message.content) && message.content.length > 0) {
const firstBlock = message.content[0]
// Extract text from either a direct text block or from inside a tool_result wrapper (native tool calling)
const firstBlockText = this.getTextFromBlock(firstBlock)
if (firstBlockText) {
const result = this.parseToolCallWithFormat(firstBlockText)
if (firstBlock.type === "text") {
const result = this.parseToolCallWithFormat(firstBlock.text)
let foundNormalFileRead = false
if (result) {
const [toolName, filePath, contentBlockIndex, headerText] = result
if (toolName === "read_file") {
// For native tool calling format, we assume contentBlockIndex=0 which is what happens naturally
this.handleReadFileToolCall(i, filePath, fileReadIndices, contentBlockIndex, headerText)
foundNormalFileRead = true
} else if (toolName === "replace_in_file" || toolName === "write_to_file") {
// For native tool calling format, the content is assumed to always in the same block (index=0 inside tool_result)
// For the XML format, the old format has the file contents in index=1 whereas the new format has it in index=0
// old format has the file contents in index=1 whereas the new format has it in index=0
// in either case we need to extract the correct contents
let blockText: string | undefined
if (firstBlock.type === "tool_result") {
blockText = firstBlockText
} else if (contentBlockIndex === 0) {
// remaining cases are for type="text"
blockText = firstBlockText
} else if (contentBlockIndex === 1 && message.content.length > 1) {
if (contentBlockIndex == 0) {
blockText = firstBlock.text
} else if (contentBlockIndex == 1 && message.content.length > 1) {
const secondBlock = message.content[1]
if (secondBlock.type === "text") {
blockText = secondBlock.text
@@ -870,20 +825,18 @@ export class ContextManager {
// file mentions can happen in most other user message blocks
if (!foundNormalFileRead) {
// search over indices 0-2 inclusive for file mentions
// this is a heuristic to catch most occurrences without looping over all inner indices
// Search over indices up to 0-2 for file mentions
// Only search index N if there's at least one more element after it
for (const candidateIndex of [0, 1, 2]) {
if (candidateIndex >= message.content.length) {
if (message.content.length <= candidateIndex + 1) {
break
}
const block = message.content[candidateIndex]
// Extract text from either a direct text block or from inside a tool_result wrapper
const blockText = this.getTextFromBlock(block)
if (blockText) {
if (block.type === "text") {
const [hasFileRead, filePaths] = this.handlePotentialFileMentionCalls(
i,
blockText,
block.text,
fileReadIndices,
thisExistingFileReads, // file reads we've already replaced in this text in the latest version of this updated text
candidateIndex,
@@ -978,7 +931,7 @@ export class ContextManager {
) {
const indices = fileReadIndices.get(filePath) || []
if (contentBlockIndex === 1) {
if (contentBlockIndex == 1) {
// the original tool call format
indices.push([i, EditType.READ_FILE_TOOL, "", formatResponse.duplicateFileReadNotice(), contentBlockIndex])
} else {
@@ -1066,12 +1019,9 @@ export class ContextManager {
// can assume that this content will exist, otherwise it would not have been in fileReadIndices
const messageContent = apiMessages[messageIndex]?.content
if (!baseText && Array.isArray(messageContent) && messageContent.length > innerIndex) {
// contentBlock can either be the type="text" dict or type="tool_result" dict which has its own content array
// but we currently assume the content we will overwrite is at index=0 in this content array
const contentBlock = messageContent[innerIndex]
const extractedText = this.getTextFromBlock(contentBlock)
if (extractedText) {
baseText = extractedText
if (contentBlock.type === "text") {
baseText = contentBlock.text
}
}
@@ -1178,9 +1128,7 @@ export class ContextManager {
// looping over inner indices of messages
const block = message.content[blockIndex]
// Extract text from either a direct text block or from inside a tool_result wrapper (native tool calling)
const blockText = this.getTextFromBlock(block)
if (blockText) {
if (block.type === "text" && block.text) {
// true if we just altered it, or it was altered before
if (hasExistingAlterations) {
const innerTuple = this.contextHistoryUpdates.get(i)
@@ -1196,7 +1144,7 @@ export class ContextManager {
if (updates.length > 1) {
originalTextLength = updates[updates.length - 2][2][0].length // handles case if we have multiple updates for same text block
} else {
originalTextLength = blockText.length
originalTextLength = block.text.length
}
const newTextLength = latestUpdate[2][0].length // replacement text
@@ -1209,11 +1157,11 @@ export class ContextManager {
}
} else {
// reach here if there was one inner index with an update, but now we are at a different index, so updates is not defined
totalCharCount += blockText.length
totalCharCount += block.text.length
}
} else {
// reach here if there's no alterations for this outer index, meaning each inner index won't have any changes either
totalCharCount += blockText.length
totalCharCount += block.text.length
}
} else if (block.type === "image" && block.source) {
if (block.source.type === "base64" && block.source.data) {
@@ -100,190 +100,6 @@ describe("ContextManager", () => {
})
})
describe("applyContextOptimizations", () => {
let contextManager: ContextManager
beforeEach(() => {
contextManager = new ContextManager()
})
it("detects duplicate file reads across write_to_file, replace_in_file, and file mentions (normal tool calling)", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial task" },
{ role: "assistant", content: "Response" },
{
role: "user",
content: [
{
type: "text",
text: "[write_to_file for 'test.txt'] Result:\nThe content was successfully saved to test.txt.\n\nHere is the full, updated content of the file that was saved:\n\n<final_file_content path=\"test.txt\">\ntest\n\n</final_file_content>",
},
{
type: "text",
text: "<environment_details>\n# Visual Studio Code Visible Files\ntest.txt\n\n# Current Mode\nACT MODE\n</environment_details>",
},
],
},
{ role: "assistant", content: "Response" },
{
role: "user",
content: [
{
type: "text",
text: "[replace_in_file for 'test.txt'] Result:\nThe content was successfully saved to test.txt.\n\nHere is the full, updated content of the file that was saved:\n\n<final_file_content path=\"test.txt\">\ntest 2\n\n</final_file_content>",
},
{
type: "text",
text: "<environment_details>\n# Visual Studio Code Visible Files\ntest.txt\n\n# Current Mode\nACT MODE\n</environment_details>",
},
],
},
{ role: "assistant", content: "Response" },
{
role: "user",
content: [
{
type: "text",
text: "[TASK RESUMPTION] This task was interrupted just now. The conversation may have been incomplete.",
},
{
type: "text",
text: "New message to respond to:\n<user_message>\n'test.txt' (see below for file content) tell me whats in this file\n</user_message>\n\n<file_content path=\"test.txt\">\ntest 2\n\n</file_content>",
},
],
},
]
const timestamp = Date.now()
const [didUpdate, indices] = contextManager.applyContextOptimizations(messages, 2, timestamp)
expect(didUpdate).to.equal(true)
expect(indices.size).to.equal(2)
expect(indices.has(2)).to.equal(true)
expect(indices.has(4)).to.equal(true)
expect(indices.has(6)).to.equal(false)
})
it("returns false when no duplicate file reads exist", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial task" },
{ role: "assistant", content: "Response" },
{
role: "user",
content: [
{
type: "text",
text: "[write_to_file for 'test.txt'] Result:\n<final_file_content path=\"test.txt\">\ntest\n\n</final_file_content>",
},
],
},
{ role: "assistant", content: "Response" },
{
role: "user",
content: [
{
type: "text",
text: "[write_to_file for 'other.txt'] Result:\n<final_file_content path=\"other.txt\">\nother content\n\n</final_file_content>",
},
],
},
]
const [didUpdate, indices] = contextManager.applyContextOptimizations(messages, 2, Date.now())
expect(didUpdate).to.equal(false)
expect(indices.size).to.equal(0)
})
it("returns false for empty messages beyond startFromIndex", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial task" },
{ role: "assistant", content: "Response" },
]
const [didUpdate, indices] = contextManager.applyContextOptimizations(messages, 2, Date.now())
expect(didUpdate).to.equal(false)
expect(indices.size).to.equal(0)
})
it("detects duplicate file reads with native tool calling format (tool_result blocks)", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial task" },
{ role: "assistant", content: [{ type: "tool_use", id: "toolu_001", name: "plan_mode_respond", input: {} }] },
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: "toolu_001",
content: [
{
type: "text",
text: "[plan_mode_respond] Result:\n<user_message>\n'test2.txt' (see below for file content)\n</user_message>\n\n<file_content path=\"/Users/toshi/Desktop/cline_testing_repo/test2.txt\">\ntest\n\n</file_content>",
},
],
},
],
},
{ role: "assistant", content: [{ type: "tool_use", id: "toolu_002", name: "write_to_file", input: {} }] },
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: "toolu_002",
content: [
{
type: "text",
text: "[write_to_file for '/Users/toshi/Desktop/cline_testing_repo/test2.txt'] Result:\nThe content was successfully saved.\n\n<final_file_content path=\"/Users/toshi/Desktop/cline_testing_repo/test2.txt\">\ntest\n\n</final_file_content>",
},
],
},
{ type: "text", text: "<environment_details>\n# Current Mode\nACT MODE\n</environment_details>" },
],
},
{ role: "assistant", content: [{ type: "tool_use", id: "toolu_003", name: "text", input: {} }] },
{
role: "user",
content: [
{
type: "text",
text: "[TASK RESUMPTION] This task was interrupted just now. The conversation may have been incomplete.",
},
{ type: "text", text: "New message to respond to with plan_mode_respond tool" },
],
},
{ role: "assistant", content: [{ type: "tool_use", id: "toolu_004", name: "replace_in_file", input: {} }] },
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: "toolu_004",
content: [
{
type: "text",
text: "[replace_in_file for '/Users/toshi/Desktop/cline_testing_repo/test2.txt'] Result:\nThe content was successfully saved.\n\n<final_file_content path=\"/Users/toshi/Desktop/cline_testing_repo/test2.txt\">\ntest2\n\n</final_file_content>",
},
],
},
{ type: "text", text: "<environment_details>\n# Current Mode\nACT MODE\n</environment_details>" },
],
},
]
const timestamp = Date.now()
const [didUpdate, indices] = contextManager.applyContextOptimizations(messages, 2, timestamp)
expect(didUpdate).to.equal(true)
expect(indices.size).to.equal(2)
expect(indices.has(2)).to.equal(true)
expect(indices.has(4)).to.equal(true)
expect(indices.has(8)).to.equal(false)
})
})
describe("getTruncatedMessages", () => {
let contextManager: ContextManager
@@ -381,4 +197,371 @@ describe("ContextManager", () => {
expect((content[0] as Anthropic.Messages.TextBlockParam).text).to.equal("Additional user text")
})
})
describe("applyFileReadContextHistoryUpdates", () => {
let contextManager: ContextManager
beforeEach(() => {
contextManager = new ContextManager()
})
it("should return early when fileReadIndices is empty", () => {
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
const messageFilePaths = new Map<number, string[]>()
const apiMessages: Anthropic.Messages.MessageParam[] = []
const timestamp = Date.now()
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
fileReadIndices,
messageFilePaths,
apiMessages,
timestamp,
)
expect(didUpdate).to.be.false
expect(updatedIndices.size).to.equal(0)
})
it("should not update when file has only one occurrence", () => {
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
fileReadIndices.set("test.ts", [[3, 2, "", "replacement text", 0]])
const messageFilePaths = new Map<number, string[]>()
const apiMessages: Anthropic.Messages.MessageParam[] = []
const timestamp = Date.now()
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
fileReadIndices,
messageFilePaths,
apiMessages,
timestamp,
)
expect(didUpdate).to.be.false
expect(updatedIndices.size).to.equal(0)
})
it("should update all but the last occurrence of duplicate file reads", () => {
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
// messageIndex, messageType (READ_FILE_TOOL=2), searchText, replaceText, innerIndex
fileReadIndices.set("test.ts", [
[3, 2, "", "[read_file for 'test.ts'] Result:\nDuplicate file read...", 0],
[5, 2, "", "[read_file for 'test.ts'] Result:\nDuplicate file read...", 0],
[7, 2, "", "[read_file for 'test.ts'] Result:\nKeep this one", 0],
])
const messageFilePaths = new Map<number, string[]>()
const apiMessages: Anthropic.Messages.MessageParam[] = []
const timestamp = Date.now()
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
fileReadIndices,
messageFilePaths,
apiMessages,
timestamp,
)
expect(didUpdate).to.be.true
expect(updatedIndices.size).to.equal(2)
expect(updatedIndices.has(3)).to.be.true
expect(updatedIndices.has(5)).to.be.true
expect(updatedIndices.has(7)).to.be.false // Last occurrence should not be updated
})
it("should handle FILE_MENTION type correctly with multiple files in same text", () => {
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
// FILE_MENTION = 4
fileReadIndices.set("file1.ts", [
[
3,
4,
'<file_content path="file1.ts">content1</file_content>',
'<file_content path="file1.ts">Duplicate file read...</file_content>',
0,
],
[
5,
4,
'<file_content path="file1.ts">content2</file_content>',
'<file_content path="file1.ts">Keep this</file_content>',
0,
],
])
fileReadIndices.set("file2.ts", [
[
3,
4,
'<file_content path="file2.ts">content3</file_content>',
'<file_content path="file2.ts">Duplicate file read...</file_content>',
0,
],
[
6,
4,
'<file_content path="file2.ts">content4</file_content>',
'<file_content path="file2.ts">Keep this</file_content>',
0,
],
])
const messageFilePaths = new Map<number, string[]>()
messageFilePaths.set(3, ["file1.ts", "file2.ts"])
const apiMessages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial" },
{ role: "assistant", content: "Response" },
{ role: "user", content: "Message" },
{
role: "user",
content: [
{
type: "text",
text: '<file_content path="file1.ts">content1</file_content>\n<file_content path="file2.ts">content3</file_content>',
},
],
},
]
const timestamp = Date.now()
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
fileReadIndices,
messageFilePaths,
apiMessages,
timestamp,
)
expect(didUpdate).to.be.true
expect(updatedIndices.size).to.equal(1)
expect(updatedIndices.has(3)).to.be.true
})
it("should handle ALTER_FILE_TOOL type correctly", () => {
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
// ALTER_FILE_TOOL = 3
fileReadIndices.set("test.ts", [
[3, 3, "", "replacement text 1", 0],
[5, 3, "", "replacement text 2", 0],
])
const messageFilePaths = new Map<number, string[]>()
const apiMessages: Anthropic.Messages.MessageParam[] = []
const timestamp = Date.now()
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
fileReadIndices,
messageFilePaths,
apiMessages,
timestamp,
)
expect(didUpdate).to.be.true
expect(updatedIndices.size).to.equal(1)
expect(updatedIndices.has(3)).to.be.true
expect(updatedIndices.has(5)).to.be.false
})
it("should handle native tool calling format (tool_result blocks)", () => {
const fileReadIndices = new Map<string, [number, number, string, string, number][]>()
fileReadIndices.set("test.ts", [
[3, 2, "", "[read_file for 'test.ts'] Result:\nDuplicate...", 0],
[5, 2, "", "[read_file for 'test.ts'] Result:\nKeep this", 0],
])
const messageFilePaths = new Map<number, string[]>()
const apiMessages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial" },
{ role: "assistant", content: "Response" },
{ role: "user", content: "Message" },
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: "tool_123",
content: [{ type: "text", text: "[read_file for 'test.ts'] Result:\noriginal content" }],
},
],
},
]
const timestamp = Date.now()
const [didUpdate, updatedIndices] = (contextManager as any).applyFileReadContextHistoryUpdates(
fileReadIndices,
messageFilePaths,
apiMessages,
timestamp,
)
expect(didUpdate).to.be.true
expect(updatedIndices.size).to.equal(1)
expect(updatedIndices.has(3)).to.be.true
})
})
describe("helper methods for applyFileReadContextHistoryUpdates", () => {
let contextManager: ContextManager
beforeEach(() => {
contextManager = new ContextManager()
})
it("getBaseTextForFileMention should get text from existing updates", () => {
const messageIndex = 3
const innerIndex = 0
const apiMessages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial" },
{ role: "assistant", content: "Response" },
{ role: "user", content: "Message" },
{ role: "user", content: [{ type: "text", text: "original text" }] },
]
// Manually set up context history updates
const timestamp = Date.now()
const innerMap = new Map<number, any[]>()
innerMap.set(innerIndex, [[timestamp, "text", ["updated text"], []]])
;(contextManager as any).contextHistoryUpdates.set(messageIndex, [4, innerMap])
const result = (contextManager as any).getBaseTextForFileMention(messageIndex, innerIndex, apiMessages)
expect(result).to.equal("updated text")
})
it("getBaseTextForFileMention should fallback to original message content", () => {
const messageIndex = 3
const innerIndex = 0
const apiMessages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial" },
{ role: "assistant", content: "Response" },
{ role: "user", content: "Message" },
{ role: "user", content: [{ type: "text", text: "original text" }] },
]
const result = (contextManager as any).getBaseTextForFileMention(messageIndex, innerIndex, apiMessages)
expect(result).to.equal("original text")
})
it("getBaseTextForFileMention should handle tool_result blocks", () => {
const messageIndex = 3
const innerIndex = 0
const apiMessages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Initial" },
{ role: "assistant", content: "Response" },
{ role: "user", content: "Message" },
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: "tool_123",
content: [{ type: "text", text: "tool result text" }],
},
],
},
]
const result = (contextManager as any).getBaseTextForFileMention(messageIndex, innerIndex, apiMessages)
expect(result).to.equal("tool result text")
})
it("getPreviouslyReplacedFiles should return empty array when no updates exist", () => {
const messageIndex = 3
const innerIndex = 0
const result = (contextManager as any).getPreviouslyReplacedFiles(messageIndex, innerIndex)
expect(result).to.deep.equal([])
})
it("getPreviouslyReplacedFiles should return previously replaced files", () => {
const messageIndex = 3
const innerIndex = 0
const timestamp = Date.now()
// Manually set up context history updates with metadata
const innerMap = new Map<number, any[]>()
innerMap.set(innerIndex, [
[
timestamp,
"text",
["updated text"],
[
["file1.ts", "file2.ts"],
["file1.ts", "file2.ts", "file3.ts"],
],
],
])
;(contextManager as any).contextHistoryUpdates.set(messageIndex, [4, innerMap])
const result = (contextManager as any).getPreviouslyReplacedFiles(messageIndex, innerIndex)
expect(result).to.deep.equal(["file1.ts", "file2.ts"])
})
it("addContextUpdate should create new entry when none exists", () => {
const messageIndex = 3
const messageType = 2 // READ_FILE_TOOL
const innerIndex = 0
const timestamp = Date.now()
const messageString = "replacement text"
;(contextManager as any).addContextUpdate(messageIndex, messageType, innerIndex, timestamp, messageString)
const contextHistory = (contextManager as any).contextHistoryUpdates
expect(contextHistory.has(messageIndex)).to.be.true
const [storedType, innerMap] = contextHistory.get(messageIndex)
expect(storedType).to.equal(messageType)
expect(innerMap.has(innerIndex)).to.be.true
const updates = innerMap.get(innerIndex)
expect(updates).to.have.lengthOf(1)
expect(updates[0]).to.deep.equal([timestamp, "text", [messageString], []])
})
it("addContextUpdate should append to existing updates", () => {
const messageIndex = 3
const messageType = 2
const innerIndex = 0
const timestamp1 = Date.now()
const timestamp2 = timestamp1 + 1000
;(contextManager as any).addContextUpdate(messageIndex, messageType, innerIndex, timestamp1, "first update")
;(contextManager as any).addContextUpdate(messageIndex, messageType, innerIndex, timestamp2, "second update")
const contextHistory = (contextManager as any).contextHistoryUpdates
const [, innerMap] = contextHistory.get(messageIndex)
const updates = innerMap.get(innerIndex)
expect(updates).to.have.lengthOf(2)
expect(updates[1]).to.deep.equal([timestamp2, "text", ["second update"], []])
})
it("getOrCreateInnerMap should return existing map", () => {
const messageIndex = 3
const messageType = 2
const innerMap = new Map<number, any[]>()
;(contextManager as any).contextHistoryUpdates.set(messageIndex, [messageType, innerMap])
const result = (contextManager as any).getOrCreateInnerMap(messageIndex, messageType)
expect(result).to.equal(innerMap)
})
it("getOrCreateInnerMap should create new map when none exists", () => {
const messageIndex = 3
const messageType = 2
const result = (contextManager as any).getOrCreateInnerMap(messageIndex, messageType)
expect(result).to.be.instanceOf(Map)
const contextHistory = (contextManager as any).contextHistoryUpdates
expect(contextHistory.has(messageIndex)).to.be.true
const [storedType, storedMap] = contextHistory.get(messageIndex)
expect(storedType).to.equal(messageType)
expect(storedMap).to.equal(result)
})
})
})
@@ -45,7 +45,6 @@ export async function getOrganizationCredits(
promptTokens: tx.promptTokens,
totalTokens: tx.totalTokens,
userId: tx.userId,
operation: tx.operation,
}),
) || [],
})
+41 -5
View File
@@ -81,6 +81,12 @@ export class Controller {
// Flag to prevent duplicate cancellations from spam clicking
private cancelInProgress = false
// Shell integration warning tracker
private shellIntegrationWarningTracker: {
timestamps: number[]
lastSuggestionShown?: number
} = { timestamps: [] }
// Timer for periodic remote config fetching
private remoteConfigTimer?: NodeJS.Timeout
@@ -509,6 +515,38 @@ export class Controller {
}
}
/**
* Check if we should show the background terminal suggestion based on shell integration warning frequency
* @returns true if we should show the suggestion, false otherwise
*/
shouldShowBackgroundTerminalSuggestion(): boolean {
const oneHourAgo = Date.now() - 60 * 60 * 1000
// Clean old timestamps (older than 1 hour)
this.shellIntegrationWarningTracker.timestamps = this.shellIntegrationWarningTracker.timestamps.filter(
(ts) => ts > oneHourAgo,
)
// Add current warning
this.shellIntegrationWarningTracker.timestamps.push(Date.now())
// Check if we've shown suggestion recently (within last hour)
if (
this.shellIntegrationWarningTracker.lastSuggestionShown &&
Date.now() - this.shellIntegrationWarningTracker.lastSuggestionShown < 60 * 60 * 1000
) {
return false
}
// Show suggestion if 3+ warnings in last hour
if (this.shellIntegrationWarningTracker.timestamps.length >= 3) {
this.shellIntegrationWarningTracker.lastSuggestionShown = Date.now()
return true
}
return false
}
async handleAuthCallback(customToken: string, provider: string | null = null) {
try {
await this.authService.handleAuthCallback(customToken, provider ? provider : "google")
@@ -951,18 +989,16 @@ export class Controller {
user: this.stateManager.getGlobalStateKey("multiRootEnabled"),
featureFlag: true, // Multi-root workspace is now always enabled
},
clineWebToolsEnabled: {
user: this.stateManager.getGlobalSettingsKey("clineWebToolsEnabled"),
featureFlag: featureFlagsService.getWebtoolsEnabled(),
hooksEnabled: {
user: this.stateManager.getGlobalStateKey("hooksEnabled"),
featureFlag: featureFlagsService.getHooksEnabled(),
},
hooksEnabled: this.stateManager.getGlobalSettingsKey("hooksEnabled"),
lastDismissedInfoBannerVersion,
lastDismissedModelBannerVersion,
remoteConfigSettings: this.stateManager.getRemoteConfigSettings(),
lastDismissedCliBannerVersion,
subagentsEnabled,
nativeToolCallSetting: this.stateManager.getGlobalStateKey("nativeToolCallEnabled"),
enableParallelToolCalling: this.stateManager.getGlobalSettingsKey("enableParallelToolCalling"),
}
}
@@ -8,6 +8,7 @@ import { StateManager } from "@/core/storage/StateManager"
import {
ANTHROPIC_MAX_THINKING_BUDGET,
CLAUDE_SONNET_1M_TIERS,
clineMicrowaveModelInfo,
openRouterClaudeSonnet41mModelId,
openRouterClaudeSonnet451mModelId,
} from "@/shared/api"
@@ -264,7 +265,23 @@ export async function refreshOpenRouterModels(controller: Controller): Promise<R
/**
* Stealth models are models that are compatible with the OpenRouter API but not listed on the OpenRouter website or API.
*/
const CLINE_STEALTH_MODELS: Record<string, ModelInfo> = {}
const CLINE_STEALTH_MODELS: Record<string, ModelInfo> = {
"stealth/microwave": {
maxTokens: clineMicrowaveModelInfo.maxTokens ?? 0,
contextWindow: clineMicrowaveModelInfo.contextWindow ?? 0,
supportsImages: clineMicrowaveModelInfo.supportsImages ?? false,
supportsPromptCache: clineMicrowaveModelInfo.supportsPromptCache ?? false,
inputPrice: clineMicrowaveModelInfo.inputPrice ?? 0,
outputPrice: clineMicrowaveModelInfo.outputPrice ?? 0,
cacheWritesPrice: clineMicrowaveModelInfo.cacheWritesPrice ?? 0,
cacheReadsPrice: clineMicrowaveModelInfo.cacheReadsPrice ?? 0,
description: clineMicrowaveModelInfo.description ?? "",
thinkingConfig: clineMicrowaveModelInfo.thinkingConfig ?? undefined,
supportsGlobalEndpoint: clineMicrowaveModelInfo.supportsGlobalEndpoint ?? undefined,
tiers: clineMicrowaveModelInfo.tiers,
},
// Add more stealth models here as needed
}
export function appendClineStealthModels(currentModels: Record<string, ModelInfo>): Record<string, ModelInfo> {
// Create a shallow clone of the current models to avoid mutating the original object
@@ -1,88 +0,0 @@
import { EmptyRequest } from "@shared/proto/cline/common"
import { SlashCommandInfo, SlashCommandsResponse } from "@shared/proto/cline/slash"
import { BASE_SLASH_COMMANDS } from "@/shared/slashCommands"
import { Controller } from ".."
/**
* Returns all available slash commands for autocomplete.
*/
export async function getAvailableSlashCommands(controller: Controller, _request: EmptyRequest): Promise<SlashCommandsResponse> {
const commands: SlashCommandInfo[] = []
// Add built-in commands
for (const cmd of [...BASE_SLASH_COMMANDS]) {
commands.push(
SlashCommandInfo.create({
name: cmd.name,
description: cmd.description,
section: "default",
cliCompatible: cmd.cliCompatible,
}),
)
}
// Get workflow toggles from state
const localWorkflowToggles = controller.stateManager.getWorkspaceStateKey("workflowToggles") ?? {}
const globalWorkflowToggles = controller.stateManager.getGlobalSettingsKey("globalWorkflowToggles") ?? {}
const remoteWorkflowToggles = controller.stateManager.getGlobalStateKey("remoteWorkflowToggles") ?? {}
const remoteConfigSettings = controller.stateManager.getRemoteConfigSettings()
const remoteWorkflows = remoteConfigSettings?.remoteGlobalWorkflows ?? []
// Track local workflow names to avoid duplicates from global
const localNames = new Set<string>()
// Add local workflows (enabled only)
for (const [path, enabled] of Object.entries(localWorkflowToggles)) {
if (enabled) {
const fileName = fullPathToFileName(path)
localNames.add(fileName)
commands.push(
SlashCommandInfo.create({
name: fileName,
description: `Custom workflow: ${fileName}`,
section: "custom",
cliCompatible: true,
}),
)
}
}
// Add global workflows (enabled only, skip if local exists with same name)
for (const [path, enabled] of Object.entries(globalWorkflowToggles)) {
if (enabled) {
const fileName = fullPathToFileName(path)
if (!localNames.has(fileName)) {
commands.push(
SlashCommandInfo.create({
name: fileName,
description: `Custom workflow: ${fileName}`,
section: "custom",
cliCompatible: true,
}),
)
}
}
}
// Add remote workflows that are enabled
for (const workflow of remoteWorkflows) {
const enabled = workflow.alwaysEnabled || remoteWorkflowToggles[workflow.name] !== false
if (enabled) {
commands.push(
SlashCommandInfo.create({
name: workflow.name,
description: `Remote workflow: ${workflow.name}`,
section: "custom",
cliCompatible: true,
}),
)
}
}
return SlashCommandsResponse.create({ commands })
}
function fullPathToFileName(path: string): string {
// e.g. replace /path/to/workflow.md with workflow.md
return path.replace(/^.*[/\\]/, "")
}
+7 -19
View File
@@ -12,6 +12,7 @@ import { OpenaiReasoningEffort } from "@shared/storage/types"
import { TelemetrySetting } from "@shared/TelemetrySetting"
import { ClineEnv } from "@/config"
import { HostProvider } from "@/hosts/host-provider"
import { TerminalInfo } from "@/integrations/terminal/TerminalRegistry"
import { McpDisplayMode } from "@/shared/McpDisplayMode"
import { ShowMessageType } from "@/shared/proto/host/window"
import { telemetryService } from "../../../services/telemetry"
@@ -185,14 +186,6 @@ export async function updateSettings(controller: Controller, request: UpdateSett
controller.stateManager.setGlobalState("yoloModeToggled", request.yoloModeToggled)
}
// Update cline web tools setting
if (request.clineWebToolsEnabled !== undefined) {
if (controller.task) {
telemetryService.captureClineWebToolsToggle(controller.task.ulid, request.clineWebToolsEnabled)
}
controller.stateManager.setGlobalState("clineWebToolsEnabled", request.clineWebToolsEnabled)
}
if (request.dictationSettings !== undefined) {
// Convert from protobuf format (snake_case) to TypeScript format (camelCase)
const dictationSettings = {
@@ -288,15 +281,14 @@ export async function updateSettings(controller: Controller, request: UpdateSett
controller.stateManager.setGlobalState("defaultTerminalProfile", profileId)
let closedCount = 0
let busyTerminalsCount = 0
let busyTerminals: TerminalInfo[] = []
// Update the terminal manager of the current task if it exists
if (controller.task) {
// Call the updated setDefaultTerminalProfile method that returns closed terminal info
// Use `as any` to handle type incompatibility between VSCode's TerminalInfo and standalone TerminalInfo
const result = controller.task.terminalManager.setDefaultTerminalProfile(profileId) as any
const result = controller.task.terminalManager.setDefaultTerminalProfile(profileId)
closedCount = result.closedCount
busyTerminalsCount = result.busyTerminals?.length ?? 0
busyTerminals = result.busyTerminals
// Show information message if terminals were closed
if (closedCount > 0) {
@@ -308,10 +300,10 @@ export async function updateSettings(controller: Controller, request: UpdateSett
}
// Show warning if there are busy terminals that couldn't be closed
if (busyTerminalsCount > 0) {
if (busyTerminals.length > 0) {
const message =
`${busyTerminalsCount} busy ${busyTerminalsCount === 1 ? "terminal has" : "terminals have"} a different profile. ` +
`Close ${busyTerminalsCount === 1 ? "it" : "them"} to use the new profile for all commands.`
`${busyTerminals.length} busy ${busyTerminals.length === 1 ? "terminal has" : "terminals have"} a different profile. ` +
`Close ${busyTerminals.length === 1 ? "it" : "them"} to use the new profile for all commands.`
HostProvider.window.showMessage({
type: ShowMessageType.WARNING,
message,
@@ -371,10 +363,6 @@ export async function updateSettings(controller: Controller, request: UpdateSett
}
}
if (request.enableParallelToolCalling !== undefined) {
controller.stateManager.setGlobalState("enableParallelToolCalling", !!request.enableParallelToolCalling)
}
// Post updated state to webview
await controller.postStateToWebview()
+7 -16
View File
@@ -11,6 +11,7 @@ import { Settings } from "@shared/storage/state-keys"
import { TelemetrySetting } from "@shared/TelemetrySetting"
import { ClineEnv } from "@/config"
import { HostProvider } from "@/hosts/host-provider"
import { TerminalInfo } from "@/integrations/terminal/TerminalRegistry"
import { ShowMessageType } from "@/shared/proto/host/window"
import { Mode, OpenaiReasoningEffort } from "@/shared/storage/types"
import { telemetryService } from "../../../services/telemetry"
@@ -64,7 +65,6 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS
telemetrySetting,
yoloModeToggled,
useAutoCondense,
clineWebToolsEnabled,
focusChainSettings,
browserSettings,
defaultTerminalProfile,
@@ -159,14 +159,6 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS
controller.stateManager.setGlobalState("useAutoCondense", useAutoCondense)
}
// Update Cline web tools setting (requires telemetry)
if (clineWebToolsEnabled !== undefined) {
if (controller.task) {
telemetryService.captureClineWebToolsToggle(controller.task.ulid, clineWebToolsEnabled)
}
controller.stateManager.setGlobalState("clineWebToolsEnabled", clineWebToolsEnabled)
}
// Update focus chain settings (requires telemetry on state change)
if (focusChainSettings !== undefined) {
const currentSettings = controller.stateManager.getGlobalSettingsKey("focusChainSettings")
@@ -223,7 +215,7 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS
controller.stateManager.setGlobalState("defaultTerminalProfile", profileId)
let closedCount = 0
let busyTerminalsCount = 0
let busyTerminals: TerminalInfo[] = []
// Update the terminal manager of the current task if it exists
if (controller.task) {
@@ -233,10 +225,9 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS
}
// Call the updated setDefaultTerminalProfile method that returns closed terminal info
// Use `as any` to handle type incompatibility between VSCode's TerminalInfo and standalone TerminalInfo
const result = controller.task.terminalManager.setDefaultTerminalProfile(profileId) as any
const result = controller.task.terminalManager.setDefaultTerminalProfile(profileId)
closedCount = result.closedCount
busyTerminalsCount = result.busyTerminals?.length ?? 0
busyTerminals = result.busyTerminals
// Show information message if terminals were closed
if (closedCount > 0) {
@@ -248,10 +239,10 @@ export async function updateSettingsCli(controller: Controller, request: UpdateS
}
// Show warning if there are busy terminals that couldn't be closed
if (busyTerminalsCount > 0) {
if (busyTerminals.length > 0) {
const message =
`${busyTerminalsCount} busy ${busyTerminalsCount === 1 ? "terminal has" : "terminals have"} a different profile. ` +
`Close ${busyTerminalsCount === 1 ? "it" : "them"} to use the new profile for all commands.`
`${busyTerminals.length} busy ${busyTerminals.length === 1 ? "terminal has" : "terminals have"} a different profile. ` +
`Close ${busyTerminals.length === 1 ? "it" : "them"} to use the new profile for all commands.`
HostProvider.window.showMessage({
type: ShowMessageType.WARNING,
message,
-12
View File
@@ -80,18 +80,6 @@ export async function newTask(controller: Controller, request: NewTaskRequest):
...(request.taskSettings?.actModeApiProvider !== undefined && {
actModeApiProvider: convertProtoToApiProvider(request.taskSettings.actModeApiProvider),
}),
...(request.taskSettings?.hooksEnabled !== undefined && {
hooksEnabled: (() => {
const isEnabled = !!request.taskSettings.hooksEnabled
// Platform validation: Only allow enabling hooks on macOS and Linux
if (isEnabled && process.platform === "win32") {
throw new Error("Hooks are not yet supported on Windows")
}
return isEnabled
})(),
}),
}).filter(([_, value]) => value !== undefined),
)
+40 -76
View File
@@ -1,4 +1,3 @@
import { telemetryService } from "../../services/telemetry"
import { getAllHooksDirs } from "../storage/disk"
import { HookFactory, Hooks } from "./hook-factory"
@@ -57,8 +56,8 @@ export class HookDiscoveryCache {
// Directories we've tried to watch (even if watcher creation failed)
private watchedDirs = new Set<string>()
// Currently scanning promises (to prevent concurrent scans)
private scanningPromises = new Map<HookName, Promise<string[]>>()
// Currently scanning (to prevent concurrent scans)
private scanning = new Set<HookName>()
// For disposal
private context: ExtensionContext | null = null
@@ -106,95 +105,60 @@ export class HookDiscoveryCache {
this.log(`Getting hooks for ${hookName}`)
const cached = this.cache.get(hookName)
const cacheHit = cached !== undefined
let scripts: string[]
let initiatedScan = false // Track if this caller initiated the scan
if (cacheHit) {
if (cached) {
this.log(`Cache hit for ${hookName}: ${cached.scriptPaths.length} scripts`)
scripts = cached.scriptPaths
} else {
this.log(`Cache miss for ${hookName}, scanning...`)
// Check if scan is already in progress
const existingPromise = this.scanningPromises.get(hookName)
if (existingPromise) {
// Another caller is already scanning, reuse their promise
this.log(`Reusing existing scan for ${hookName}`)
scripts = await existingPromise
} else {
// This caller initiates the scan
initiatedScan = true
scripts = await this.scan(hookName)
}
return cached.scriptPaths
}
// Only report telemetry if:
// 1. It was a cache hit, OR
// 2. This caller initiated the scan (not reusing another caller's promise)
if (cacheHit || initiatedScan) {
telemetryService.safeCapture(
() => telemetryService.captureHookCacheAccess(hookName, cacheHit),
"HookDiscoveryCache.get",
)
}
return scripts
this.log(`Cache miss for ${hookName}, scanning...`)
return this.scan(hookName)
}
/**
* Scan for hook scripts and cache the result
*/
private async scan(hookName: HookName): Promise<string[]> {
// Check if a scan is already in progress for this hook
const existingPromise = this.scanningPromises.get(hookName)
if (existingPromise) {
this.log(`Already scanning ${hookName}, waiting for existing scan...`)
return existingPromise
// Prevent concurrent scans of the same hook
if (this.scanning.has(hookName)) {
this.log(`Already scanning ${hookName}, waiting...`)
await new Promise((resolve) => setTimeout(resolve, 50))
return this.get(hookName)
}
// Create a new scan promise
const scanPromise = (async () => {
try {
// Get all current hooks directories
const hooksDirs = await getAllHooksDirs()
this.log(`Scanning ${hooksDirs.length} directories for ${hookName}`)
this.scanning.add(hookName)
// Ensure watchers are set up for each directory (lazy initialization)
for (const dir of hooksDirs) {
this.ensureWatcher(dir)
}
try {
// Get all current hooks directories
const hooksDirs = await getAllHooksDirs()
this.log(`Scanning ${hooksDirs.length} directories for ${hookName}`)
// Scan each directory for this hook
const scriptPromises = hooksDirs.map((dir) => HookFactory.findHookInHooksDir(hookName, dir))
const results = await Promise.all(scriptPromises)
const scripts = results.filter((path): path is string => path !== undefined)
this.log(`Found ${scripts.length} scripts for ${hookName}`)
// Cache the result
this.cache.set(hookName, {
scriptPaths: scripts,
timestamp: Date.now(),
})
return scripts
} catch (error) {
console.error(`Error scanning for ${hookName} hooks:`, error)
// Return empty array on error - don't break the whole system
return []
} finally {
// Remove from scanning promises map
this.scanningPromises.delete(hookName)
// Ensure watchers are set up for each directory (lazy initialization)
for (const dir of hooksDirs) {
this.ensureWatcher(dir)
}
})()
// Store the promise so concurrent calls can await it
this.scanningPromises.set(hookName, scanPromise)
// Scan each directory for this hook
const scriptPromises = hooksDirs.map((dir) => HookFactory.findHookInHooksDir(hookName, dir))
return scanPromise
const results = await Promise.all(scriptPromises)
const scripts = results.filter((path): path is string => path !== undefined)
this.log(`Found ${scripts.length} scripts for ${hookName}`)
// Cache the result
this.cache.set(hookName, {
scriptPaths: scripts,
timestamp: Date.now(),
})
return scripts
} catch (error) {
console.error(`Error scanning for ${hookName} hooks:`, error)
// Return empty array on error - don't break the whole system
return []
} finally {
this.scanning.delete(hookName)
}
}
/**
-2
View File
@@ -105,8 +105,6 @@ export async function executeHook<Name extends keyof Hooks>(options: HookExecuti
hookName,
streamCallback,
isCancellable ? abortController.signal : undefined,
taskId,
options.toolName,
)
const result = await hook.run({
+3 -226
View File
@@ -2,7 +2,6 @@ import fs from "fs/promises"
import path from "path"
import { version as clineVersion } from "../../../package.json"
import { getDistinctId } from "../../services/logging/distinctId"
import { telemetryService } from "../../services/telemetry"
import {
HookInput,
HookOutput,
@@ -26,9 +25,6 @@ const HOOK_EXECUTION_TIMEOUT_MS = 30000
// Maximum size for context modification (to prevent prompt overflow)
const MAX_CONTEXT_MODIFICATION_SIZE = 50000 // ~50KB
// Exit code indicating cancellation/interruption (Unix SIGINT convention: 128 + signal 2)
const EXIT_CODE_SIGINT = 130
/**
* Validates hook output JSON structure.
* Ensures required fields are present and have correct types.
@@ -237,7 +233,6 @@ export type HookStreamCallback = (line: string, stream: "stdout" | "stderr") =>
* - Parses JSON output from stdout, attempting to extract it even if mixed with debug output
* - Truncates context modifications that exceed 50KB to prevent prompt overflow
* - Handles both successful and failed executions gracefully
* - Emits per-hook telemetry with source attribution (global or workspace)
*
* Error handling:
* - Treats hooks as "fail-open": only shouldContinue:false blocks tool execution
@@ -250,31 +245,13 @@ class StdioHookRunner<Name extends HookName> extends HookRunner<Name> {
constructor(
hookName: Name,
public readonly scriptPath: string,
private readonly source: "global" | "workspace",
private readonly streamCallback?: HookStreamCallback,
private readonly abortSignal?: AbortSignal,
private readonly taskId?: string,
private readonly toolName?: string,
) {
super(hookName)
}
override async [exec](input: HookInput): Promise<HookOutput> {
const startTime = performance.now()
const taskId = this.taskId // Local const for type narrowing in closures
// Capture telemetry at the start of individual hook execution
if (taskId) {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "started", {
source: this.source,
toolName: this.toolName,
}),
"HookFactory.exec.started",
)
}
// Check if already aborted before starting
if (this.abortSignal?.aborted) {
throw HookExecutionError.cancellation(this.scriptPath)
@@ -421,8 +398,6 @@ class StdioHookRunner<Name extends HookName> extends HookRunner<Name> {
// If we have valid JSON, honor it regardless of exit code
if (parsedOutput) {
const durationMs = performance.now() - startTime
// Log warning if non-zero exit but valid JSON (for developers)
if (exitCode !== 0) {
console.warn(`[Hook ${this.hookName}] Exited with code ${exitCode} but provided valid JSON response`)
@@ -431,39 +406,6 @@ class StdioHookRunner<Name extends HookName> extends HookRunner<Name> {
}
}
// Capture success/cancellation telemetry
if (taskId) {
if (parsedOutput.cancel) {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "completed", {
source: this.source,
toolName: this.toolName,
durationMs,
exitCode: exitCode ?? EXIT_CODE_SIGINT,
cancelRequested: true,
contextModified: !!parsedOutput.contextModification,
contextSize: parsedOutput.contextModification?.length,
}),
"HookFactory.exec.completed.cancel",
)
} else {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "completed", {
source: this.source,
toolName: this.toolName,
durationMs,
exitCode: exitCode ?? 0,
cancelRequested: false,
contextModified: !!parsedOutput.contextModification,
contextSize: parsedOutput.contextModification?.length,
}),
"HookFactory.exec.completed.success",
)
}
}
return parsedOutput
}
@@ -471,24 +413,6 @@ class StdioHookRunner<Name extends HookName> extends HookRunner<Name> {
if (exitCode === 0) {
// Hook succeeded but didn't provide JSON - allow execution (no cancellation)
console.warn(`[Hook ${this.hookName}] Completed successfully but no JSON response found`)
const durationMs = performance.now() - startTime
// Capture success telemetry even without JSON
if (taskId) {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "completed", {
source: this.source,
toolName: this.toolName,
durationMs,
exitCode: 0,
cancelRequested: false,
contextModified: false,
}),
"HookFactory.exec.completed.noJson",
)
}
return HookOutput.create({
cancel: false,
})
@@ -497,48 +421,8 @@ class StdioHookRunner<Name extends HookName> extends HookRunner<Name> {
throw HookExecutionError.execution(this.scriptPath, exitCode ?? 1, stderr, this.hookName)
}
} catch (error) {
const durationMs = performance.now() - startTime
// If it's already a HookExecutionError, re-throw it
if (HookExecutionError.isHookError(error)) {
// Capture failure telemetry based on error type
if (taskId) {
if (error.errorInfo.type === "cancellation") {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "cancelled", {
source: this.source,
toolName: this.toolName,
}),
"HookFactory.exec.error.cancellation",
)
} else if (error.errorInfo.type === "timeout") {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "failed", {
source: this.source,
toolName: this.toolName,
durationMs,
errorType: "timeout",
errorMessage: error.message,
}),
"HookFactory.exec.error.timeout",
)
} else {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "failed", {
source: this.source,
toolName: this.toolName,
durationMs,
exitCode: error.errorInfo.exitCode ?? 1,
errorType: error.errorInfo.type as "execution" | "timeout" | "validation",
errorMessage: error.message,
}),
"HookFactory.exec.error.failed",
)
}
}
throw error
}
@@ -548,52 +432,15 @@ class StdioHookRunner<Name extends HookName> extends HookRunner<Name> {
// Check for timeout
if (error instanceof Error && error.message.includes("timed out")) {
if (taskId) {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "failed", {
source: this.source,
toolName: this.toolName,
durationMs,
errorType: "timeout",
errorMessage: error.message,
}),
"HookFactory.exec.catch.timeout",
)
}
throw HookExecutionError.timeout(this.scriptPath, HOOK_EXECUTION_TIMEOUT_MS, stderr, this.hookName)
}
// Check for cancellation
if (error instanceof Error && error.message.includes("cancelled")) {
if (taskId) {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "cancelled", {
source: this.source,
toolName: this.toolName,
}),
"HookFactory.exec.catch.cancelled",
)
}
throw HookExecutionError.cancellation(this.scriptPath, this.hookName)
}
// Generic execution error - include hook name
if (taskId) {
telemetryService.safeCapture(
() =>
telemetryService.captureHookExecution(taskId, this.hookName, "failed", {
source: this.source,
toolName: this.toolName,
durationMs,
exitCode: exitCode ?? 1,
errorType: "execution",
errorMessage: error instanceof Error ? error.message : String(error),
}),
"HookFactory.exec.catch.execution",
)
}
throw HookExecutionError.execution(this.scriptPath, exitCode ?? 1, stderr, this.hookName)
}
}
@@ -699,8 +546,8 @@ export class HookFactory {
/**
* Create a hook runner without streaming support (backwards compatible)
*/
async create<Name extends HookName>(hookName: Name, taskId?: string, toolName?: string): Promise<HookRunner<Name>> {
return this.createWithStreaming(hookName, undefined, undefined, taskId, toolName)
async create<Name extends HookName>(hookName: Name): Promise<HookRunner<Name>> {
return this.createWithStreaming(hookName)
}
/**
@@ -719,94 +566,24 @@ export class HookFactory {
* @param hookName The type of hook to create (e.g., "PreToolUse", "PostToolUse")
* @param streamCallback Optional callback for real-time output streaming
* @param abortSignal Optional signal to cancel hook execution
* @param taskId Optional task ID for telemetry context
* @param toolName Optional tool name for telemetry context
* @returns A HookRunner that executes the hook(s), or NoOpRunner if none found
*/
async createWithStreaming<Name extends HookName>(
hookName: Name,
streamCallback?: HookStreamCallback,
abortSignal?: AbortSignal,
taskId?: string,
toolName?: string,
): Promise<HookRunner<Name>> {
// Use cache for hook discovery instead of direct file system scan
const { HookDiscoveryCache } = await import("./HookDiscoveryCache")
const scripts = await HookDiscoveryCache.getInstance().get(hookName)
// Fetch hooks dirs once for source determination and telemetry
const hooksDirs = await getAllHooksDirs()
// Capture hook discovery telemetry
// Categorize scripts by location (global vs workspace)
const { globalCount, workspaceCount } = this.categorizeHookScripts(scripts, hooksDirs)
if (scripts.length > 0) {
telemetryService.safeCapture(
() => telemetryService.captureHookDiscovery(hookName, globalCount, workspaceCount),
"HookFactory.createWithStreaming.discovery",
)
}
// Create runners with source determination for each script
const runners = scripts.map((script) => {
const source = this.determineScriptSource(script, hooksDirs)
return new StdioHookRunner(hookName, script, source, streamCallback, abortSignal, taskId, toolName)
})
const runners = scripts.map((script) => new StdioHookRunner(hookName, script, streamCallback, abortSignal))
if (runners.length === 0) {
return new NoOpRunner(hookName)
}
return runners.length === 1 ? runners[0] : new CombinedHookRunner(hookName, runners)
}
/**
* Checks if a hooks directory is a global hooks directory.
* Global hooks are located in paths containing "Cline/Hooks" or "cline/hooks".
*/
private static isGlobalHooksDir(dir: string): boolean {
return /[/\\][Cc]line[/\\][Hh]ooks/i.test(dir)
}
/**
* Determines if a single script is from global or workspace location
*/
private determineScriptSource(scriptPath: string, hooksDirs: string[]): "global" | "workspace" {
const containingDir = hooksDirs.find((dir) => scriptPath.startsWith(dir))
if (containingDir && HookFactory.isGlobalHooksDir(containingDir)) {
return "global"
}
return "workspace" // Default to workspace if uncertain
}
/**
* Categorizes hook scripts by their location (global vs workspace).
* Global hooks are located in ~/Documents/Cline/Hooks/
* Workspace hooks are located in workspace .clinerules/hooks/ directories
*
* @param scripts Array of hook script paths
* @param hooksDirs Array of hooks directories (passed to avoid redundant fetches)
* @returns Object with globalCount and workspaceCount
*/
private categorizeHookScripts(scripts: string[], hooksDirs: string[]): { globalCount: number; workspaceCount: number } {
if (scripts.length === 0) {
return { globalCount: 0, workspaceCount: 0 }
}
let globalCount = 0
let workspaceCount = 0
for (const script of scripts) {
const containingDir = hooksDirs.find((dir) => script.startsWith(dir))
if (containingDir && HookFactory.isGlobalHooksDir(containingDir)) {
globalCount++
} else {
workspaceCount++
}
}
return { globalCount, workspaceCount }
}
/**
* @returns A list of paths to scripts for the given hook name.
* Includes both global hooks (from ~/Documents/Cline/Hooks/) and workspace hooks
-293
View File
@@ -1,293 +0,0 @@
import { findLastIndex } from "@shared/array"
import type { ClineMessage } from "@shared/ExtensionMessage"
import type { ClineStorageMessage } from "@shared/messages/content"
import type { ContextManager } from "../context/context-management/ContextManager"
import type { MessageStateHandler } from "../task/message-state"
/**
* Active hook execution state
* Represents a hook process that is currently running
*/
export type HookExecution = {
hookName: string
toolName?: string
messageTs: number
abortController: AbortController
}
/**
* Custom error class for hook cancellation
* Used to signal that a hook cancelled an operation
*/
export class HookCancellationError extends Error {
public readonly wasCancelled: boolean
constructor(wasCancelled: boolean) {
super("Hook cancelled the operation")
this.name = "HookCancellationError"
this.wasCancelled = wasCancelled
}
}
/**
* Token usage information extracted from an API request message
*/
export interface TokenUsage {
tokensIn: number
tokensOut: number
tokensInCache: number
tokensOutCache: number
}
/**
* Extract token usage from an API request message
* @param message The API request message to parse
* @returns Token usage information, or zeros if parsing fails
*/
export function extractTokenUsageFromMessage(message: ClineMessage | undefined): TokenUsage {
const defaultUsage: TokenUsage = {
tokensIn: 0,
tokensOut: 0,
tokensInCache: 0,
tokensOutCache: 0,
}
if (!message?.text) {
return defaultUsage
}
try {
const apiReqInfo = JSON.parse(message.text)
return {
tokensIn: apiReqInfo.tokensIn || 0,
tokensOut: apiReqInfo.tokensOut || 0,
tokensInCache: apiReqInfo.cacheWrites || 0,
tokensOutCache: apiReqInfo.cacheReads || 0,
}
} catch (error) {
console.error("[PreCompact] Failed to parse API request token usage:", error)
return defaultUsage
}
}
/**
* Context files written for hook access
*/
export interface PreCompactContextFiles {
contextJsonPath: string
contextRawPath: string
hookTimestamp: number
}
/**
* Write context files for PreCompact hook access
* @param taskId Task identifier
* @param currentContext Current conversation context
* @returns Paths to written files and timestamp
*/
export async function writePreCompactContextFiles(
taskId: string,
currentContext: ClineStorageMessage[],
): Promise<PreCompactContextFiles> {
const { writeConversationHistoryJson, writeConversationHistoryText } = await import("../storage/disk")
// Generate single timestamp for both files to ensure they match
const hookTimestamp = Date.now()
// Write context files for hook access
const contextJsonPath = await writeConversationHistoryJson(taskId, currentContext, hookTimestamp)
const contextRawPath = await writeConversationHistoryText(taskId, currentContext, hookTimestamp)
return { contextJsonPath, contextRawPath, hookTimestamp }
}
/**
* Task state interface for cancellation handling
*/
export interface TaskStateForCancellation {
didFinishAbortingStream: boolean
}
/**
* Parameters for executing the PreCompact hook
* Organized into logical groups for better clarity
*/
export interface PreCompactHookParams {
// Task identification
/** Task identifier */
taskId: string
/** ULID for telemetry */
ulid: string
// Conversation state
/** API conversation history */
apiConversationHistory: ClineStorageMessage[]
/** Current deleted range (if any) */
conversationHistoryDeletedRange?: [number, number]
/** Cline messages for extracting token usage */
clineMessages: ClineMessage[]
// Services
/** Context manager for getting truncated messages */
contextManager: ContextManager
/** Message state handler for accessing conversation data */
messageStateHandler: MessageStateHandler
// Compaction metadata
/** Compaction strategy to report in hook data */
compactionStrategy: string
/** Optional: Pre-calculated deleted range to report */
deletedRange?: [number, number]
// UI callbacks
/** Callback to display messages */
say: (type: any, text?: string, images?: string[], files?: string[], partial?: boolean) => Promise<number | undefined>
/** Callback to save state and post to webview */
postStateToWebview: () => Promise<void>
// Hook management callbacks
/** Callback to set active hook execution */
setActiveHookExecution: (hookExecution: HookExecution | undefined) => Promise<void>
/** Callback to clear active hook execution */
clearActiveHookExecution: () => Promise<void>
// Cancellation dependencies
/** Task state object for setting abort flag */
taskState: TaskStateForCancellation
/** Callback to cancel the task */
cancelTask: () => Promise<void>
// Configuration
/** Whether hooks are enabled */
hooksEnabled: boolean
}
/**
* Result from executing the PreCompact hook
*/
export interface PreCompactHookResult {
/** Context modification provided by the hook */
contextModification?: string
}
/**
* Executes the PreCompact hook with proper cleanup and error handling.
* This shared function eliminates duplication between Task.executePreCompactHook
* and SummarizeTaskHandler.execute.
*
* @param params - Configuration for executing the hook
* @returns Result containing any context modification provided by the hook
* @throws HookCancellationError if the hook cancels the operation
* @throws Re-throws other errors after cleanup (caller should handle gracefully)
*/
export async function executePreCompactHookWithCleanup(params: PreCompactHookParams): Promise<PreCompactHookResult> {
const { executeHook } = await import("./hook-executor")
const { cleanupConversationHistoryFile } = await import("../storage/disk")
let contextJsonPath: string | undefined
let contextRawPath: string | undefined
try {
// Get current active context (respects previous compactions)
const currentContext = params.contextManager.getTruncatedMessages(
params.apiConversationHistory,
params.conversationHistoryDeletedRange,
)
// Write context files for hook access
const contextFiles = await writePreCompactContextFiles(params.taskId, currentContext)
contextJsonPath = contextFiles.contextJsonPath
contextRawPath = contextFiles.contextRawPath
// Extract token usage from the most recent API request
const previousApiReqIndex = findLastIndex(params.clineMessages, (m) => m.say === "api_req_started")
const previousRequest = previousApiReqIndex !== -1 ? params.clineMessages[previousApiReqIndex] : undefined
const { tokensIn, tokensOut, tokensInCache, tokensOutCache } = extractTokenUsageFromMessage(previousRequest)
// Extract truncation range - use provided range or extract from conversationHistoryDeletedRange
let deletedRangeStart = 0
let deletedRangeEnd = 0
if (params.deletedRange) {
;[deletedRangeStart, deletedRangeEnd] = params.deletedRange
} else if (params.conversationHistoryDeletedRange) {
;[deletedRangeStart, deletedRangeEnd] = params.conversationHistoryDeletedRange
}
// Execute the hook
const preCompactResult = await executeHook({
hookName: "PreCompact",
hookInput: {
preCompact: {
taskId: params.taskId,
ulid: params.ulid,
contextSize: currentContext.length,
compactionStrategy: params.compactionStrategy,
previousApiReqIndex: previousApiReqIndex,
tokensIn,
tokensOut,
tokensInCache,
tokensOutCache,
deletedRangeStart,
deletedRangeEnd,
contextJsonPath: contextJsonPath,
contextRawPath: contextRawPath,
},
},
isCancellable: true,
say: params.say,
setActiveHookExecution: params.setActiveHookExecution,
clearActiveHookExecution: params.clearActiveHookExecution,
messageStateHandler: params.messageStateHandler,
taskId: params.taskId,
hooksEnabled: params.hooksEnabled,
})
// Handle cancellation from hook
if (preCompactResult.cancel === true) {
// Log cancellation for debugging
const cancellationSource = preCompactResult.wasCancelled ? "user" : "PreCompact hook"
console.log(`[PreCompact] Context compaction cancelled by ${cancellationSource} for task ${params.taskId}`)
// Internalized cancellation state management (replaces handleCancellation callback)
// Always save state before cancelling, regardless of cancellation source
params.taskState.didFinishAbortingStream = true
await params.messageStateHandler.saveClineMessagesAndUpdateHistory()
await params.messageStateHandler.overwriteApiConversationHistory(
params.messageStateHandler.getApiConversationHistory(),
)
await params.postStateToWebview()
// Trigger full cancellation flow
await params.cancelTask()
// Throw error to signal cancellation to caller
throw new HookCancellationError(preCompactResult.wasCancelled)
}
// Hook completed successfully - log if context modification provided
if (preCompactResult.contextModification) {
console.log(`[PreCompact] Hook provided context modification for task ${params.taskId}`)
}
return {
contextModification: preCompactResult.contextModification,
}
} catch (error) {
// Re-throw error for caller to handle
throw error
} finally {
// Clean up temporary files - always executed regardless of success or error
// Wrap in try-catch to prevent cleanup failures from masking original errors
try {
if (contextJsonPath) {
await cleanupConversationHistoryFile(contextJsonPath)
}
if (contextRawPath) {
await cleanupConversationHistoryFile(contextRawPath)
}
} catch (cleanupError) {
console.error("[PreCompact] Failed to cleanup context files:", cleanupError)
// Don't throw - cleanup failure shouldn't mask original error
}
}
}
+1 -1
View File
@@ -1,5 +1,6 @@
import { FileContextTracker } from "@core/context/context-tracking/FileContextTracker"
import * as extractTextModule from "@integrations/misc/extract-text"
import * as terminalModule from "@integrations/terminal/get-latest-output"
import { UrlContentFetcher } from "@services/browser/UrlContentFetcher"
import * as gitModule from "@utils/git"
import { expect } from "chai"
@@ -8,7 +9,6 @@ import * as isBinaryFileModule from "isbinaryfile"
import * as path from "path"
import * as sinon from "sinon"
import { HostProvider } from "@/hosts/host-provider"
import * as terminalModule from "@/hosts/vscode/terminal/get-latest-output"
import { setVscodeHostProviderMock } from "@/test/host-provider-test-utils"
import { parseMentions } from "."
+1 -1
View File
@@ -1,6 +1,7 @@
import { diagnosticsToProblemsString } from "@integrations/diagnostics"
import { extractTextFromFile } from "@integrations/misc/extract-text"
import { openFile } from "@integrations/misc/open-file"
import { getLatestTerminalOutput } from "@integrations/terminal/get-latest-output"
import { UrlContentFetcher } from "@services/browser/UrlContentFetcher"
import { telemetryService } from "@services/telemetry"
import { mentionRegexGlobal } from "@shared/context-mentions"
@@ -11,7 +12,6 @@ import fs from "fs/promises"
import { isBinaryFile } from "isbinaryfile"
import * as path from "path"
import { HostProvider } from "@/hosts/host-provider"
import { getLatestTerminalOutput } from "@/hosts/vscode/terminal/get-latest-output"
import { ShowMessageType } from "@/shared/proto/host/window"
import { DiagnosticSeverity } from "@/shared/proto/index.cline"
import { isDirectory } from "@/utils/fs"
+5 -3
View File
@@ -1,8 +1,10 @@
import type { ApiProviderInfo } from "@/core/api"
import { getDeepPlanningPrompt } from "./commands/deep-planning"
export const newTaskToolResponse = (willUseNativeTools: boolean) => {
const xmlExample = `
export const newTaskToolResponse = (enableNativeToolCalls?: boolean) => {
const xmlExample = enableNativeToolCalls
? ""
: `
Example:
<new_task>
<context>1. Current Work:
@@ -34,7 +36,7 @@ Example:
return `<explicit_instructions type="new_task">
The user has explicitly asked you to help them create a new task with preloaded context, which you will generate. The user may have provided instructions or additional information for you to consider when summarizing existing work and creating the context for the new task.
Irrespective of whether additional information or instructions are given, you are ONLY allowed to respond to this message by calling the new_task tool.${willUseNativeTools ? " You MUST call the new_task tool EVEN if it's not in your existing toolset." : ""}
Irrespective of whether additional information or instructions are given, you are ONLY allowed to respond to this message by calling the new_task tool.
The new_task tool is defined below:
@@ -1,812 +0,0 @@
# Contributing to System Prompts and Model Configuration
This guide explains how to add new model families and configure custom system prompts for contributors from model labs / providers.
> **⚡ Key Principle: Fallback to GENERIC**
>
> The system uses automatic fallbacks to minimize configuration:
> - **No matching variant?** → Falls back to `GENERIC` variant
> - **No tool variant for model family?** → Falls back to `GENERIC` tool variant
> - **No component override?** → Uses shared component from `components/`
>
> **This means:** Only customize what's necessary. Start minimal, add specifics only when needed.
## Table of Contents
1. [Glossary](#glossary)
2. [Architecture Overview](#architecture-overview)
3. [Creating a Model Family](#creating-a-model-family)
4. [Configuring System Prompts](#configuring-system-prompts)
5. [Configuring Tool Calling](#configuring-tool-calling)
6. [Configuring API Request/Response Shapes](#configuring-api-requestresponse-shapes)
7. [Adding Model-Specific Tools](#adding-model-specific-tools)
8. [Testing](#testing)
---
## Glossary
### Model Family
A category grouping models with similar capabilities and behavior patterns. Each family has an optimized system prompt variant.
**Examples:** `NEXT_GEN` (Claude 4+, GPT-5, Gemini 2.5), `GENERIC` (fallback), `XS` (small models)
**Location:** [`src/shared/prompts.ts`](../../shared/prompts.ts) `ModelFamily` enum
### System Prompt Variant
A complete configuration for a model family, including:
- Component selection and ordering
- Tool configuration
- Template with placeholders
- Matcher function determining when to use it
**Location:** [`variants/*/config.ts`](./variants/)
### Matcher Function
Function that determines if a variant applies to a given model and context. Returns `true` if the variant should be used.
```typescript
.matcher((context) => {
const modelId = context.providerInfo.model.id.toLowerCase()
return modelId.includes("gpt-5") && context.enableNativeToolCalls
})
```
### Native Tool Calling
Modern approach where tools are sent to the model via the provider's native API (e.g., OpenAI function calling, Anthropic tool use). More reliable than XML-based calling.
**Characteristics:**
- Tools passed separately via API (not embedded in system prompt)
- Structured tool calls in API response (JSON)
- Requires `enableNativeToolCalls` setting enabled
- Indicated by `use_native_tools: 1` label in variant config
**Supported providers:** OpenAI, Anthropic, Gemini, OpenRouter, Minimax
### XML (Text-Based) Tool Calling
Traditional approach where tools are described in the system prompt and the model outputs tool calls as XML tags in text.
**Characteristics:**
- Tools embedded in system prompt as XML format instructions
- Model generates XML: `<tool_name><param>value</param></tool_name>`
- Client parses XML from text response
- Works with any model that can follow instructions
### API Format
Defines the request/response structure for a model provider's API. Different formats have different message structures and capabilities.
**Values:** `ANTHROPIC_CHAT`, `GEMINI_CHAT`, `OPENAI_CHAT`, `R1_CHAT`, `OPENAI_RESPONSES`
**Location:** [`proto/cline/models.proto`](../../../proto/cline/models.proto)
**Usage:** `model.info.apiFormat` determines how requests/responses are structured
### Component
A reusable function that generates a section of the system prompt (e.g., `AGENT_ROLE`, `RULES`, `CAPABILITIES`). Components can be shared or overridden per-variant.
**Location:** [`components/`](./components/)
### Tool Specification
Defines how a tool appears in the system prompt for a specific model family. Multiple variants can exist for the same tool.
**Example:** [`tools/write_to_file.ts`](./tools/write_to_file.ts) defines `GENERIC`, `NATIVE_NEXT_GEN`, and `NATIVE_GPT_5` variants
---
## Architecture Overview
### Fallback Behavior
The system uses **automatic fallbacks** to ensure robustness:
1. **Variant Selection Fallback:**
- If no variant matcher returns `true`, falls back to `GENERIC` variant
- `GENERIC` is the universal fallback that works with all models
2. **Tool Variant Fallback:**
- If a tool doesn't define a variant for the current model family, automatically falls back to `GENERIC` tool variant
- Handled by `ClineToolSet.getToolByNameWithFallback()`
- **You only need to export model-specific tool variants when behavior differs from `GENERIC`**
3. **Component Fallback:**
- If a variant doesn't override a component, uses the shared component from [`components/`](./components/)
- Only override when model needs custom instructions
- Example: Most variants use shared `AGENT_ROLE`, but override `RULES` for model-specific behavior
**This means:** When adding a new model family, you can start with minimal configuration and only customize what's necessary.
### System Prompt Generation Flow
```
User Request
Model Detection (model-utils.ts)
Variant Selection (matcher functions) → Falls back to GENERIC if no match
Component Building (components/) → Uses shared components unless overridden
Tool Configuration (tools/) → Falls back to GENERIC tool variant if not defined
Template Resolution ({{PLACEHOLDER}})
Final System Prompt
```
### Key Files
| Purpose | File |
|---------|------|
| Model detection | [`src/utils/model-utils.ts`](../../../utils/model-utils.ts) |
| Model family enum | [`src/shared/prompts.ts`](../../shared/prompts.ts) |
| Tool enum | [`src/shared/tools.ts`](../../shared/tools.ts) |
| Variant registry | [`variants/index.ts`](./variants/index.ts) |
| Tool registry | [`tools/init.ts`](./tools/init.ts) |
---
## Creating a Model Family
### Step 1: Add Model Detection Logic
Add helper functions to [`src/utils/model-utils.ts`](../../../utils/model-utils.ts):
```typescript
// Add detector function
export function isMyNewModelFamily(id: string): boolean {
const modelId = normalize(id)
return modelId.includes("my-model") || modelId.includes("my-model-v2")
}
// If it's a next-gen model, add to isNextGenModelFamily()
export function isNextGenModelFamily(id: string): boolean {
return (
isClaude4PlusModelFamily(modelId) ||
// ... existing checks
isMyNewModelFamily(modelId) // Add here
)
}
// If it's a next-gen provider, add to isNextGenModelProvider()
export function isNextGenModelProvider(providerInfo: ApiProviderInfo): boolean {
const providerId = normalize(providerInfo.providerId)
return [
"anthropic", "openai", "gemini", "openrouter",
"my-new-provider", // Add here
].some((id) => providerId === id)
}
```
### Step 2: Add Model Family Enum
Add to `ModelFamily` enum in [`src/shared/prompts.ts`](../../shared/prompts.ts):
```typescript
export enum ModelFamily {
CLAUDE = "claude",
GPT_5 = "gpt-5",
NEXT_GEN = "next-gen",
MY_NEW_MODEL = "my-new-model", // Add here
}
```
### Step 3: Create Variant Configuration
Create [`variants/my-new-model/config.ts`](./variants/):
```typescript
import { isMyNewModelFamily, isNextGenModelProvider } from "@utils/model-utils"
import { ModelFamily } from "@/shared/prompts"
import { ClineDefaultTool } from "@/shared/tools"
import { SystemPromptSection } from "../../templates/placeholders"
import { createVariant } from "../variant-builder"
import { validateVariant } from "../variant-validator"
export const config = createVariant(ModelFamily.MY_NEW_MODEL)
.description("Optimized for My New Model")
.version(1)
.tags("production", "my-model")
.labels({
stable: 1,
production: 1,
// Add use_native_tools: 1 if native tool calling supported
})
.matcher((context) => {
const modelId = context.providerInfo.model.id
return isMyNewModelFamily(modelId)
})
// Template: Structure with placeholders that will be replaced
.template(`{{AGENT_ROLE_SECTION}}
====
{{TOOL_USE_SECTION}}
====
{{RULES_SECTION}}
====
{{OBJECTIVE_SECTION}}`)
// Components: Which sections to include (must match template placeholders)
.components(
SystemPromptSection.AGENT_ROLE,
SystemPromptSection.TOOL_USE,
SystemPromptSection.RULES,
SystemPromptSection.OBJECTIVE,
)
.tools(
ClineDefaultTool.BASH,
ClineDefaultTool.FILE_READ,
ClineDefaultTool.ASK,
)
.placeholders({
MODEL_FAMILY: ModelFamily.MY_NEW_MODEL,
})
.config({})
.build()
// Validation
const validationResult = validateVariant({ ...config, id: ModelFamily.MY_NEW_MODEL }, { strict: true })
if (!validationResult.isValid) {
throw new Error(`Invalid config: ${validationResult.errors.join(", ")}`)
}
export type MyNewModelVariantConfig = typeof config
```
**How Templates and Placeholders Work:**
The `.template()` defines the **structure** of your system prompt using placeholders like `{{AGENT_ROLE_SECTION}}`, `{{RULES_SECTION}}`, etc.
**Placeholder Resolution Process:**
1. **Component Building:** Each component in `.components()` generates content by calling its function from [`components/`](./components/)
- `SystemPromptSection.AGENT_ROLE` → generates `{{AGENT_ROLE_SECTION}}` content
- `SystemPromptSection.RULES` → generates `{{RULES_SECTION}}` content
- etc.
2. **Default vs Override:**
- **By default:** Uses shared component from [`components/`](./components/) (e.g., [`components/rules.ts`](./components/rules.ts))
- **With override:** Uses your custom template instead
3. **Template Resolution:** The `TemplateEngine` replaces all `{{PLACEHOLDERS}}` with generated content
**Example with Override:**
```typescript
import { CUSTOM_AGENT_ROLE } from "./template"
export const config = createVariant(ModelFamily.MY_NEW_MODEL)
.template(`{{AGENT_ROLE_SECTION}}
{{RULES_SECTION}}`)
.components(
SystemPromptSection.AGENT_ROLE, // Will use override below
SystemPromptSection.RULES, // Will use shared components/rules.ts
)
// Override AGENT_ROLE to use custom template
.overrideComponent(SystemPromptSection.AGENT_ROLE, {
template: CUSTOM_AGENT_ROLE, // Your custom content
})
.build()
```
**Result:**
- `{{AGENT_ROLE_SECTION}}` → Replaced with `CUSTOM_AGENT_ROLE` content (overridden)
- `{{RULES_SECTION}}` → Replaced with shared `components/rules.ts` content (default)
See [`variants/native-gpt-5-1/config.ts`](./variants/native-gpt-5-1/config.ts) for a real example with multiple overrides.
### Step 4: Register Variant
Add to [`variants/index.ts`](./variants/index.ts):
```typescript
export { config as myNewModelConfig } from "./my-new-model/config"
import { config as myNewModelConfig } from "./my-new-model/config"
export const VARIANT_CONFIGS = {
// ... existing variants
[ModelFamily.MY_NEW_MODEL]: myNewModelConfig,
} as const
```
---
## Configuring System Prompts
### Basic Configuration
See [Step 3 above](#step-3-create-variant-configuration) for basic variant structure.
### Component Overrides
**Default behavior:** If you don't override a component, the variant automatically uses the shared component from [`components/`](./components/).
**Only override when:**
- Model needs custom instructions for a specific section
- Default component doesn't work well for the model
- Model has unique capabilities requiring different guidance
**To override a component:**
**Create [`variants/my-new-model/template.ts`](./variants/):**
```typescript
export const CUSTOM_RULES_TEMPLATE = `
# Rules for My New Model
1. Use specific syntax optimized for this model
2. Avoid patterns this model struggles with
3. Leverage unique capabilities
`
```
**Update `config.ts`:**
```typescript
import { CUSTOM_RULES_TEMPLATE } from "./template"
export const config = createVariant(ModelFamily.MY_NEW_MODEL)
// ... other configuration
.overrideComponent(SystemPromptSection.RULES, {
template: CUSTOM_RULES_TEMPLATE,
})
.build()
```
### Available Components
You can include/exclude these in `.components()`:
- `AGENT_ROLE` - Agent identity and role
- `TOOL_USE` - Tool usage instructions
- `TASK_PROGRESS` - Task progress tracking
- `MCP` - MCP server information
- `EDITING_FILES` - File editing guidelines
- `ACT_VS_PLAN` - Action vs planning mode
- `CAPABILITIES` - Agent capabilities
- `FEEDBACK` - Feedback and improvement
- `RULES` - Behavioral rules
- `SYSTEM_INFO` - System environment info
- `OBJECTIVE` - Current task objective
- `USER_INSTRUCTIONS` - User custom instructions
- `TODO` - Todo management
See [`components/`](./components/) for implementations.
### Available Tools
Common tools to include in `.tools()`:
- `BASH` - Execute shell commands
- `FILE_READ`, `FILE_NEW`, `FILE_EDIT` - File operations
- `SEARCH`, `LIST_FILES`, `LIST_CODE_DEF` - Code search
- `BROWSER`, `WEB_FETCH` - Web operations
- `MCP_USE`, `MCP_ACCESS` - MCP integration
- `ASK`, `ATTEMPT` - Task management
- `PLAN_MODE`, `ACT_MODE` - Mode switching
- `TODO` - Todo management
See [`src/shared/tools.ts`](../../shared/tools.ts) for full list.
---
## Configuring Tool Calling
### Native Tool Calling
**When to use:** Provider supports native function calling and `enableNativeToolCalls` is enabled.
**Example:** [`variants/native-next-gen/config.ts`](./variants/native-next-gen/config.ts)
```typescript
export const config = createVariant(ModelFamily.NATIVE_NEXT_GEN)
.labels({
use_native_tools: 1, // Enable native tool calling
})
.matcher((context) => {
if (!context.enableNativeToolCalls) {
return false
}
if (!isNextGenModelProvider(context.providerInfo)) {
return false
}
return isNextGenModelFamily(context.providerInfo.model.id)
})
// ... rest of configuration
```
**Key points:**
- Set `use_native_tools: 1` label
- Check `context.enableNativeToolCalls` in matcher
- Check provider supports native tools via `isNextGenModelProvider()`
- Tools sent separately via API, not embedded in prompt
### XML Tool Calling
**When to use:** Provider doesn't support native tools OR `enableNativeToolCalls` is disabled.
**Example:** [`variants/next-gen/config.ts`](./variants/next-gen/config.ts)
```typescript
export const config = createVariant(ModelFamily.NEXT_GEN)
.matcher((context) => {
const providerInfo = context.providerInfo
// Use this variant if next-gen BUT native tools disabled
if (isNextGenModelFamily(providerInfo.model.id) && !context.enableNativeToolCalls) {
return true
}
// OR if provider doesn't support native tools
return !isNextGenModelProvider(providerInfo) && isNextGenModelFamily(providerInfo.model.id)
})
.tools(
// Include MCP_USE for XML-based tool calling
ClineDefaultTool.MCP_USE, // Instead of MCP_ACCESS
// ... other tools
)
```
**Key points:**
- Don't set `use_native_tools` label
- Check native tools are disabled OR provider doesn't support them
- Include detailed tool descriptions in system prompt
- Use `MCP_USE` instead of `MCP_ACCESS`
### Decision Flow
```
Is enableNativeToolCalls enabled?
NO → Use XML variant
YES → Does provider support native tools?
NO → Use XML variant
YES → Does model support native tools?
NO → Use XML variant
YES → Use native variant
```
---
## Configuring API Request/Response Shapes
### Setting API Format
API formats are defined in [`proto/cline/models.proto`](../../../proto/cline/models.proto):
```protobuf
enum ApiFormat {
ANTHROPIC_CHAT = 0; // Messages API
GEMINI_CHAT = 1; // Gemini generateContent
OPENAI_CHAT = 2; // Chat Completions API
R1_CHAT = 3; // DeepSeek R1 format
OPENAI_RESPONSES = 4; // Responses API (GPT-5.1+)
}
```
### Using API Format in Provider Code
**Example from [`src/core/api/providers/openai-native.ts`](../../api/providers/openai-native.ts):**
```typescript
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: ChatCompletionTool[]): ApiStream {
// Route based on API format
if (tools?.length && this.getModel()?.info?.apiFormat === ApiFormat.OPENAI_RESPONSES) {
yield* this.createResponseStream(systemPrompt, messages, tools)
} else {
yield* this.createCompletionStream(systemPrompt, messages, tools)
}
}
```
### Format Comparison
| Format | Provider | Tool Support | System Prompt | Special Features |
|--------|----------|--------------|---------------|------------------|
| `ANTHROPIC_CHAT` | Anthropic | Native (input_schema) | Content blocks | Caching, thinking |
| `GEMINI_CHAT` | Gemini/Vertex | Native (function_declarations) | String | Thinking levels |
| `OPENAI_CHAT` | OpenAI, OpenRouter | Native (function) | String | Reasoning effort |
| `R1_CHAT` | DeepSeek R1 | Limited | String | Reasoning-focused |
| `OPENAI_RESPONSES` | GPT-5.1+ | Native (strict mode) | String | Structured outputs |
### Adding a New API Format
1. **Add to proto:** [`proto/cline/models.proto`](../../../proto/cline/models.proto)
2. **Regenerate:** `npm run protos`
3. **Import:** `import { ApiFormat } from "@/shared/proto/cline/models"`
4. **Handle in provider:** Add format-specific logic in your provider handler
See existing providers in [`src/core/api/providers/`](../../api/providers/) for examples.
---
## Adding Model-Specific Tools
### When to Create Model-Specific Tool Variants
**Default behavior:** Tools automatically fall back to `GENERIC` variant via `ClineToolSet.getToolByNameWithFallback()`.
**Only create a model-specific tool variant when:**
- Tool needs different parameters or descriptions for the model
- Tool requires model-specific instructions
- Tool behavior differs significantly across models
**Examples requiring specific variants:**
- Native tool calling models need absolute paths vs relative paths
- Models with different context handling need adjusted descriptions
- Models with specific quirks need tailored instructions
**Important:** If you only export `[GENERIC]` from your tool file, all model families will use it automatically. You don't need to create variants for every model family.
### Creating a Tool Variant
**Example from [`tools/write_to_file.ts`](./tools/write_to_file.ts):**
```typescript
import { ModelFamily } from "@/shared/prompts"
import { ClineDefaultTool } from "@/shared/tools"
import type { ClineToolSpec } from "../spec"
const id = ClineDefaultTool.FILE_NEW
const GENERIC: ClineToolSpec = {
variant: ModelFamily.GENERIC,
id,
name: "write_to_file",
description: "Request to write content to a file...",
parameters: [
{
name: "path",
required: true,
instruction: "The path of the file to write to (relative to {{CWD}})",
usage: "File path here",
},
{
name: "content",
required: true,
instruction: "The content to write. ALWAYS provide COMPLETE content.",
usage: "Your file content here",
},
],
}
const NATIVE_NEXT_GEN: ClineToolSpec = {
variant: ModelFamily.NATIVE_NEXT_GEN,
id,
name: "write_to_file",
description: "[IMPORTANT: Always output absolutePath first] Request to write...",
parameters: [
{
name: "absolutePath",
required: true,
instruction: "The absolute path to the file.",
},
{
name: "content",
required: true,
instruction: "After providing path, use this for content.",
},
],
}
export const write_to_file_variants = [GENERIC, NATIVE_NEXT_GEN]
```
### Key Differences in Tool Variants
**GENERIC (XML-based):**
- Relative paths (with `{{CWD}}` placeholder)
- Verbose instructions
- XML usage examples
**NATIVE_NEXT_GEN (Native calling):**
- Absolute paths (clearer for structured API)
- Concise instructions
- Parameter ordering hints (e.g., "Always output X first")
### Registering Tool Variants
**1. Export from [`tools/index.ts`](./tools/index.ts):**
```typescript
export * from "./write_to_file"
```
**2. Register in [`tools/init.ts`](./tools/init.ts):**
```typescript
import { write_to_file_variants } from "./write_to_file"
export function registerClineToolSets(): void {
const allToolVariants = [
...write_to_file_variants,
// ... other tool variants
]
allToolVariants.forEach((v) => ClineToolSet.register(v))
}
```
### Adding Tool to Variant Configs
**Update all relevant variant configs** in [`variants/*/config.ts`](./variants/) to include the tool:
```typescript
.tools(
ClineDefaultTool.BASH,
ClineDefaultTool.FILE_NEW, // Add your tool here
// ... other tools
)
```
**Important:** If you add a tool to a variant's config, ensure either:
1. The tool exports a spec for that `ModelFamily`, OR
2. The tool exports a `GENERIC` spec (automatic fallback)
**Note:** When a variant includes a tool in `.tools()` but the tool doesn't have a specific variant for that model family, the system automatically uses the `GENERIC` variant. This is handled by `ClineToolSet.getToolByNameWithFallback()`, so you don't need to manually define variants for every model family—only when behavior needs to differ.
---
## Testing
### Running Tests
```bash
# Run tests (fails if snapshots don't match)
npm run test:unit
# Update snapshots after intentional changes
npm run test:unit -- --update-snapshots
# OR
UPDATE_SNAPSHOTS=true npm run test:unit
```
### Snapshot Tests
**Location:** [`__tests__/__snapshots__/`](./__tests__/__snapshots__/)
**What's tested:**
- System prompts generate correctly for each model family
- Prompts remain consistent across contexts (browser, MCP, focus chain)
- Component ordering and overrides work correctly
- Tool specifications properly included
**Test file:** [`__tests__/integration.test.ts`](./__tests__/integration.test.ts)
### Testing in Debug Mode
**For live testing with real models**, run Cline in debug mode to verify your variant works correctly:
1. **Enable Debug Mode:**
- See the main [CONTRIBUTING.md](../../../../CONTRIBUTING.md) for instructions on running Cline in debug mode
- Debug mode enables additional features for testing and verification
2. **Run a Task with Your Model:**
- Configure your model in Cline settings
- Start a conversation or task with the model
- The system will automatically select your variant based on the matcher function
3. **Export Task JSON (Debug Mode Only):**
- After the task completes, click the **task header** in the chat
- Look for the **export JSON** option (only available in debug mode)
- Export the task JSON file
4. **Verify Your Configuration:**
- Open the exported JSON file
- Search for `"systemPrompt"` to see the full generated system prompt
- Verify:
- Correct variant was selected
- All placeholders resolved correctly
- Component overrides applied
- Tools included as expected
- Template structure matches your config
**Example verification:**
```json
{
"systemPrompt": "You are Cline...\n\n====\n\n# Agent Role\n...",
"modelFamily": "my-new-model",
"tools": ["bash", "file_read", "ask"],
// ... rest of task data
}
```
This exported JSON is invaluable for debugging and verifying that your variant configuration is working as intended in real-world usage.
### Manual Testing Checklist
1. **Verify variant selection:**
- Confirm correct variant selected for test model IDs
- Check matcher logic returns true/false as expected
- Use exported JSON to verify `modelFamily` matches expected value
2. **Test tool conversion:**
- Verify tools converted to correct format (native vs XML)
- Check provider-specific tool format matches expectations
- Review tools in exported JSON to confirm correct conversion
3. **Validate prompt structure:**
- Confirm all `{{PLACEHOLDERS}}` resolved in exported JSON
- Check section ordering matches config
- Verify overrides applied correctly by inspecting `systemPrompt` field
4. **Test across contexts:**
- With/without browser support
- With/without MCP servers
- With/without native tool calling enabled
- Export JSON for each context to compare differences
---
## Additional Resources
- **System Prompt Architecture:** [README.md](./README.md)
- **Tool Development:** [tools/README.md](./tools/README.md)
- **Testing Guide:** [__tests__/README.md](./__tests__/README.md)
- **Model Utilities:** [`src/utils/model-utils.ts`](../../../utils/model-utils.ts)
- **Proto Definitions:** [`proto/cline/models.proto`](../../../proto/cline/models.proto)
- **CLAUDE.md:** [`CLAUDE.md`](../../../../CLAUDE.md) (tribal knowledge)
---
## Quick Reference
### Common File Locations
```
src/
├── shared/
│ ├── prompts.ts # ModelFamily enum
│ └── tools.ts # ClineDefaultTool enum
├── utils/
│ └── model-utils.ts # Model detection functions
├── core/
│ ├── api/providers/ # API provider handlers
│ └── prompts/system-prompt/
│ ├── components/ # Shared prompt components
│ ├── tools/ # Tool specifications
│ ├── variants/ # Model family configs
│ │ ├── generic/
│ │ ├── next-gen/
│ │ ├── native-next-gen/
│ │ └── [family]/
│ │ ├── config.ts # Variant configuration
│ │ └── template.ts # Custom templates
│ └── registry/ # Core logic
│ ├── PromptRegistry.ts
│ ├── PromptBuilder.ts
│ └── ClineToolSet.ts
proto/
└── cline/
└── models.proto # ApiFormat enum
```
### Common Patterns
**Model detection:**
```typescript
export function isMyModelFamily(id: string): boolean {
return normalize(id).includes("my-model")
}
```
**Variant matcher:**
```typescript
.matcher((context) => isMyModelFamily(context.providerInfo.model.id))
```
**Component override:**
```typescript
.overrideComponent(SystemPromptSection.RULES, { template: CUSTOM_TEMPLATE })
```
**Native tools check:**
```typescript
.matcher((context) =>
context.enableNativeToolCalls &&
isNextGenModelProvider(context.providerInfo)
)
```
---
For questions or issues, consult existing variant configurations in [`variants/`](./variants/) or review the model detection logic in [`model-utils.ts`](../../../utils/model-utils.ts).
@@ -171,6 +171,24 @@ Usage:
<text>Text to type (optional)</text>
</browser_action>
## web_fetch
Description: Fetches content from a specified URL and processes into markdown
- Takes a URL as input
- Fetches the URL content, converts HTML to markdown
- Use this tool when you need to retrieve and analyze web content
- IMPORTANT: If an MCP-provided web fetch tool is available, prefer using that tool instead of this one, as it may have fewer restrictions.
- The URL must be a fully-formed valid URL
- HTTP URLs will be automatically upgraded to HTTPS
- This tool is read-only and does not modify any files
Parameters:
- url: (required) The URL to fetch content from
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
Usage:
<web_fetch>
<url>https://example.com/docs</url>
<task_progress>Checklist here (optional)</task_progress>
</web_fetch>
## use_mcp_tool
Description: Request to use a tool provided by a connected MCP server. Each MCP server can provide multiple tools with different capabilities. Tools have defined input schemas that specify required and optional parameters.
Parameters:

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