Compare commits

..
164 changed files with 3712 additions and 7840 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Refactor Anthropic handler to use metadata for reasoning support
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
docs: add workflow guide for adding settings to Cline
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Exclude files without extensions (and dotfiles) from getDiffSet results if they are binary
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Fix Cerebras rate limiting by using conservative max_tokens (16K) instead of model maximum.
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Fixed Auto Compact not triggering when using Claude Code provider. Short model aliases like "sonnet" and "opus" are now correctly recognized as Claude 4+ models.
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Fix Workspace and Favorites history filters to work independently instead of being mutually exclusive
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Fixed connection failures with remote MCP servers that return 404 instead of 405 for SSE stream checks. This was causing "Failed to open SSE stream: Not Found" errors after the v3.46.0 SDK upgrade.
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: correct typos in gemini system prompt overrides
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
feat(prompts): modify prompts for parallel tool usage in claude and gemini 3 models
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Stop automatically opening Cline sidebar on extension update - only show a notification
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
add supportsReasoning property to Baseten models
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Bug fix for the isMacOSOrLinux() function in the webview-ui/ code for the extension.
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
show slash command autocompletion in the cli
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
fix regression that broke JSON parsing for SAP AI Core provider in native API mode for claude models
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: Fetch remote config values from the cache
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Fix Baseten model selector issue in model picker modal mode
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Replace current diff edit tools with Apply Patch tool for GPT-5+ models
@@ -1,51 +0,0 @@
#!/bin/bash
set -euo pipefail
# Only run in Claude Code remote environments
if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then
exit 0
fi
cd "$CLAUDE_PROJECT_DIR"
echo "=== Claude Code for Web Setup ==="
echo ""
# Install latest gh CLI tool
echo "Installing GitHub CLI..."
GH_VERSION=$(curl -s https://api.github.com/repos/cli/cli/releases/latest | grep '"tag_name"' | cut -d'"' -f4 | sed 's/^v//')
curl -sL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o /tmp/gh.tar.gz
tar -xzf /tmp/gh.tar.gz -C /tmp
sudo mv "/tmp/gh_${GH_VERSION}_linux_amd64/bin/gh" /usr/local/bin/gh
rm -rf /tmp/gh.tar.gz /tmp/gh_${GH_VERSION}_linux_amd64
echo "Installed gh version: $(gh --version | head -1)"
echo ""
# Check if GITHUB_TOKEN is set and configure gh
if [ -n "${GITHUB_TOKEN:-}" ]; then
echo "GITHUB_TOKEN is configured - gh CLI is ready to use"
echo ""
echo "You can use gh commands directly, for example:"
echo " gh issue list --repo cline/cline --limit 5"
echo " gh pr list --repo cline/cline --state open"
echo " gh issue view 123 --repo cline/cline"
echo ""
else
echo "GITHUB_TOKEN is not set - gh CLI will have limited functionality"
echo ""
echo "To enable full GitHub API access:"
echo "1. Create a Fine-grained Personal Access Token at https://github.com/settings/tokens?type=beta"
echo "2. Add it as GITHUB_TOKEN in your Claude Code environment settings"
echo ""
fi
# Install project dependencies
echo "Installing dependencies..."
npm run install:all
# Generate gRPC/protobuf types (required for TypeScript)
echo "Generating proto types..."
npm run protos
echo ""
echo "Session setup complete!"
-14
View File
@@ -1,14 +0,0 @@
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/claude-code-for-web-setup.sh"
}
]
}
]
}
}
-162
View File
@@ -1,162 +0,0 @@
# Adding a Setting to Cline
Add a new user-facing setting (boolean toggle, dropdown, etc.) to Cline.
## Overview
Settings in Cline flow through multiple layers:
1. **Proto definition** → TypeScript generation
2. **Backend state management** → reading, writing, persistence
3. **State broadcasting** → sending to webview
4. **Frontend** → displaying and updating
**Common gotcha**: Forgetting to include the setting in `getStateToPostToWebview()` causes the setting to reset when navigating away from settings or toggling other settings.
## Step 1: Proto Definition
Add the field to both messages in `proto/cline/state.proto`:
1. **GlobalState** (for persistence):
```protobuf
message GlobalState {
// ... existing fields
optional bool your_setting = NEXT_NUMBER;
}
```
2. **UpdateSettingsRequest** (for updates from UI):
```protobuf
message UpdateSettingsRequest {
// ... existing fields
optional bool your_setting = NEXT_NUMBER;
}
```
Generate TypeScript:
```bash
npm run proto:generate
```
## Step 2: TypeScript Type Definitions
### `src/shared/storage/state-keys.ts`
Add to the `Settings` interface:
```typescript
export interface Settings {
// ... existing
yourSetting: boolean
}
```
### `src/shared/ExtensionMessage.ts`
Add to the `ExtensionState` interface:
```typescript
export interface ExtensionState {
// ... existing
yourSetting?: boolean
}
```
## Step 3: Backend State Management
### `src/core/storage/utils/state-helpers.ts`
Add reading from storage (with default value):
```typescript
// Near other similar reads (~line 320)
const yourSetting = context.globalState.get<GlobalStateAndSettings["yourSetting"]>("yourSetting")
// In the return object (~line 690)
return {
// ... existing
yourSetting: yourSetting ?? false, // default value
}
```
### `src/core/controller/state/updateSettings.ts`
Add handling for the setting update:
```typescript
if (request.yourSetting !== undefined) {
controller.stateManager.setGlobalState("yourSetting", !!request.yourSetting)
}
```
## Step 4: State Broadcasting (CRITICAL)
### `src/core/controller/index.ts`
**This is the most commonly missed step!**
In `getStateToPostToWebview()`:
1. **Read the setting** (near other similar reads ~line 867):
```typescript
const yourSetting = this.stateManager.getGlobalSettingsKey("yourSetting")
```
2. **Include in return object** (~line 968):
```typescript
return {
// ... existing
yourSetting,
}
```
Without this, the setting will appear to save but will reset when the UI refreshes.
## Step 5: Frontend
### `webview-ui/src/context/ExtensionStateContext.tsx`
Add default value in `defaultState`:
```typescript
const defaultState: ExtensionState = {
// ... existing
yourSetting: false,
}
```
### UI Component (e.g., `FeatureSettingsSection.tsx`)
1. **Extract from context**:
```typescript
const { yourSetting } = useExtensionState()
```
2. **Render the control**:
```tsx
<VSCodeCheckbox
checked={yourSetting}
onChange={(e: any) => {
const checked = e.target.checked === true
updateSetting("yourSetting", checked)
}}>
Your Setting Label
</VSCodeCheckbox>
```
## Checklist
- [ ] `proto/cline/state.proto` - GlobalState field
- [ ] `proto/cline/state.proto` - UpdateSettingsRequest field
- [ ] `npm run proto:generate`
- [ ] `src/shared/storage/state-keys.ts` - Settings interface
- [ ] `src/shared/ExtensionMessage.ts` - ExtensionState interface
- [ ] `src/core/storage/utils/state-helpers.ts` - read with default
- [ ] `src/core/controller/state/updateSettings.ts` - handle update
- [ ] `src/core/controller/index.ts` - **getStateToPostToWebview()** (read + return)
- [ ] `webview-ui/src/context/ExtensionStateContext.tsx` - default state
- [ ] UI component - display and onChange handler
## Testing
1. Toggle the setting ON
2. Navigate away from settings (e.g., to chat)
3. Return to settings - verify setting is still ON
4. Toggle a DIFFERENT setting
5. Verify your setting didn't reset
6. Reload the window - verify persistence
+1 -1
View File
@@ -1,4 +1,4 @@
/docs/
/.github/ @saoudrizwan @garoth @sjf
/README.md @saoudrizwan @nickbaumann98
/src/core/storage/ @celestial-vault @abeatrix
/src/core/storage/ @celestial-vault
-173
View File
@@ -1,173 +0,0 @@
name: Claude Issue Triage
on:
issues:
types: [opened]
# Manual trigger for backfilling existing issues. Run from terminal:
# gh workflow run claude-issue-triage.yml -f issue_number=1234
# Or batch process:
# gh issue list --state open --limit 10 --json number --jq '.[].number' | while read num; do
# gh workflow run claude-issue-triage.yml -f issue_number=$num
# sleep 60
# done
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number to triage'
required: true
type: string
jobs:
claude-issue-triage:
runs-on: ubuntu-latest
timeout-minutes: 120
# SECURITY: These permissions are intentionally restrictive.
# - contents: read -> Claude can read the codebase but CANNOT write/push any code
# - issues: write -> Claude can comment and add labels (the only write access needed)
# - pull-requests: read -> Claude can view PR context but CANNOT create PRs
# This ensures that even if a malicious user attempts prompt injection via issue content,
# Claude cannot modify repository code, create branches, or open PRs.
permissions:
contents: read
issues: write
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Issue Response & Triage
id: triage
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
# Allow all tools - security is enforced by GitHub permissions above (contents: read, issues: write)
claude_args: --model claude-opus-4-5-20251101 --allowedTools "Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch"
prompt: |
You're a GitHub issue first responder for the open source Cline repository.
**Issue:** #${{ github.event.issue.number || inputs.issue_number }}
**Title:** ${{ github.event.issue.title || 'See issue details below' }}
**Author:** @${{ github.event.issue.user.login || 'See issue details below' }}
## Your job
Investigate this issue thoroughly, then post a single helpful comment that helps the user and gives maintainers the context they need.
## Investigation
Start by reading the full issue:
gh issue view ${{ github.event.issue.number || inputs.issue_number }}
### Search for duplicates and related issues
Search thoroughly for existing issues that match this one:
gh issue list --search "<keywords from the issue>" --state all --limit 30
gh issue list --search "<error messages>" --state all --limit 20
gh issue list --search "<affected feature/component>" --state all --limit 20
For each relevant issue you find, read it including its comments:
gh issue view <number> --comments
You're looking for:
- **Duplicates**: Issues describing the same problem. Link to them and explain why you think they're duplicates. If closed, check how they were resolved - the solution might apply here.
- **Related issues**: Similar problems or context that could help. Pull useful information from their comments (workarounds others found, debugging steps that helped, maintainer explanations). Link to them and explain the connection.
If there are closed issues with solutions, surface those solutions prominently - this might immediately solve the user's problem.
### Analyze recent changes (ALWAYS DO THIS)
Many issues are regressions from recent releases. **Always** check what changed recently:
gh release list --limit 10
gh pr list --state merged --limit 50 --json number,title,mergedAt,author,body
Look for PRs merged in the last few weeks that might correlate with the issue. If you find a likely connection:
gh pr view <number>
gh pr diff <number>
git log --since="1 month ago" --oneline -- <relevant paths>
git show <commit>
**Always include your findings in your comment:**
- If you find a regression, call it out explicitly: which PR/commit likely caused it, who authored it, what changed, and suggest a fix direction if you can see one.
- If you don't find anything related, still mention it: "I analyzed recent PRs and releases but didn't find any changes that seem related to this issue."
### Search the codebase
Find the relevant code:
- Use grep/find to locate code related to the issue
- Key areas: `src/api/` (providers/models), `src/core/prompts/` (tools/prompts), platform-specific code for VS Code vs JetBrains
### Find documentation
Cline docs are at **https://docs.cline.bot/** and built with Mintlify from the `docs/` directory.
The URL structure maps directly to the file structure:
- `docs/getting-started/selecting-your-model.mdx` → https://docs.cline.bot/getting-started/selecting-your-model
- `docs/troubleshooting.mdx` → https://docs.cline.bot/troubleshooting
- Headings become anchors: `## Which Model` → `#which-model`
Search the `docs/` directory to find relevant documentation, then construct URLs to link users to:
```bash
ls docs/
grep -r "keyword" docs/ --include="*.mdx" -l
```
### Identify subject matter experts
For issues that clearly need engineering attention:
git log --since="6 months ago" --format="%an" -- <relevant paths> | sort | uniq -c | sort -rn | head -5
Cross-reference with GitHub usernames. Include in your response (@mention, do NOT assign):
| SME | Reason |
|-----|--------|
| @username1 | Authored PR #X which modified this area |
| @username2 | Primary contributor to affected file |
## Weak model detection
Many issues are caused by users running small or non-frontier models that don't tool-call reliably. Signs include:
- Model failing to use tools correctly
- Nonsensical or malformed responses
- User is running a small/local model or older model version
If this looks like a weak model issue, kindly suggest they try reproducing with Claude Sonnet and report back if it persists. Link to https://docs.cline.bot/getting-started/selecting-your-model if helpful. Still label and triage normally.
## Your comment
Write a single comment as a helpful community member. Be conversational, not robotic. Include what's relevant:
- **Helpful response** - Answer their question, suggest a fix, provide a workaround. If you found solutions in related closed issues, surface those prominently.
- **Duplicates and related issues** - Link to any you found and explain why they're duplicates/related. Summarize useful context from their comments.
- **Regression analysis** - If this looks like a regression, explain what change likely caused it, link to the PR/commit, and tag the author.
- **Clarifying questions** - If you need more info, ask specific questions. Don't ask for things already provided.
- **SME table** - Include the table above if this needs engineering attention. Don't tag people for questions with obvious answers or weak-model issues.
- **Context for maintainers** - Relevant code paths, what you found. Keep it concise.
- **Docs links** - If there's relevant documentation, link to it naturally in your response as a recommendation (e.g., "For more details, check out [the Ollama setup guide](url)"). Do NOT add a "Sources" section at the end - integrate doc links into your response where they're helpful.
- **Possible Duplicates section** - ALWAYS include a "Possible Duplicates" section at the end of your comment listing issues that might be duplicates so maintainers can quickly close if appropriate. If none found, say "No obvious duplicates found."
## Labels
First, retrieve all available labels and read their descriptions to understand what each is for:
gh label list --json name,description --limit 100
Then apply the appropriate labels based on your analysis. Only use labels from the list above—do not create new labels.
gh issue edit ${{ github.event.issue.number || inputs.issue_number }} --add-label "label1,label2"
If your regression analysis found a likely culprit (a recent PR/commit that probably caused this issue), add the "Regression" label:
gh issue edit ${{ github.event.issue.number || inputs.issue_number }} --add-label "Regression"
IMPORTANT: After posting your comment, add the "Bot Responded" label to indicate this issue has received an automated response:
gh issue edit ${{ github.event.issue.number || inputs.issue_number }} --add-label "Bot Responded"
## Remember
- **This is a one-time automated response** - you will NOT see their reply or respond again. Never say things like "I can help you", "let me know", "once I have that info", or "I can give you more targeted help" - you won't be there to follow up. If you ask clarifying questions, frame them for the maintainers who will follow up, e.g., "If you can share X, that would help the maintainers diagnose this."
- Don't be formulaic. Respond to what the issue actually needs.
- Surface solutions from past issues - often the fastest path to helping.
- Connecting regressions to specific changes is extremely valuable.
- Link issues with #number so they're clickable.
-272
View File
@@ -1,272 +0,0 @@
name: Claude PR Review
on:
pull_request:
types: [opened, ready_for_review]
# Manual trigger for backfilling existing PRs. Run from terminal:
# gh workflow run claude-pr-review.yml -f pr_number=1234
# Or batch process open PRs:
# gh pr list --state open --limit 10 --json number --jq '.[].number' | while read num; do
# gh workflow run claude-pr-review.yml -f pr_number=$num
# sleep 60
# done
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to review'
required: true
type: string
jobs:
claude-pr-review:
# Runs on PR opened/ready_for_review (skips drafts) or manual trigger for backfilling
if: |
(github.event_name == 'pull_request' && github.event.pull_request.draft == false) ||
github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 120
# SECURITY: These permissions are intentionally restrictive.
# - contents: read -> Claude can read the codebase but CANNOT write/push any code
# - pull-requests: write -> Claude can post reviews and inline suggestions
# - issues: read -> Claude can search for related issues
# NOTE: Even with pull-requests: write, Claude CANNOT merge PRs because branch protection
# requires 1 approval from a Code Owner. The GITHUB_TOKEN cannot bypass this.
permissions:
contents: read
pull-requests: write
issues: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get PR number
id: pr
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "number=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT
else
echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
fi
- name: Run PR Review
id: review
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
claude_args: --model claude-opus-4-5-20251101 --allowedTools "Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch"
prompt: |
You're a GitHub PR reviewer for the open source Cline repository. Your goal is to give the PR author helpful feedback and give maintainers the context they need to review efficiently.
PR: #${{ steps.pr.outputs.number }}
## Gather context
```bash
# Get full PR details
gh pr view ${{ steps.pr.outputs.number }} --json number,title,body,author,createdAt,updatedAt,isDraft,labels,commits,files,additions,deletions,changedFiles,baseRefName,headRefName,mergeable,reviewDecision
# Get the diff
gh pr diff ${{ steps.pr.outputs.number }}
# Check CI status
gh pr checks ${{ steps.pr.outputs.number }}
# Get existing review comments (to understand context and your previous feedback)
gh api repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.number }}/comments --jq '.[] | {user: .user.login, body: .body, path: .path, created_at: .created_at}'
# Get conversation comments
gh pr view ${{ steps.pr.outputs.number }} --comments
```
If this is a re-review (workflow_dispatch event):
Read your previous comments carefully. Understand what you asked for before.
Check if new commits or comments address your previous feedback.
## Check contributing guidelines
Flag (but don't block) if:
- Missing changeset - For user-facing changes, check if there's a `.changeset/` file:
```bash
gh pr diff ${{ steps.pr.outputs.number }} --name-only | grep '.changeset/' || echo "No changeset found"
```
If missing, ask them to run `npm run changeset`
- Missing tests - New features should have tests
## Find related issues and PRs
Search thoroughly for context that might help with the review:
```bash
# Find related issues for context
gh issue list --search "<keywords from the PR>" --state all --limit 30
gh issue list --search "<error messages or feature names>" --state all --limit 20
# Find similar PRs for reference
gh pr list --search "<keywords>" --state all --limit 30
```
For each relevant issue or PR you find, read it including comments:
```bash
gh issue view <number> --comments
gh pr view <number> --comments
```
Look for:
- Open issues this PR might fix that weren't linked in the description
- Similar PRs that went through review - what feedback did they get? What patterns did they follow?
- Context from maintainer discussions that could inform your review
## Find subject matter experts
For files changed in this PR, find who knows the code best:
```bash
# Get files changed
gh pr diff ${{ steps.pr.outputs.number }} --name-only
# For each relevant path, find contributors
git log --since="6 months ago" --format="%an" -- <path> | sort | uniq -c | sort -rn | head -5
```
Cross-reference git authors with GitHub usernames. Include an SME table in your response:
| SME | Reason |
|-----|--------|
| @username1 | Authored PR #X which modified this area |
| @username2 | Primary contributor to affected file (15 commits in 6 months) |
| @username3 | Reviewed similar PR #Y with extensive feedback |
## Deep code review
This is the most important part. Don't just look for syntax issues - understand what the PR is trying to achieve and whether the implementation is the right approach.
Step 1: Understand the intent
Read the PR description and understand what the author is trying to accomplish. What problem are they solving? What feature are they adding?
Step 2: Form your own opinion first
Before analyzing their code, think about how YOU would implement this feature or fix. What files would you touch? What patterns would you follow? What edge cases would you handle?
Step 3: Compare approaches
Now look at their implementation. How does it compare to what you would have done?
- Is their approach better in some ways? Note what they did well.
- Is their approach missing something? Be specific about what and why.
- Are there edge cases they haven't considered?
- Does it follow the patterns established in similar parts of the codebase?
Step 4: Look at the bigger picture
- What other files or systems does this change interact with?
- Could this break anything else?
- Is there additional work needed beyond this PR to complete the feature?
- Does this fit well with the overall architecture?
Step 5: Find reference implementations
Look for similar changes in the codebase:
```bash
git log --oneline --all --grep="<relevant keywords>" | head -20
git log --oneline -- <similar files> | head -20
```
If this is adding a new API provider, look at how other providers are implemented.
If this is adding a new feature, look at how similar features were added.
Note where their implementation aligns with or diverges from established patterns.
Step 6: Standard code review checks
- DRY: Is there duplicated code that could be extracted?
- Error handling: Are errors handled appropriately?
- Security: Any injection risks, credential exposure, unsafe dependencies?
- Performance: Any obvious inefficiencies, memory leaks, N+1 patterns?
- Types: Is TypeScript used correctly? Any unsafe type assertions?
- Naming: Are variables and functions named clearly?
- Comments: Is complex logic explained? Are there outdated comments?
## Inline code suggestions
For specific code improvements, use GitHub's suggestion syntax via `gh api`.
This creates suggestions the author can commit with one click.
Single-line suggestion:
```bash
gh api repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.number }}/reviews \
-X POST \
-f commit_id="$(gh pr view ${{ steps.pr.outputs.number }} --json headRefOid -q .headRefOid)" \
-f event="COMMENT" \
-f body="" \
-F comments='[
{
"path": "src/example.ts",
"line": 42,
"body": "Consider simplifying:\n\n```suggestion\nconst result = items.filter(Boolean);\n```"
}
]'
```
Multi-line suggestion (replacing lines 40-45):
```bash
gh api repos/${{ github.repository }}/pulls/${{ steps.pr.outputs.number }}/reviews \
-X POST \
-f commit_id="$(gh pr view ${{ steps.pr.outputs.number }} --json headRefOid -q .headRefOid)" \
-f event="COMMENT" \
-f body="" \
-F comments='[
{
"path": "src/example.ts",
"start_line": 40,
"line": 45,
"body": "This can be simplified:\n\n```suggestion\nconst simplified = doThing();\n```"
}
]'
```
Use inline suggestions for concrete improvements. Use regular comments for questions or broader feedback.
## Post your review
After your investigation, post a single helpful comment that helps the author and gives maintainers context.
Start with a warm thank you for their contribution. Be conversational, not robotic.
Include what's relevant:
- In-depth explanation of what the PR does - Be comprehensive. A maintainer should be able to read this section and fully understand the author's intent, why they made the changes, how they implemented it, and what files/systems are affected. Don't just summarize - explain.
- Related issues/PRs you found that provide useful context (link to them)
- Your review findings (issues to address, suggestions, etc.)
- Clear next steps for the author
Include a "For Maintainers" section with:
- Anything else useful to help the maintainer resolve this PR
- Related issues/PRs with context on why they're relevant
- Open issues this PR might fix that weren't linked in the description
- Your recommendation: merge as-is, needs changes, needs discussion, close, etc.
- SME table - who should review this and why
For the SME table:
| SME | Reason |
|-----|--------|
| @username | Primary contributor to affected files |
## Update labels
Add appropriate labels based on your analysis:
```bash
gh label list --json name,description --limit 100
gh pr edit ${{ steps.pr.outputs.number }} --add-label "label1,label2"
```
When done, add the reviewed label:
```bash
gh pr edit ${{ steps.pr.outputs.number }} --add-label "Bot Reviewed"
```
## Remember
- This is a one-time automated response - you will NOT see their reply or respond again. Never say things like "let me know if you have questions", "I can help you with", or "feel free to ask" - you won't be there to follow up. Frame any questions for the maintainers who will follow up.
- Be helpful and welcoming - Many contributors are new to the project
- Be specific - Point to exact lines and suggest fixes, don't give vague feedback
- Think deeply - Don't just surface-level review, understand the intent and evaluate the approach
- Use inline suggestions - Make it easy for authors to accept changes
- You're a first-pass reviewer - A human maintainer will do final approval
+10 -21
View File
@@ -36,8 +36,6 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.tag }}
fetch-depth: 0
fetch-tags: true
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -118,31 +116,22 @@ jobs:
echo "Successfully published release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
fi
- name: Get Previous Tag
id: prev_tag
run: |
CURRENT_TAG="${{ steps.validate_tag.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
- name: Get Changelog Entry
id: changelog
run: |
# Get content between first ## [ and second ## [
CONTENT=$(awk '/^## \[/{if(found) exit; found=1; next} found{print}' CHANGELOG.md)
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# - name: Get Changelog Entry
# id: changelog
# uses: mindsers/changelog-reader-action@v2
# with:
# # This expects a standard Keep a Changelog format
# # "latest" means it will read whichever is the most recent version
# # set in "## [1.2.3] - 2025-01-28" style
# version: latest
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.validate_tag.outputs.tag }}
files: "*.vsix"
body: |
${{ steps.changelog.outputs.content }}
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.validate_tag.outputs.tag }}
# body: ${{ steps.changelog.outputs.content }}
generate_release_notes: true
prerelease: ${{ github.event.inputs.release-type == 'pre-release' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-4
View File
@@ -8,14 +8,12 @@ tmp
.DS_Store
.idea
.husky/_/
pnpm-lock.yaml
.clineignore
.venv
.actrc
CLAUDE.local.md
webview-ui/src/**/*.js
webview-ui/src/**/*.js.map
@@ -30,8 +28,6 @@ coverage-unit
*evals.env
.env
.worktrees
## Generated files ##
src/generated/
src/shared/proto/
-1
View File
@@ -1 +0,0 @@
.gitignore
-32
View File
@@ -1,37 +1,5 @@
# Changelog
## [3.46.1]
### Fixed
- Remove GLM 4.6 from free models
## [3.46.0]
### Added
- Added GLM 4.7 model
- Enhanced background terminal execution with command tracking, log file output, zombie process prevention (10-minute timeout), and clickable log paths in UI
- Apply Patch tool for GPT-5+ models (replacing current diff edit tools)
### Fixed
- Duplicate error messages during streaming for Diff Edit tool when Parallel Tool Calling is not enabled
- Banner carousel styling and dismiss functionality
- Typos in Gemini system prompt overrides
- Model picker favorites ordering, star toggle, and keyboard navigation for OpenRouter and Vercel AI Gateway providers
- Fetch remote config values from the cache
### Refactored
- Anthropic handler to use metadata for reasoning support
- Bedrock provider to use metadata for reasoning support
## [3.45.1]
- Fixed MCP settings race condition where toggling auto-approve or changing timeout settings would cause the UI to flash and revert
## [3.45.0]
- Added Gemini 3 Flash Preview model
-4
View File
@@ -14,10 +14,6 @@ This file is the secret sauce for working effectively in this codebase. It captu
**What NOT to add:** Stuff you can figure out from reading a few files, obvious patterns, or standard practices. This file should be high-signal, not comprehensive.
## Miscellaneous
- This is a VS Code extension—check `package.json` for available scripts before trying to verify builds (e.g., `npm run compile`, not `npm run build`).
- When creating PRs, if the change is user-facing and significant enough to warrant a changelog entry, run `npm run changeset` and create a patch changeset. Never create minor or major version bumps. Skip changesets for trivial fixes, internal refactors, or minor UI tweaks that users wouldn't notice.
## gRPC/Protobuf Communication
The extension and webview communicate via gRPC-like protocol over VS Code message passing.
+3 -5
View File
@@ -14,9 +14,8 @@ import (
)
var (
port int
verbose bool
workspaces []string
port int
verbose bool
)
func main() {
@@ -29,7 +28,6 @@ func main() {
rootCmd.Flags().IntVarP(&port, "port", "p", 51052, "port to listen on")
rootCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "verbose logging")
rootCmd.Flags().StringSliceVar(&workspaces, "workspace", nil, "workspace paths")
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -41,7 +39,7 @@ func runServer(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
// Create gRPC hostbridge service
service := hostbridge.NewGrpcServer(port, verbose, workspaces)
service := hostbridge.NewGrpcServer(port, verbose)
// Handle graceful shutdown
ctx, cancel := context.WithCancel(ctx)
+24 -69
View File
@@ -6,7 +6,6 @@ import (
"fmt"
"io"
"os"
"slices"
"strings"
"github.com/charmbracelet/huh"
@@ -26,13 +25,12 @@ var (
outputFormat string
// Task creation flags (for root command)
images []string
files []string
mode string
settings []string
yolo bool
oneshot bool
workspaces []string
images []string
files []string
mode string
settings []string
yolo bool
oneshot bool
)
func main() {
@@ -72,23 +70,12 @@ see the manual page: man cline`,
var instanceAddress string
// Validate workspace paths exist
if err := common.ValidateDirsExist(workspaces); err != nil {
return err
}
// Build the full workspace list: cwd first, then additional workspaces
allWorkspaces, err := buildWorkspaceList(workspaces)
if err != nil {
return fmt.Errorf("failed to build workspace list: %w", err)
}
// If --address flag not provided, start instance BEFORE getting prompt
if !cmd.Flags().Changed("address") {
if global.Config.Verbose {
fmt.Println("Starting new Cline instance...")
}
instance, err := global.Clients.StartNewInstance(ctx, allWorkspaces...)
instance, err := global.Clients.StartNewInstance(ctx)
if err != nil {
return fmt.Errorf("failed to start new instance: %w", err)
}
@@ -144,8 +131,8 @@ see the manual page: man cline`,
// If no prompt from args or stdin, show interactive input
if prompt == "" {
// Pass the mode flag and workspaces to banner so it shows correct info
prompt, err = promptForInitialTask(ctx, instanceAddress, mode, allWorkspaces)
// Pass the mode flag to banner so it shows correct mode
prompt, err = promptForInitialTask(ctx, instanceAddress, mode)
if err != nil {
// Check if user cancelled - exit cleanly without error
if err == huh.ErrUserAborted {
@@ -165,14 +152,13 @@ see the manual page: man cline`,
}
return cli.CreateAndFollowTask(ctx, prompt, cli.TaskOptions{
Images: images,
Files: files,
Mode: mode,
Settings: settings,
Yolo: yolo,
Address: instanceAddress,
Verbose: verbose,
Workspaces: allWorkspaces,
Images: images,
Files: files,
Mode: mode,
Settings: settings,
Yolo: yolo,
Address: instanceAddress,
Verbose: verbose,
})
},
}
@@ -189,7 +175,6 @@ see the manual page: man cline`,
rootCmd.Flags().BoolVarP(&yolo, "yolo", "y", false, "enable yolo mode (non-interactive)")
rootCmd.Flags().BoolVar(&yolo, "no-interactive", false, "enable yolo mode (non-interactive)")
rootCmd.Flags().BoolVarP(&oneshot, "oneshot", "o", false, "full autonomous mode")
rootCmd.Flags().StringSliceVarP(&workspaces, "workspace", "w", nil, "additional workspace paths (can be specified multiple times)")
rootCmd.AddCommand(cli.NewTaskCommand())
rootCmd.AddCommand(cli.NewInstanceCommand())
@@ -204,9 +189,9 @@ see the manual page: man cline`,
}
}
func promptForInitialTask(ctx context.Context, instanceAddress, modeFlag string, workspaces []string) (string, error) {
func promptForInitialTask(ctx context.Context, instanceAddress, modeFlag string) (string, error) {
// Show session banner before the initial input
showSessionBanner(ctx, instanceAddress, modeFlag, workspaces)
showSessionBanner(ctx, instanceAddress, modeFlag)
var prompt string
@@ -248,7 +233,7 @@ func promptForInitialTask(ctx context.Context, instanceAddress, modeFlag string,
}
// showSessionBanner displays session info before initial prompt
func showSessionBanner(ctx context.Context, instanceAddress, modeFlag string, workspaces []string) {
func showSessionBanner(ctx context.Context, instanceAddress, modeFlag string) {
bannerInfo := display.BannerInfo{
Version: global.CliVersion,
Mode: modeFlag, // Use the mode from command flag, not state
@@ -259,7 +244,10 @@ func showSessionBanner(ctx context.Context, instanceAddress, modeFlag string, wo
bannerInfo.Mode = "plan"
}
bannerInfo.Workdirs = workspaces
// Get current working directory (this is what Cline will use)
if cwd, err := os.Getwd(); err == nil {
bannerInfo.Workdir = cwd
}
// Get provider/model using auth functions (same logic as auth menu)
manager, err := cli.NewTaskManagerForAddress(ctx, instanceAddress)
@@ -357,37 +345,4 @@ func getContentFromStdinAndArgs(args []string) (string, error) {
}
return content.String(), nil
}
// buildWorkspaceList builds the full workspace list with cwd as the first entry
func buildWorkspaceList(additionalWorkspaces []string) ([]string, error) {
cwd, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("failed to get current working directory: %w", err)
}
// Start with cwd
workspaces := []string{cwd}
// Add additional workspaces, avoiding duplicates
for _, ws := range additionalWorkspaces {
// Normalize the path
absPath, err := common.AbsPath(ws)
if err != nil {
return nil, fmt.Errorf("failed to resolve workspace path %s: %w", ws, err)
}
// Skip if it's the same as cwd
if absPath == cwd {
continue
}
// Check for duplicates
isDuplicate := slices.Contains(workspaces, absPath)
if !isDuplicate {
workspaces = append(workspaces, absPath)
}
}
return workspaces, nil
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
module github.com/cline/cli
go 1.24.0
go 1.23.0
require (
github.com/atotto/clipboard v0.1.4
-4
View File
@@ -70,10 +70,6 @@ When using the instant task syntax **cline "prompt"** the following options are
: Starting mode. Options: **act** (default), **plan**
**-w**, **\--workspace** *path*
: Additional workspace paths. Can be specified multiple times to include multiple directories. The current working directory is always included as the first workspace. Example: cline -w /path/to/other/project "refactor shared code"
# GLOBAL OPTIONS
These options apply to all subcommands:
+38 -44
View File
@@ -47,7 +47,7 @@ func GetProviderConfigurations(ctx context.Context, manager *task.Manager) (*Pro
}
// Parse state_json as map[string]interface{}
var stateData map[string]any
var stateData map[string]interface{}
if err := json.Unmarshal([]byte(stateJSON), &stateData); err != nil {
return nil, fmt.Errorf("failed to parse state JSON: %w", err)
}
@@ -57,7 +57,7 @@ func GetProviderConfigurations(ctx context.Context, manager *task.Manager) (*Pro
}
// Extract apiConfiguration object from state
apiConfig, ok := stateData["apiConfiguration"].(map[string]any)
apiConfig, ok := stateData["apiConfiguration"].(map[string]interface{})
if !ok {
if global.Config.Verbose {
fmt.Println("[DEBUG] No apiConfiguration found in state")
@@ -128,11 +128,11 @@ func (r *ProviderListResult) GetAllReadyProviders() []*ProviderDisplay {
modelID := getProviderSpecificModelID(r.apiConfig, "plan", provider)
// Determine if credentials exist
hasCreds := checkCredentialsExists(r.apiConfig, provider)
hasCreds := checkAPIKeyExists(r.apiConfig, provider)
// Determine readiness: OCA uses auth state presence; others need creds and model
if provider == cline.ApiProvider_OCA {
state, _ := GetLatestOCAState(context.Background(), 2*time.Second)
state, _ := GetLatestOCAState(context.Background(), 2 *time.Second)
if state == nil || state.User == nil {
continue
}
@@ -156,7 +156,7 @@ func (r *ProviderListResult) GetAllReadyProviders() []*ProviderDisplay {
Mode: "Ready",
Provider: provider,
ModelID: modelID,
HasAPIKey: checkCredentialsExists(r.apiConfig, provider),
HasAPIKey: checkAPIKeyExists(r.apiConfig, provider),
BaseURL: baseURL,
})
seenProviders[provider] = true
@@ -192,7 +192,7 @@ func extractProviderFromState(stateData map[string]interface{}, mode string) *Pr
modelID := getProviderSpecificModelID(stateData, mode, provider)
// Check if API key exists
hasCredentials := checkCredentialsExists(stateData, provider)
hasAPIKey := checkAPIKeyExists(stateData, provider)
// Get base URL for Ollama (can be shown publicly)
baseURL := ""
@@ -206,7 +206,7 @@ func extractProviderFromState(stateData map[string]interface{}, mode string) *Pr
Mode: capitalizeMode(mode),
Provider: provider,
ModelID: modelID,
HasAPIKey: hasCredentials,
HasAPIKey: hasAPIKey,
BaseURL: baseURL,
}
}
@@ -215,7 +215,7 @@ func extractProviderFromState(stateData map[string]interface{}, mode string) *Pr
// Returns (provider, ok) where ok is false if the provider is unknown
func mapProviderStringToEnum(providerStr string) (cline.ApiProvider, bool) {
normalizedStr := strings.ToLower(providerStr)
// Map string values to enum values
switch normalizedStr {
case "anthropic":
@@ -303,23 +303,19 @@ func getProviderSpecificModelID(stateData map[string]interface{}, mode string, p
return modelID
}
// checkCredentialsExists checks if API key field exists in state (never retrieve actual key)
func checkCredentialsExists(stateData map[string]interface{}, provider cline.ApiProvider) bool {
// checkAPIKeyExists checks if API key field exists in state (never retrieve actual key)
func checkAPIKeyExists(stateData map[string]interface{}, provider cline.ApiProvider) bool {
// Get field mapping from centralized function
fields, err := GetProviderFields(provider)
if err != nil {
return false
}
// Check if the key exists and is not empty
if value, ok := stateData[fields.APIKeyField]; ok {
if str, ok := value.(string); ok && str != "" {
return true
}
}
keyField := fields.APIKeyField
if value, ok := stateData[fields.UseProfileField]; ok {
if hasProfileField, ok := value.(bool); ok && hasProfileField {
// Check if the key exists and is not empty
if value, ok := stateData[keyField]; ok {
if str, ok := value.(string); ok && str != "" {
return true
}
}
@@ -442,13 +438,13 @@ func DetectAllConfiguredProviders(ctx context.Context, manager *task.Manager) ([
stateJSON := state.StateJson
// Parse state_json as map[string]interface{}
var stateData map[string]any
var stateData map[string]interface{}
if err := json.Unmarshal([]byte(stateJSON), &stateData); err != nil {
return nil, fmt.Errorf("failed to parse state JSON: %w", err)
}
// Extract apiConfiguration object from state
apiConfig, ok := stateData["apiConfiguration"].(map[string]any)
apiConfig, ok := stateData["apiConfiguration"].(map[string]interface{})
if !ok {
verboseLog("[DEBUG] No apiConfiguration found in state")
verboseLog("[DEBUG] Available keys in stateData: %v", getMapKeys(stateData))
@@ -473,38 +469,36 @@ func DetectAllConfiguredProviders(ctx context.Context, manager *task.Manager) ([
// Check each BYO provider for API key presence
providersToCheck := []struct {
provider cline.ApiProvider
keyFields []string
provider cline.ApiProvider
keyField string
}{
{cline.ApiProvider_ANTHROPIC, []string{"apiKey"}},
{cline.ApiProvider_OPENAI, []string{"openAiApiKey"}},
{cline.ApiProvider_OPENAI_NATIVE, []string{"openAiNativeApiKey"}},
{cline.ApiProvider_OPENROUTER, []string{"openRouterApiKey"}},
{cline.ApiProvider_XAI, []string{"xaiApiKey"}},
{cline.ApiProvider_BEDROCK, []string{"awsAccessKey", "awsUseProfile"}},
{cline.ApiProvider_GEMINI, []string{"geminiApiKey"}},
{cline.ApiProvider_OLLAMA, []string{"ollamaBaseUrl"}}, // Ollama uses baseUrl instead of API key
{cline.ApiProvider_CEREBRAS, []string{"cerebrasApiKey"}},
{cline.ApiProvider_HICAP, []string{"hicapApiKey"}},
{cline.ApiProvider_NOUSRESEARCH, []string{"nousResearchApiKey"}},
{cline.ApiProvider_ANTHROPIC, "apiKey"},
{cline.ApiProvider_OPENAI, "openAiApiKey"},
{cline.ApiProvider_OPENAI_NATIVE, "openAiNativeApiKey"},
{cline.ApiProvider_OPENROUTER, "openRouterApiKey"},
{cline.ApiProvider_XAI, "xaiApiKey"},
{cline.ApiProvider_BEDROCK, "awsAccessKey"},
{cline.ApiProvider_GEMINI, "geminiApiKey"},
{cline.ApiProvider_OLLAMA, "ollamaBaseUrl"}, // Ollama uses baseUrl instead of API key
{cline.ApiProvider_CEREBRAS, "cerebrasApiKey"},
{cline.ApiProvider_HICAP, "hicapApiKey"},
{cline.ApiProvider_NOUSRESEARCH, "nousResearchApiKey"},
}
for _, providerCheck := range providersToCheck {
verboseLog("[DEBUG] Checking for %s key: %s", GetProviderDisplayName(providerCheck.provider), providerCheck.keyFields)
for _, keyField := range providerCheck.keyFields {
if value, ok := apiConfig[keyField]; ok {
verboseLog("[DEBUG] Found key, value type: %T, is empty: %v", value, value == "")
if str, ok := value.(string); ok && str != "" {
configuredProviders = append(configuredProviders, providerCheck.provider)
verboseLog("[DEBUG] ✓ Provider %s is configured", GetProviderDisplayName(providerCheck.provider))
break
}
} else {
verboseLog("[DEBUG] Key %s not found", keyField)
verboseLog("[DEBUG] Checking for %s key: %s", GetProviderDisplayName(providerCheck.provider), providerCheck.keyField)
if value, ok := apiConfig[providerCheck.keyField]; ok {
verboseLog("[DEBUG] Found key, value type: %T, is empty: %v", value, value == "")
if str, ok := value.(string); ok && str != "" {
configuredProviders = append(configuredProviders, providerCheck.provider)
verboseLog("[DEBUG] ✓ Provider %s is configured", GetProviderDisplayName(providerCheck.provider))
}
} else {
verboseLog("[DEBUG] Key %s not found", providerCheck.keyField)
}
}
verboseLog("[DEBUG] Total configured providers: %d", len(configuredProviders))
for _, p := range configuredProviders {
verboseLog("[DEBUG] - %s", GetProviderDisplayName(p))
@@ -54,7 +54,6 @@ type ProviderFields struct {
// Provider-specific additional model ID fields
PlanModeProviderSpecificModelIDField string // e.g., "planModeOpenRouterModelId"
ActModeProviderSpecificModelIDField string // e.g., "actModeOpenRouterModelId"
UseProfileField string // e.g., "awsUseProfile" (for bedrock) (optional, empty if not applicable)
}
// GetProviderFields returns the field mapping for a given provider
@@ -97,7 +96,6 @@ func GetProviderFields(provider cline.ApiProvider) (ProviderFields, error) {
case cline.ApiProvider_BEDROCK:
return ProviderFields{
UseProfileField: "awsUseProfile",
APIKeyField: "awsAccessKey",
PlanModeModelIDField: "planModeApiModelId",
ActModeModelIDField: "actModeApiModelId",
+15 -22
View File
@@ -15,23 +15,23 @@ import (
// BedrockConfig holds all AWS Bedrock-specific configuration fields
type BedrockConfig struct {
// Profile authentication fields
UseProfile bool // Always true for successful config
Profile string // Optional: AWS profile name (empty = default)
Region string // Required: AWS region
Endpoint string // Optional: Custom VPC endpoint URL
UseProfile bool // Always true for successful config
Profile string // Optional: AWS profile name (empty = default)
Region string // Required: AWS region
Endpoint string // Optional: Custom VPC endpoint URL
// Optional features
UseCrossRegionInference bool // Optional: Enable cross-region inference
UseGlobalInference bool // Optional: Use global inference endpoint
UsePromptCache bool // Optional: Enable prompt caching
UseCrossRegionInference bool // Optional: Enable cross-region inference
UseGlobalInference bool // Optional: Use global inference endpoint
UsePromptCache bool // Optional: Enable prompt caching
// Authentication method (always "profile")
Authentication string // Always set to "profile"
Authentication string // Always set to "profile"
// Legacy fields (no longer used in profile-only flow)
AccessKey string // No longer used
SecretKey string // No longer used
SessionToken string // No longer used
AccessKey string // No longer used
SecretKey string // No longer used
SessionToken string // No longer used
}
// PromptForBedrockConfig displays a profile-first authentication form for Bedrock configuration
@@ -130,12 +130,7 @@ func ApplyBedrockConfig(ctx context.Context, manager *task.Manager, config *Bedr
// Build the API configuration with all Bedrock fields
apiConfig := &cline.ModelsApiConfiguration{}
// Set provider for both Plan and Act modes
bedrockProvider := cline.ApiProvider_BEDROCK
apiConfig.PlanModeApiProvider = &bedrockProvider
apiConfig.ActModeApiProvider = &bedrockProvider
// Set model ID field - this is the primary model ID used by Cline Core
// Set model ID fields
apiConfig.PlanModeApiModelId = proto.String(modelID)
apiConfig.ActModeApiModelId = proto.String(modelID)
apiConfig.PlanModeAwsBedrockCustomModelBaseId = proto.String(modelID)
@@ -171,8 +166,6 @@ func ApplyBedrockConfig(ctx context.Context, manager *task.Manager, config *Bedr
// Build field mask including all fields we're setting (excluding access keys)
fieldPaths := []string{
"planModeApiProvider",
"actModeApiProvider",
"planModeApiModelId",
"actModeApiModelId",
"planModeAwsBedrockCustomModelBaseId",
+130 -18
View File
@@ -1,19 +1,22 @@
package display
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/cline/cli/pkg/common"
)
// BannerInfo contains information to display in the session banner
type BannerInfo struct {
Version string
Provider string
ModelID string
Workdirs []string // workspace directories
Mode string
Version string
Provider string
ModelID string
Workdir string
Mode string
}
// RenderSessionBanner renders a nice banner showing version, model, and workspace info
@@ -78,22 +81,131 @@ func RenderSessionBanner(info BannerInfo) string {
// Model line - dim gray
if info.Provider != "" && info.ModelID != "" {
lines = append(lines, dimStyle.Render(info.Provider+"/"+common.ShortenPath(info.ModelID, 30)))
lines = append(lines, dimStyle.Render(info.Provider+"/"+shortenPath(info.ModelID, 30)))
}
for _, wd := range info.Workdirs {
lines = append(lines, dimStyle.Render(common.ShortenPath(wd, 45)))
}
// Checkpoint warning for multi-root workspaces
if len(info.Workdirs) > 1 {
warningStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("3")). // Yellow warning color
Italic(true)
lines = append(lines, "")
lines = append(lines, warningStyle.Render("⚠ Checkpoints disabled for multi-root workspaces"))
// Workspace line - dim gray
if info.Workdir != "" {
lines = append(lines, dimStyle.Render(shortenPath(info.Workdir, 45)))
}
content := lipgloss.JoinVertical(lipgloss.Left, lines...)
return boxStyle.Render(content)
}
// shortenPath shortens a filesystem path to fit within maxLen
func shortenPath(path string, maxLen int) string {
// Try to replace home directory with ~ (cross-platform)
if homeDir, err := os.UserHomeDir(); err == nil {
if strings.HasPrefix(path, homeDir) {
shortened := "~" + path[len(homeDir):]
// Always use ~ version if we can
path = shortened
}
}
if len(path) <= maxLen {
return path
}
// If still too long, show last few path components
if len(path) > maxLen {
parts := strings.Split(path, string(filepath.Separator))
if len(parts) > 2 {
// Show last 2-3 components
lastParts := parts[len(parts)-2:]
shortened := "..." + string(filepath.Separator) + strings.Join(lastParts, string(filepath.Separator))
if len(shortened) <= maxLen {
return shortened
}
}
}
// Last resort: truncate with ellipsis
if len(path) > maxLen {
return "..." + path[len(path)-maxLen+3:]
}
return path
}
// ExtractBannerInfoFromState extracts banner info from state JSON
func ExtractBannerInfoFromState(stateJSON, version string) (BannerInfo, error) {
var state map[string]interface{}
if err := json.Unmarshal([]byte(stateJSON), &state); err != nil {
return BannerInfo{}, fmt.Errorf("failed to parse state JSON: %w", err)
}
info := BannerInfo{
Version: version,
}
// Extract mode
if mode, ok := state["mode"].(string); ok {
info.Mode = mode
}
// Extract workspace roots
if workspaceRoots, ok := state["workspaceRoots"].([]interface{}); ok && len(workspaceRoots) > 0 {
if root, ok := workspaceRoots[0].(map[string]interface{}); ok {
if path, ok := root["path"].(string); ok {
info.Workdir = path
}
}
}
// Extract API configuration to get provider/model
if apiConfig, ok := state["apiConfiguration"].(map[string]interface{}); ok {
// Try common keys for provider and model (both camelCase and lowercase variants)
providerKeys := []string{"apiProvider", "api_provider"}
modelKeys := []string{"apiModelId", "api_model_id"}
// Try to extract provider
for _, key := range providerKeys {
if provider, ok := apiConfig[key].(string); ok && provider != "" {
info.Provider = provider
break
}
}
// Try to extract model ID
for _, key := range modelKeys {
if modelID, ok := apiConfig[key].(string); ok && modelID != "" {
info.ModelID = shortenModelID(modelID)
break
}
}
}
return info, nil
}
// shortenModelID shortens long model IDs for display
func shortenModelID(modelID string) string {
// Remove date suffixes only if they're at the end (e.g., -20241022)
// Check if the model ID ends with -YYYYMMDD pattern
if len(modelID) > 9 {
suffix := modelID[len(modelID)-9:] // Last 9 chars: -20241022
if suffix[0] == '-' &&
(strings.HasPrefix(suffix[1:], "202") || strings.HasPrefix(suffix[1:], "201")) {
// Verify all remaining chars are digits
allDigits := true
for _, c := range suffix[1:] {
if c < '0' || c > '9' {
allDigits = false
break
}
}
if allDigits {
return modelID[:len(modelID)-9]
}
}
}
// If still too long, show first 40 chars
if len(modelID) > 40 {
return modelID[:37] + "..."
}
return modelID
}
+14 -22
View File
@@ -36,7 +36,7 @@ func (c *ClineClients) Initialize(ctx context.Context) error {
}
// StartNewInstance starts a new Cline instance and waits for cline-core to self-register
func (c *ClineClients) StartNewInstance(ctx context.Context, workspaces ...string) (*common.CoreInstanceInfo, error) {
func (c *ClineClients) StartNewInstance(ctx context.Context) (*common.CoreInstanceInfo, error) {
// Find available ports
corePort, hostPort, err := common.FindAvailablePortPair()
if err != nil {
@@ -48,7 +48,7 @@ func (c *ClineClients) StartNewInstance(ctx context.Context, workspaces ...strin
}
// Start cline-host first
hostCmd, err := startClineHost(hostPort, workspaces)
hostCmd, err := startClineHost(hostPort, corePort)
if err != nil {
return nil, fmt.Errorf("failed to start cline-host: %w", err)
}
@@ -120,7 +120,7 @@ func (c *ClineClients) StartNewInstance(ctx context.Context, workspaces ...strin
}
// StartNewInstanceAtPort starts a new Cline instance at the specified port and waits for self-registration
func (c *ClineClients) StartNewInstanceAtPort(ctx context.Context, corePort int, workspaces ...string) (*common.CoreInstanceInfo, error) {
func (c *ClineClients) StartNewInstanceAtPort(ctx context.Context, corePort int) (*common.CoreInstanceInfo, error) {
// Find available host port (core port + 1000)
hostPort := corePort + 1000
coreAddress := fmt.Sprintf("localhost:%d", corePort)
@@ -135,7 +135,7 @@ func (c *ClineClients) StartNewInstanceAtPort(ctx context.Context, corePort int,
}
// Start cline-host first
hostCmd, err := startClineHost(hostPort, workspaces)
hostCmd, err := startClineHost(hostPort, corePort)
if err != nil {
return nil, fmt.Errorf("failed to start cline-host: %w", err)
}
@@ -242,7 +242,7 @@ func (c *ClineClients) EnsureInstanceAtAddress(ctx context.Context, address stri
return fmt.Errorf("cannot start remote instance at %s", normalized)
}
func startClineHost(hostPort int, workspaces []string) (*exec.Cmd, error) {
func startClineHost(hostPort, corePort int) (*exec.Cmd, error) {
if Config.Verbose {
fmt.Printf("Starting cline-host on port %d\n", hostPort)
}
@@ -255,18 +255,10 @@ func startClineHost(hostPort int, workspaces []string) (*exec.Cmd, error) {
binDir := path.Dir(execPath)
clineHostPath := path.Join(binDir, "cline-host")
// Build command arguments
args := []string{
"--verbose",
"--port", fmt.Sprintf("%d", hostPort),
}
for _, ws := range workspaces {
args = append(args, "--workspace", ws)
}
// Start the cline-host process
cmd := exec.Command(clineHostPath, args...)
cmd := exec.Command(clineHostPath,
"--verbose",
"--port", fmt.Sprintf("%d", hostPort))
// Create logs directory in ~/.cline/logs
logsDir := path.Join(Config.ConfigPath, "logs")
@@ -341,7 +333,7 @@ func KillInstanceByAddress(ctx context.Context, registry *ClientRegistry, addres
if Config.Verbose {
fmt.Printf("Waiting for instance to clean up registry entry...\n")
}
for range 5 {
for i := 0; i < 5; i++ {
time.Sleep(1 * time.Second)
if !registry.HasInstanceAtAddress(address) {
if Config.Verbose {
@@ -416,15 +408,15 @@ func startClineCore(corePort, hostPort int) (*exec.Cmd, error) {
// This handles the case where we're running from cli/bin/cline
devClineCorePath := path.Join(binDir, "..", "..", "dist-standalone", "cline-core.js")
devInstallDir := path.Join(binDir, "..", "..", "dist-standalone")
if Config.Verbose {
fmt.Printf("Primary location not found, trying development path: %s\n", devClineCorePath)
}
if _, err := os.Stat(devClineCorePath); os.IsNotExist(err) {
return nil, fmt.Errorf("cline-core.js not found at '%s' or '%s'. Please ensure you're running from the correct location or reinstall with 'npm install -g cline'", clineCorePath, devClineCorePath)
}
finalClineCorePath = devClineCorePath
finalInstallDir = devInstallDir
if Config.Verbose {
@@ -483,7 +475,7 @@ func startClineCore(corePort, hostPort int) (*exec.Cmd, error) {
realNodeModules := path.Join(finalInstallDir, "node_modules")
fakeNodeModules := path.Join(finalInstallDir, "fake_node_modules")
nodePath := fmt.Sprintf("%s%c%s", realNodeModules, os.PathListSeparator, fakeNodeModules)
env = append(env,
fmt.Sprintf("NODE_PATH=%s", nodePath),
// These control gRPC debug logging
@@ -492,7 +484,7 @@ func startClineCore(corePort, hostPort int) (*exec.Cmd, error) {
"NODE_ENV=development",
)
cmd.Env = env
if Config.Verbose {
fmt.Printf("NODE_PATH set to: %s\n", nodePath)
}
+7 -8
View File
@@ -20,14 +20,13 @@ import (
// TaskOptions contains options for creating a task
type TaskOptions struct {
Images []string
Files []string
Mode string
Settings []string
Yolo bool
Address string
Verbose bool
Workspaces []string
Images []string
Files []string
Mode string
Settings []string
Yolo bool
Address string
Verbose bool
}
func NewTaskCommand() *cobra.Command {
+11 -12
View File
@@ -3,16 +3,15 @@ package types
// HistoryItem represents a task history item from taskHistory.json
// This struct matches the JSON format stored on disk
type HistoryItem struct {
Id string `json:"id"`
Ulid string `json:"ulid,omitempty"`
Ts int64 `json:"ts"`
Task string `json:"task"`
TokensIn int32 `json:"tokensIn"`
TokensOut int32 `json:"tokensOut"`
CacheWrites int32 `json:"cacheWrites,omitempty"`
CacheReads int32 `json:"cacheReads,omitempty"`
TotalCost float64 `json:"totalCost"`
Size int64 `json:"size,omitempty"`
IsFavorited bool `json:"isFavorited,omitempty"`
WorkspacePaths []string `json:"workspacePaths,omitempty"`
Id string `json:"id"`
Ulid string `json:"ulid,omitempty"`
Ts int64 `json:"ts"`
Task string `json:"task"`
TokensIn int32 `json:"tokensIn"`
TokensOut int32 `json:"tokensOut"`
CacheWrites int32 `json:"cacheWrites,omitempty"`
CacheReads int32 `json:"cacheReads,omitempty"`
TotalCost float64 `json:"totalCost"`
Size int64 `json:"size,omitempty"`
IsFavorited bool `json:"isFavorited,omitempty"`
}
-71
View File
@@ -4,9 +4,7 @@ import (
"context"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
@@ -185,72 +183,3 @@ DEBUGGING STEPS:
For additional help, visit: https://github.com/cline/cline/issues
`, maxRetries, lastErr, GetNodeVersion())
}
// validateDirsExist validates that all workspace paths exist on the filesystem
func ValidateDirsExist(paths []string) error {
for _, p := range paths {
info, err := os.Stat(p)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("path does not exist: %s", p)
}
return fmt.Errorf("failed to access path %s: %w", p, err)
}
if !info.IsDir() {
return fmt.Errorf("path is not a directory: %s", p)
}
}
return nil
}
// absPath returns the absolute path, resolving symlinks
func AbsPath(path string) (string, error) {
// First get absolute path
abs, err := filepath.Abs(path)
if err != nil {
return "", err
}
// Then resolve any symlinks
resolved, err := filepath.EvalSymlinks(abs)
if err != nil {
// If symlink resolution fails, return the absolute path
return abs, nil
}
return resolved, nil
}
// shortenPath shortens a filesystem path to fit within maxLen
func ShortenPath(path string, maxLen int) string {
// Try to replace home directory with ~ (cross-platform)
if homeDir, err := os.UserHomeDir(); err == nil {
if strings.HasPrefix(path, homeDir) {
shortened := "~" + path[len(homeDir):]
// Always use ~ version if we can
path = shortened
}
}
if len(path) <= maxLen {
return path
}
// If still too long, show last few path components
if len(path) > maxLen {
parts := strings.Split(path, string(filepath.Separator))
if len(parts) > 2 {
// Show last 2-3 components
lastParts := parts[len(parts)-2:]
shortened := "..." + string(filepath.Separator) + strings.Join(lastParts, string(filepath.Separator))
if len(shortened) <= maxLen {
return shortened
}
}
}
// Last resort: truncate with ellipsis
if len(path) > maxLen {
return "..." + path[len(path)-maxLen+3:]
}
return path
}
+2 -4
View File
@@ -16,17 +16,15 @@ import (
type GrpcServer struct {
port int
verbose bool
workspaces []string
server *grpc.Server
shutdownCh chan struct{}
}
// NewGrpcServer creates a new GrpcServer
func NewGrpcServer(port int, verbose bool, workspaces []string) *GrpcServer {
func NewGrpcServer(port int, verbose bool) *GrpcServer {
return &GrpcServer{
port: port,
verbose: verbose,
workspaces: workspaces,
shutdownCh: make(chan struct{}),
}
}
@@ -52,7 +50,7 @@ func (s *GrpcServer) Start(ctx context.Context) error {
grpc_health_v1.RegisterHealthServer(s.server, healthServer)
// Register services
workspaceService := NewSimpleWorkspaceService(s.verbose, s.workspaces)
workspaceService := NewSimpleWorkspaceService(s.verbose)
host.RegisterWorkspaceServiceServer(s.server, workspaceService)
windowService := NewWindowService(s.verbose)
+8 -20
View File
@@ -12,15 +12,13 @@ import (
// SimpleWorkspaceService implements a basic workspace service without complex dependencies
type SimpleWorkspaceService struct {
host.UnimplementedWorkspaceServiceServer
verbose bool
workspaces []string
verbose bool
}
// NewSimpleWorkspaceService creates a new SimpleWorkspaceService
func NewSimpleWorkspaceService(verbose bool, workspaces []string) *SimpleWorkspaceService {
func NewSimpleWorkspaceService(verbose bool) *SimpleWorkspaceService {
return &SimpleWorkspaceService{
verbose: verbose,
workspaces: workspaces,
verbose: verbose,
}
}
@@ -30,24 +28,14 @@ func (s *SimpleWorkspaceService) GetWorkspacePaths(ctx context.Context, req *hos
log.Printf("GetWorkspacePaths called")
}
paths := []string{}
if len(s.workspaces) == 0 {
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
paths = append(paths, cwd)
} else {
paths = s.workspaces
}
if s.verbose {
log.Printf("Returning configured workspaces: %v", paths)
// Get current working directory as the workspace
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
return &host.GetWorkspacePathsResponse{
Paths: paths,
Paths: []string{cwd},
}, nil
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

-6
View File
@@ -95,12 +95,6 @@ INSTANT TASK OPTIONS
-m, --mode mode
Starting mode. Options: act (default), plan
-w, --workspace path
Additional workspace paths. Can be specified multiple times to
include multiple directories. The current working directory is
always included as the first workspace. Example: cline -w
/path/to/other/project "refactor shared code"
GLOBAL OPTIONS
These options apply to all subcommands:
+1 -3
View File
@@ -278,7 +278,6 @@
"pages": [
"enterprise-solutions/overview",
"enterprise-solutions/onboarding",
"enterprise-solutions/sso-setup",
"enterprise-solutions/team-management/managing-members",
{
"group": "SaaS Provider Configuration",
@@ -318,8 +317,7 @@
"pages": [
"enterprise-solutions/monitoring/overview",
"enterprise-solutions/monitoring/telemetry",
"enterprise-solutions/monitoring/opentelemetry",
"enterprise-solutions/monitoring/opentelemetry_override"
"enterprise-solutions/monitoring/opentelemetry"
]
}
]
@@ -31,7 +31,7 @@ Check which models are available in your region first. Some newer models might n
<Frame>
<img
src="https://assets.int.cline.bot/assets/AWS%20Remote%20Config.gif"
src="https://storage.googleapis.com/cline-static-assets-prod/assets/AWS%20Remote%20Config.gif"
/>
</Frame>
@@ -27,7 +27,7 @@ If you don't have AWS credentials yet, reach out to your IT or cloud team to get
<Frame>
<img
src="https://assets.int.cline.bot/assets/VS%20Code%20Bedrock%20API%20Key.gif"
src="https://storage.googleapis.com/cline-static-assets-prod/assets/VS%20Code%20Bedrock%20API%20Key.gif"
/>
</Frame>
@@ -42,45 +42,76 @@ Cline supports three OTLP export protocols:
- **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 [Remote Configuration](/enterprise-solutions/configuration/remote-configuration/overview#how-remote-configuration-works) from the [dashboard](https://app.cline.bot/dashboard/organization?tab=settings).
OpenTelemetry is configured using environment variables before launching Cline.
### Basic Setup
Enable OpenTelemetry, configure an OTLP endpoint and select a protocol:
Enable OpenTelemetry and configure an OTLP endpoint:
<Frame>
<img
src="https://assets.int.cline.bot/assets/open_telemetry_main_options.png"
/>
</Frame>
```bash
# Enable OpenTelemetry
export OTEL_TELEMETRY_ENABLED=1
If you're using gRPC, you can opt out of TLS.
# Configure metrics and logs export
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
Once the collector has been configured, you can enable logs and/or metrics collection. At least one of them needs to be enabled.
# Set your OTLP endpoint
export OTEL_EXPORTER_OTLP_ENDPOINT=https://your-collector:4317
You only need to configure it further if you need an advanced configuration.
# 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
You can add custom protocols and endpoints for both, logs and metrics. You can also configure the metrics export interval, and the logs batch size, batch timeout and max queue size.
<Frame>
<img
src="https://assets.int.cline.bot/assets/open_telemetry_metrics_and_logs.png"
/>
</Frame>
**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"
```
Finally, if your collector needs authentication headers, you can add key value pairs in the headers section.
**Multiple exporters (console + OTLP):**
```bash
export OTEL_METRICS_EXPORTER=console,otlp
export OTEL_LOGS_EXPORTER=console,otlp
```
<Frame>
<img
src="https://assets.int.cline.bot/assets/open_telemetry_headers.png"
/>
</Frame>
**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
@@ -88,46 +119,73 @@ Finally, if your collector needs authentication headers, you can add key value p
Export to Datadog using their OTLP endpoint:
<Frame>
<img
src="https://assets.int.cline.bot/assets/open_telemetry_datadog_example.png"
/>
</Frame>
```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:
<Frame>
<img
src="https://assets.int.cline.bot/assets/open_telemetry_relic_example.png"
/>
</Frame>
```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:
<Frame>
<img
src="https://assets.int.cline.bot/assets/open_telemetry_grafana_example.png"
/>
</Frame>
```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
To test your configuration, log in to your account, perform some actions in a task, wait for the export interval, and verify that the data has arrived at your collector.
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
If you arent getting any data in your collector, the easiest way to verify your integration is to enable the developer tools in your editor.
### No Data Being Exported
To do this, open the [webview developer tools](https://code.visualstudio.com/api/extension-guides/webview#inspecting-and-debugging-webviews).
1. **Verify OpenTelemetry is enabled:**
```bash
echo $OTEL_TELEMETRY_ENABLED
```
Should output `1` or `true`
Once youve done so, if you perform some actions that trigger metrics and/or logs (such as doing a task with Cline),
you will see error logs if any error occurs when sending the data to your collector.
2. **Check exporters are configured:**
```bash
echo $OTEL_METRICS_EXPORTER
echo $OTEL_LOGS_EXPORTER
```
If you don't see any logs, enable [debug mode](#debug-mode).
3. **Test with console exporter first:**
```bash
export OTEL_METRICS_EXPORTER=console
export OTEL_LOGS_EXPORTER=console
```
### Connection Errors
@@ -136,7 +194,10 @@ If you don't see any logs, enable [debug mode](#debug-mode).
curl -v https://your-otlp-endpoint:4317
```
2. **Check if insecure mode is needed** by opting out of TLS
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
@@ -146,7 +207,7 @@ If you don't see any logs, enable [debug mode](#debug-mode).
Enable debug logging to see detailed OpenTelemetry information:
```bash
TEL_DEBUG_DIAGNOSTICS=true code .
export TEL_DEBUG_DIAGNOSTICS=true
```
This will output detailed information about:
@@ -157,7 +218,7 @@ This will output detailed information about:
## What Gets Exported
When OpenTelemetry is enabled, Cline exports:
When Opentelemetry is enabled, Cline exports:
### Metrics
- Feature usage counts
@@ -177,9 +238,9 @@ Exported data is already anonymous and doesn't include code content, file paths,
## Limitations
Current OpenTelemetry support in Cline:
- ✅ OTLP metrics export (gRPC, HTTP)
- ✅ OTLP logs export (gRPC, HTTP)
- ✅ Basic configuration via [Remote Configuration](/enterprise-solutions/configuration/remote-configuration/overview#how-remote-configuration-works)
- ✅ 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)
@@ -1,266 +0,0 @@
---
title: "OpenTelemetry Integration Override"
sidebarTitle: "OpenTelemetry Override"
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>
@@ -8,20 +8,14 @@ Cline includes optional monitoring capabilities for organizations that want to t
## Monitoring Options
<CardGroup cols={2}>
<Card title="OpenTelemetry" icon="chart-line" href="/enterprise-solutions/monitoring/opentelemetry">
Export metrics and logs to your own observability backends
</Card>
<Card title="OpenTelemetry Override" icon="chart-line" href="/enterprise-solutions/monitoring/opentelemetry_override">
Export to your own observability backends through environment variables (advanced)
</Card>
</CardGroup>
<CardGroup cols={1}>
<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>
<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
-4
View File
@@ -22,10 +22,6 @@ Your IdP administrator will receive an email with a link to register their organ
### Step 2: Configure Your Identity Provider
<Info>
For a short overview of where SSO configuration lives (Cline dashboard vs WorkOS vs your IdP), see [SSO Setup](/enterprise-solutions/sso-setup).
</Info>
Connect your identity provider (IdP) to WorkOS:
1. In the WorkOS dashboard, go to **AuthKit → Connections**
-61
View File
@@ -1,61 +0,0 @@
---
title: "SSO Setup"
sidebarTitle: "SSO Setup"
description: "Configure Single Sign-On (SSO) for Cline Enterprise via WorkOS AuthKit."
---
## Overview
Cline Enterprise integrates with your identity provider (IdP) via **WorkOS AuthKit** for SSO.
This page describes, at a high level, how SSO is set up for Cline Enterprise using WorkOS AuthKit.
If you havent completed initial onboarding, start with [Onboarding](/enterprise-solutions/onboarding).
## Where setup happens
SSO setup spans two places:
1) **Cline Dashboard (app.cline.bot)**
- Where you sign in and verify SSO works for your organization.
2) **WorkOS dashboard**
- Where your IdP connection is configured (AuthKit → Connections). Your designated admin receives access to this during enterprise onboarding.
## Using the Cline Dashboard
Use the Cline Dashboard at https://app.cline.bot to:
- complete sign-in and onboarding flows
- verify users can authenticate via SSO
## Configure your IdP connection in WorkOS
During enterprise onboarding, your designated admin will receive an invitation email from WorkOS with a link to access your organization's WorkOS dashboard.
<Frame>
<img src="/assets/workos-invite-email.png" alt="WorkOS invitation email example" />
</Frame>
To connect your IdP to Cline Enterprise, configure your identity provider in **WorkOS AuthKit**:
1. In the WorkOS dashboard, go to **AuthKit → Connections**
2. Click **Add Connection**
3. Select your identity provider (e.g., Okta, Microsoft Entra ID/Azure AD, Google Workspace, Generic SAML/OIDC)
4. Follow the provider-specific instructions in WorkOS
WorkOSs UI and required fields vary by provider. For details, follow WorkOS documentation:
- https://workos.com/docs/authkit/sso
## Keycloak note (IdP compatibility)
Cline Enterprises default SSO integration is **via WorkOS**.
If you use **Keycloak** as your IdP, the supported path is to configure Keycloak in WorkOS as a **Generic SAML** or **Generic OIDC** provider (using the settings WorkOS requests for those provider types).
## Verification
After configuring WorkOS:
1) Attempt an SSO sign-in from https://app.cline.bot.
2) Confirm the sign-in completes (you are redirected back successfully).
## Troubleshooting
- **Redirect URI mismatch**: confirm the redirect/callback URL configured in WorkOS matches what was provided during your Cline Enterprise onboarding.
For additional troubleshooting guidance, refer to WorkOS documentation:
- https://workos.com/docs/authkit/sso
@@ -13,6 +13,46 @@ Cline is your AI assistant that can:
- Automate repetitive tasks
- Integrate with external tools
## First Steps
1. **Start a Task**
- Type your request in the chat
- Example: "Create a new React component called Header"
2. **Provide Context**
- Use @ mentions to add files, folders, or URLs
- Example: "@file:src/components/App.tsx"
3. **Review Changes**
- Cline will show diffs before making changes
- You can edit or reject changes
## Key Features
1. **File Editing**
- Create new files
- Modify existing code
- Search and replace across files
2. **Terminal Commands**
- Run npm commands
- Start development servers
- Install dependencies
3. **Code Analysis**
- Find and fix errors
- Refactor code
- Add documentation
4. **Browser Integration**
- Test web pages
- Capture screenshots
- Inspect console logs
## Available Tools
@@ -44,7 +84,6 @@ Cline has access to the following tools for various tasks:
- `ask_followup_question`: Ask user for clarification
- `attempt_completion`: Present final results
Each tool has specific parameters and usage patterns. Here are some examples:
- Create a new file (write_to_file):
@@ -12,19 +12,6 @@ sidebarTitle: "/deep-planning"
/>
</Frame>
## Demo Video
Watch Deep Planning in action as Cline investigates a codebase, asks clarifying questions, and generates a comprehensive implementation plan:
<Frame>
<video
muted
controls
playsInline
src="https://storage.googleapis.com/cline_public_images/docs/assets/Cline-Deep-Planning-Demo.mp4"
/>
</Frame>
When you use `/deep-planning`, Cline follows a four-step process that mirrors how senior developers approach complex features: thorough investigation, discussion & clarification of requirements, detailed planning, and structured task creation with progress tracking.
## The Four-Step Process
+1423 -2311
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -13,7 +13,7 @@
"license": "ISC",
"description": "",
"dependencies": {
"mintlify": "^4.2.249"
"mintlify": "^4.2.23"
},
"overrides": {
"tar-fs": "^3.1.1",
@@ -6,17 +6,12 @@ description: "Complete guide to resolving terminal integration issues in Cline"
This guide helps you resolve terminal integration issues in Cline. Terminal integration is crucial for Cline to execute commands and read their output, enabling it to understand errors, test results, and command responses.
## Try This First: Background Execution Mode
<Tip>
If you're experiencing terminal issues, try switching to a simpler shell like `bash` in the Cline settings, under "Terminal Settings"
The simplest fix for most terminal issues is switching to **Background Execution Mode**:
This resolves most terminal integration problems.
1. Click **Settings** (top right of Cline chat)
2. Go to **Terminal Settings**
3. Set **Terminal Execution Mode** → **Background Exec**
This runs commands in a background process instead of VSCode's terminal, bypassing most integration issues. The guide below is for users who specifically need VSCode's integrated terminal.
---
</Tip>
## Quick Diagnosis Flowchart
+1 -15
View File
@@ -4,21 +4,7 @@ sidebarTitle: "Terminal Quick Fixes"
description: "Quick solutions for common terminal issues"
---
## Try This First: Background Execution Mode
The simplest fix for most terminal issues is switching to **Background Execution Mode**:
1. Click **Settings** (top right of Cline chat)
2. Go to **Terminal Settings**
3. Set **Terminal Execution Mode** → **Background Exec**
This runs commands in a background process instead of VSCode's terminal, bypassing most integration issues.
---
## Other Fixes
If you need VSCode's integrated terminal, try these:
**Here is a list of common fixes, starting with the most applicable:**
- **Switch to bash** (solves most instances)
+1 -1
View File
@@ -1,4 +1,4 @@
streamlit==1.43.2
streamlit>=1.28.0
plotly>=5.17.0
pandas>=2.0.0
numpy>=1.24.0
+69 -176
View File
@@ -1,12 +1,12 @@
{
"name": "claude-dev",
"version": "3.46.1",
"version": "3.45.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-dev",
"version": "3.46.1",
"version": "3.45.0",
"license": "Apache-2.0",
"dependencies": {
"@anthropic-ai/sdk": "^0.37.0",
@@ -20,7 +20,7 @@
"@grpc/grpc-js": "^1.9.15",
"@grpc/reflection": "^1.0.4",
"@mistralai/mistralai": "^1.5.0",
"@modelcontextprotocol/sdk": "^1.25.1",
"@modelcontextprotocol/sdk": "^1.11.1",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/core": "^2.1.0",
"@opentelemetry/exporter-logs-otlp-grpc": "^0.56.0",
@@ -68,7 +68,6 @@
"image-size": "^2.0.2",
"isbinaryfile": "^5.0.2",
"jschardet": "^3.1.4",
"json5": "^2.2.3",
"mammoth": "^1.11.0",
"nanoid": "^5.1.6",
"nice-grpc": "^2.1.12",
@@ -1187,6 +1186,7 @@
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
@@ -2649,6 +2649,7 @@
"node_modules/@grpc/grpc-js": {
"version": "1.9.15",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@grpc/proto-loader": "^0.7.8",
"@types/node": ">=12.12.47"
@@ -2684,18 +2685,6 @@
"@grpc/grpc-js": "^1.8.21"
}
},
"node_modules/@hono/node-server": {
"version": "1.19.7",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.7.tgz",
"integrity": "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==",
"license": "MIT",
"engines": {
"node": ">=18.14.1"
},
"peerDependencies": {
"hono": "^4"
}
},
"node_modules/@inquirer/external-editor": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
@@ -3228,12 +3217,12 @@
"license": "BSD-2-Clause"
},
"node_modules/@modelcontextprotocol/sdk": {
"version": "1.25.1",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.1.tgz",
"integrity": "sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==",
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.22.0.tgz",
"integrity": "sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@hono/node-server": "^1.19.7",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"content-type": "^1.0.5",
@@ -3243,26 +3232,20 @@
"eventsource-parser": "^3.0.0",
"express": "^5.0.1",
"express-rate-limit": "^7.5.0",
"jose": "^6.1.1",
"json-schema-typed": "^8.0.2",
"pkce-challenge": "^5.0.0",
"raw-body": "^3.0.0",
"zod": "^3.25 || ^4.0",
"zod-to-json-schema": "^3.25.0"
"zod": "^3.23.8",
"zod-to-json-schema": "^3.24.1"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@cfworker/json-schema": "^4.1.1",
"zod": "^3.25 || ^4.0"
"@cfworker/json-schema": "^4.1.1"
},
"peerDependenciesMeta": {
"@cfworker/json-schema": {
"optional": true
},
"zod": {
"optional": false
}
}
},
@@ -3300,6 +3283,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -4915,8 +4899,7 @@
"optional": true,
"os": [
"android"
],
"peer": true
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.52.4",
@@ -4929,8 +4912,7 @@
"optional": true,
"os": [
"android"
],
"peer": true
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.52.4",
@@ -4943,8 +4925,7 @@
"optional": true,
"os": [
"darwin"
],
"peer": true
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.52.4",
@@ -4957,8 +4938,7 @@
"optional": true,
"os": [
"darwin"
],
"peer": true
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.52.4",
@@ -4971,8 +4951,7 @@
"optional": true,
"os": [
"freebsd"
],
"peer": true
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.52.4",
@@ -4985,8 +4964,7 @@
"optional": true,
"os": [
"freebsd"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.52.4",
@@ -4999,8 +4977,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.52.4",
@@ -5013,8 +4990,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.52.4",
@@ -5027,8 +5003,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.52.4",
@@ -5041,8 +5016,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.52.4",
@@ -5055,8 +5029,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.52.4",
@@ -5069,8 +5042,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.52.4",
@@ -5083,8 +5055,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.52.4",
@@ -5097,8 +5068,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.52.4",
@@ -5111,8 +5081,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.52.4",
@@ -5125,8 +5094,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.52.4",
@@ -5139,8 +5107,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.52.4",
@@ -5153,8 +5120,7 @@
"optional": true,
"os": [
"openharmony"
],
"peer": true
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.52.4",
@@ -5167,8 +5133,7 @@
"optional": true,
"os": [
"win32"
],
"peer": true
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.52.4",
@@ -5181,8 +5146,7 @@
"optional": true,
"os": [
"win32"
],
"peer": true
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.52.4",
@@ -5195,8 +5159,7 @@
"optional": true,
"os": [
"win32"
],
"peer": true
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.52.4",
@@ -5209,8 +5172,7 @@
"optional": true,
"os": [
"win32"
],
"peer": true
]
},
"node_modules/@sap-ai-sdk/ai-api": {
"version": "2.1.0",
@@ -6467,60 +6429,6 @@
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.5.0",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.1.0",
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.5.0",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.1.0",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "1.0.5",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.5.0",
"@emnapi/runtime": "^1.5.0",
"@tybys/wasm-util": "^0.10.1"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
"version": "2.8.1",
"inBundle": true,
"license": "0BSD",
"optional": true
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.1.14",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz",
@@ -6763,8 +6671,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@types/get-folder-size": {
"version": "3.0.4",
@@ -6789,6 +6696,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.21.tgz",
"integrity": "sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA==",
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -7486,6 +7394,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -8252,6 +8161,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.3",
"caniuse-lite": "^1.0.30001741",
@@ -9455,7 +9365,8 @@
},
"node_modules/devtools-protocol": {
"version": "0.0.1342118",
"license": "BSD-3-Clause"
"license": "BSD-3-Clause",
"peer": true
},
"node_modules/diff": {
"version": "5.2.0",
@@ -11440,16 +11351,6 @@
"he": "bin/he"
}
},
"node_modules/hono": {
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.11.1.tgz",
"integrity": "sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=16.9.0"
}
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
"dev": true,
@@ -12431,19 +12332,11 @@
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"license": "MIT",
"peer": true,
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/jose": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz",
"integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"dev": true,
@@ -12504,16 +12397,9 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/json-schema-typed": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
"integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
"license": "BSD-2-Clause"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
@@ -12533,12 +12419,10 @@
}
},
"node_modules/jsonwebtoken": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
"integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
"version": "9.0.2",
"license": "MIT",
"dependencies": {
"jws": "^4.0.1",
"jws": "^3.2.2",
"lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4",
@@ -12554,6 +12438,23 @@
"npm": ">=6"
}
},
"node_modules/jsonwebtoken/node_modules/jwa": {
"version": "1.4.2",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jsonwebtoken/node_modules/jws": {
"version": "3.2.2",
"license": "MIT",
"dependencies": {
"jwa": "^1.4.1",
"safe-buffer": "^5.0.1"
}
},
"node_modules/jszip": {
"version": "3.10.1",
"license": "(MIT OR GPL-3.0-or-later)",
@@ -12581,12 +12482,10 @@
}
},
"node_modules/jws": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"version": "4.0.0",
"license": "MIT",
"dependencies": {
"jwa": "^2.0.1",
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
@@ -12662,6 +12561,7 @@
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
"integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
"license": "MPL-2.0",
"peer": true,
"dependencies": {
"detect-libc": "^2.0.3"
},
@@ -15542,7 +15442,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -15563,7 +15462,6 @@
}
],
"license": "MIT",
"peer": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -16231,7 +16129,6 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz",
"integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -17668,7 +17565,6 @@
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
@@ -17685,7 +17581,6 @@
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12.0.0"
},
@@ -18049,6 +17944,7 @@
"version": "5.5.3",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -18308,7 +18204,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -18383,7 +18278,6 @@
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12.0.0"
},
@@ -19079,17 +18973,16 @@
"node_modules/zod": {
"version": "3.25.76",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zod-to-json-schema": {
"version": "3.25.0",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz",
"integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==",
"version": "3.24.4",
"license": "ISC",
"peerDependencies": {
"zod": "^3.25 || ^4"
"zod": "^3.24.1"
}
}
}
+2 -3
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.46.1",
"version": "3.45.0",
"icon": "assets/icons/icon.png",
"engines": {
"vscode": "^1.84.0"
@@ -463,7 +463,7 @@
"@grpc/grpc-js": "^1.9.15",
"@grpc/reflection": "^1.0.4",
"@mistralai/mistralai": "^1.5.0",
"@modelcontextprotocol/sdk": "^1.25.1",
"@modelcontextprotocol/sdk": "^1.11.1",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/core": "^2.1.0",
"@opentelemetry/exporter-logs-otlp-grpc": "^0.56.0",
@@ -511,7 +511,6 @@
"image-size": "^2.0.2",
"isbinaryfile": "^5.0.2",
"jschardet": "^3.1.4",
"json5": "^2.2.3",
"mammoth": "^1.11.0",
"nanoid": "^5.1.6",
"nice-grpc": "^2.1.12",
-1
View File
@@ -369,7 +369,6 @@ message UpdateSettingsRequest {
optional OnboardingModelGroup onboarding_models = 33;
optional bool cline_web_tools_enabled = 34;
optional bool enable_parallel_tool_calling = 35;
optional bool background_edit_enabled = 36;
}
message UpdateTerminalConnectionTimeoutRequest {
-2
View File
@@ -55,10 +55,8 @@ enum Setting {
}
message GetTelemetrySettingsResponse {
Setting is_enabled = 1;
optional string error_level = 2;
}
message TelemetrySettingsEvent {
Setting is_enabled = 1;
optional string error_level = 2;
}
-1
View File
@@ -77,7 +77,6 @@ function createMockService<T extends grpc.UntypedServiceImplementation>(serviceN
case "getTelemetrySettings":
callback(null, {
isEnabled: 2, // Setting.DISABLED
errorLevel: "all",
})
return
+4 -2
View File
@@ -104,15 +104,17 @@ async function showVersionUpdateAnnouncement(context: vscode.ExtensionContext) {
if (!previousVersion || currentVersion !== previousVersion) {
Logger.log(`Cline version changed: ${previousVersion} -> ${currentVersion}. First run or update detected.`)
// Check if there's a new announcement to show
// Use the same condition as announcements: focus when there's a new announcement to show
const lastShownAnnouncementId = context.globalState.get<string>("lastShownAnnouncementId")
const latestAnnouncementId = getLatestAnnouncementId()
if (lastShownAnnouncementId !== latestAnnouncementId) {
// Show notification when there's a new announcement (major/minor updates or fresh installs)
// Focus Cline when there's a new announcement to show (major/minor updates or fresh installs)
const message = previousVersion
? `Cline has been updated to v${currentVersion}`
: `Welcome to Cline v${currentVersion}`
await HostProvider.workspace.openClineSidebarPanel({})
await new Promise((resolve) => setTimeout(resolve, 200))
HostProvider.window.showMessage({
type: ShowMessageType.INFORMATION,
message,
+1 -5
View File
@@ -4,7 +4,6 @@ import { ModelInfo } from "@shared/api"
import OpenAI from "openai"
import { ApiHandler, CommonApiHandlerOptions } from "../index"
import { withRetry } from "../retry"
import { sanitizeAnthropicMessages } from "../transform/anthropic-format"
import { convertAnthropicMessageToGemini } from "../transform/gemini-format"
import { convertToOpenAiMessages } from "../transform/openai-format"
import { ApiStream } from "../transform/stream"
@@ -145,15 +144,12 @@ export class AIhubmixHandler implements ApiHandler {
const client = this.ensureAnthropicClient()
const modelId = this.options.modelId || "claude-3-5-sonnet-20241022"
// Sanitize messages to remove Cline-specific fields like call_id that are not allowed by Anthropic API
const sanitizedMessages = sanitizeAnthropicMessages(messages, false)
const stream = await client.messages.create({
model: modelId,
temperature: 0,
max_tokens: this.options.modelInfo?.maxTokens || 8192,
system: [{ text: systemPrompt, type: "text" }],
messages: sanitizedMessages,
messages,
stream: true,
})
+22 -2
View File
@@ -755,7 +755,10 @@ export class AwsBedrockHandler implements ApiHandler {
// For Anthropic models with thinking enabled, temperature must be 1
if (modelType === "anthropic") {
const budget_tokens = this.options.thinkingBudgetTokens || 0
const reasoningOn = modelInfo.supportsReasoning && budget_tokens > 0
const baseModelId =
(this.options.awsBedrockCustomSelected ? this.options.awsBedrockCustomModelBaseId : this.getModel().id) ||
this.getModel().id
const reasoningOn = this.shouldEnableReasoning(baseModelId, budget_tokens)
return {
maxTokens: modelInfo.maxTokens || 8192,
@@ -769,6 +772,20 @@ export class AwsBedrockHandler implements ApiHandler {
}
}
/**
* Determines if reasoning should be enabled for Claude models
*/
private shouldEnableReasoning(baseModelId: string, budgetTokens: number): boolean {
return (
(baseModelId.includes("3-7") ||
baseModelId.includes("sonnet-4") ||
baseModelId.includes("opus-4") ||
baseModelId.includes("haiku-4-5") ||
baseModelId.includes("sonnet-4-5")) &&
budgetTokens !== 0
)
}
/**
* Creates a message using Anthropic Claude models through AWS Bedrock Converse API
* Implements support for Anthropic Claude models using the unified Converse API
@@ -798,7 +815,10 @@ export class AwsBedrockHandler implements ApiHandler {
// Get thinking configuration
const budget_tokens = this.options.thinkingBudgetTokens || 0
const reasoningOn = model.info.supportsReasoning && budget_tokens > 0
const baseModelId =
(this.options.awsBedrockCustomSelected ? this.options.awsBedrockCustomModelBaseId : this.getModel().id) ||
this.getModel().id
const reasoningOn = this.shouldEnableReasoning(baseModelId, budget_tokens)
// Prepare request for Anthropic model using Converse API
const command = new ConverseStreamCommand({
+1 -7
View File
@@ -11,12 +11,6 @@ interface CerebrasHandlerOptions extends CommonApiHandlerOptions {
apiModelId?: string
}
// Conservative max_tokens for Cerebras to avoid premature rate limiting.
// Cerebras rate limiter estimates token consumption using max_completion_tokens upfront,
// so requesting the model maximum (e.g., 64K) reserves that quota even if actual usage is low.
// 16K is sufficient for most agentic tool use while preserving rate limit headroom.
const CEREBRAS_DEFAULT_MAX_TOKENS = 16_384
export class CerebrasHandler implements ApiHandler {
private options: CerebrasHandlerOptions
private client: Cerebras | undefined
@@ -117,7 +111,7 @@ export class CerebrasHandler implements ApiHandler {
messages: cerebrasMessages,
temperature: 0,
stream: true,
max_tokens: CEREBRAS_DEFAULT_MAX_TOKENS,
max_tokens: this.getModel().info.maxTokens,
})
// Handle streaming response
+7 -3
View File
@@ -7,7 +7,6 @@ import {
import { ChatMessage, OrchestrationClient, OrchestrationModuleConfig } from "@sap-ai-sdk/orchestration"
import { ModelInfo, SapAiCoreModelId, sapAiCoreDefaultModelId, sapAiCoreModels } from "@shared/api"
import axios from "axios"
import JSON5 from "json5"
import OpenAI from "openai"
import { ClineStorageMessage } from "@/shared/messages/content"
import { getAxiosSettings } from "@/shared/net"
@@ -879,6 +878,12 @@ export class SapAiCoreHandler implements ApiHandler {
stream: any,
_model: { id: SapAiCoreModelId; info: ModelInfo },
): AsyncGenerator<any, void, unknown> {
function toStrictJson(str: string): string {
// Wrap it in parentheses so JS will treat it as an expression
const obj = new Function("return " + str)()
return JSON.stringify(obj)
}
const _usage = { input_tokens: 0, output_tokens: 0 }
try {
@@ -893,8 +898,7 @@ export class SapAiCoreHandler implements ApiHandler {
try {
// Parse the incoming JSON data from the stream
// Using JSON5 to handle relaxed JSON syntax (e.g., single quotes)
const data = JSON5.parse(jsonData)
const data = JSON.parse(toStrictJson(jsonData))
// Handle metadata (token usage)
if (data.metadata?.usage) {
+1 -5
View File
@@ -48,11 +48,7 @@ export function convertAnthropicContentToGemini(content: string | ClineStorageMe
},
}
case "thinking":
return {
text: block.thinking,
thought: true,
thoughtSignature: block.signature || GEMINI_DUMMY_THOUGHT_SIGNATURE,
}
return { text: block.thinking, thought: true, thoughtSignature: block.signature }
default:
return undefined
}
+4 -20
View File
@@ -176,18 +176,11 @@ export function convertToOpenAiMessages(
// Process tool use messages
const tool_calls: OpenAI.Chat.ChatCompletionMessageToolCall[] = toolMessages.map((toolMessage) => {
const toolDetails = toolMessage.reasoning_details
if (toolDetails) {
if (toolDetails?.length) {
if (Array.isArray(toolDetails)) {
// For Gemini: reasoning details must be linkable back to the tool call.
// Sometimes OpenRouter/Gemini returns entries without `id`; those poison the next request.
// Keep only entries with an id matching the tool call id.
// See: https://github.com/cline/cline/issues/8214
const validDetails = toolDetails.filter((detail: any) => detail?.id === toolMessage.id)
if (validDetails.length > 0) reasoningDetails.push(...validDetails)
reasoningDetails.push(...toolDetails)
} else {
// Single reasoning detail - only include if it has matching id
const detail = toolDetails as any
if (detail?.id === toolMessage.id) reasoningDetails.push(toolDetails)
reasoningDetails.push(toolDetails)
}
}
@@ -207,17 +200,13 @@ export function convertToOpenAiMessages(
const hasMeaningfulContent = content !== undefined && content.trim() !== ""
const finalContent = hasMeaningfulContent ? content : hasToolCalls ? null : undefined
const consolidatedReasoningDetails =
reasoningDetails.length > 0 ? consolidateReasoningDetails(reasoningDetails as any) : []
openAiMessages.push({
role: "assistant",
content: finalContent,
// Cannot be an empty array. API expects an array with minimum length 1, and will respond with an error if it's empty
tool_calls: tool_calls?.length > 0 ? tool_calls : undefined,
// Only include reasoning_details when non-empty; sending [] can trigger provider validation issues.
// @ts-ignore-next-line
reasoning_details: consolidatedReasoningDetails.length > 0 ? consolidatedReasoningDetails : undefined,
reasoning_details: reasoningDetails.length > 0 ? consolidateReasoningDetails(reasoningDetails) : undefined,
})
}
}
@@ -256,11 +245,6 @@ function consolidateReasoningDetails(reasoningDetails: ReasoningDetail[]): Reaso
const groupedByIndex = new Map<number, ReasoningDetail[]>()
for (const detail of reasoningDetails) {
// Drop corrupted encrypted reasoning blocks that would otherwise trigger:
// "Invalid input: expected string, received undefined" for reasoning_details.*.data
// See: https://github.com/cline/cline/issues/8214
if (detail.type === "reasoning.encrypted" && !detail.data) continue
const index = detail.index ?? 0
if (!groupedByIndex.has(index)) {
groupedByIndex.set(index, [])
@@ -36,46 +36,6 @@ export async function createOpenRouterStream(
model.id = model.id.slice(0, -CLAUDE_SONNET_1M_SUFFIX.length)
}
// Gemini models require thought signatures for tool calls. When switching providers mid-conversation,
// historical tool calls may not include Gemini/OpenRouter reasoning details, which can poison the next request.
// Bandaid: for Gemini only, drop tool_calls that lack reasoning_details and their paired tool messages.
if (model.id.includes("gemini")) {
const droppedToolCallIds = new Set<string>()
const sanitized: OpenAI.Chat.ChatCompletionMessageParam[] = []
for (const msg of openAiMessages) {
if (msg.role === "assistant") {
const anyMsg = msg as any
const toolCalls = anyMsg.tool_calls
if (Array.isArray(toolCalls) && toolCalls.length > 0) {
const reasoningDetails = anyMsg.reasoning_details
const hasReasoningDetails = Array.isArray(reasoningDetails) && reasoningDetails.length > 0
if (!hasReasoningDetails) {
for (const tc of toolCalls) {
if (tc?.id) droppedToolCallIds.add(tc.id)
}
// Keep any textual content, but drop the tool_calls themselves.
if (anyMsg.content) {
sanitized.push({ role: "assistant", content: anyMsg.content } as any)
}
continue
}
}
}
if (msg.role === "tool") {
const anyMsg = msg as any
if (anyMsg.tool_call_id && droppedToolCallIds.has(anyMsg.tool_call_id)) {
continue
}
}
sanitized.push(msg)
}
openAiMessages = sanitized
}
// prompt caching: https://openrouter.ai/docs/prompt-caching
// this was initially specifically for claude models (some models may 'support prompt caching' automatically without this)
// handles direct model.id match logic
-1
View File
@@ -954,7 +954,6 @@ export class Controller {
subagentsEnabled,
nativeToolCallSetting: this.stateManager.getGlobalStateKey("nativeToolCallEnabled"),
enableParallelToolCalling: this.stateManager.getGlobalSettingsKey("enableParallelToolCalling"),
backgroundEditEnabled: this.stateManager.getGlobalSettingsKey("backgroundEditEnabled"),
}
}
@@ -1,10 +1,10 @@
import fs from "node:fs/promises"
import path from "node:path"
import { ensureCacheDirectoryExists, GlobalFileNames } from "@core/storage/disk"
import { ANTHROPIC_MAX_THINKING_BUDGET, ModelInfo } from "@shared/api"
import { ModelInfo } from "@shared/api"
import { fileExistsAtPath } from "@utils/fs"
import { parsePrice } from "@utils/model-utils"
import axios from "axios"
import fs from "fs/promises"
import path from "path"
import { getAxiosSettings } from "@/shared/net"
import { basetenModels } from "../../../shared/api"
import { Controller } from ".."
@@ -22,7 +22,22 @@ export async function refreshBasetenModels(controller: Controller): Promise<Reco
const models: Record<string, Partial<ModelInfo> & { supportedFeatures?: string[] }> = {}
try {
if (basetenApiKey) {
if (!basetenApiKey) {
// Don't throw an error, just use static models, althought this might be slightly out of date
for (const [modelId, modelInfo] of Object.entries(basetenModels)) {
models[modelId] = {
maxTokens: modelInfo.maxTokens,
contextWindow: modelInfo.contextWindow,
supportsImages: modelInfo.supportsImages,
supportsPromptCache: modelInfo.supportsPromptCache,
inputPrice: modelInfo.inputPrice,
outputPrice: modelInfo.outputPrice,
cacheWritesPrice: (modelInfo as any).cacheWritesPrice || 0,
cacheReadsPrice: (modelInfo as any).cacheReadsPrice || 0,
description: (modelInfo as any).description || `${modelId} model`,
}
}
} else {
// Ensure the API key is properly formatted
const cleanApiKey = basetenApiKey.trim()
if (!cleanApiKey) {
@@ -39,9 +54,9 @@ export async function refreshBasetenModels(controller: Controller): Promise<Reco
...getAxiosSettings(),
})
const rawModels = response?.data?.data
if (response.data?.data) {
const rawModels = response.data.data
if (rawModels && Array.isArray(rawModels)) {
for (const rawModel of rawModels) {
// Filter out non-chat models and validate model capabilities
if (!isValidChatModel(rawModel)) {
@@ -50,9 +65,6 @@ export async function refreshBasetenModels(controller: Controller): Promise<Reco
// Check if we have static pricing information for this model
const staticModelInfo = basetenModels[rawModel.id as keyof typeof basetenModels]
const supportThinking = rawModel?.supported_features?.some(
(p: string) => p === "reasoning_effort" || p === "reasoning",
)
const modelInfo: Partial<ModelInfo> & { supportedFeatures?: string[] } = {
maxTokens: rawModel.max_completion_tokens || staticModelInfo?.maxTokens,
@@ -65,23 +77,15 @@ export async function refreshBasetenModels(controller: Controller): Promise<Reco
cacheReadsPrice: staticModelInfo?.cacheReadsPrice || 0,
description: generateModelDescription(rawModel, staticModelInfo),
supportedFeatures: rawModel.supported_features || [],
supportsReasoning: supportThinking || false,
// If thinking is supported, set maxBudget with a default value as a placeholder
// to ensure it has a valid thinkingConfig that lets the application know thinking is supported.
thinkingConfig: supportThinking ? { maxBudget: ANTHROPIC_MAX_THINKING_BUDGET } : undefined,
}
models[rawModel.id] = modelInfo
}
} else {
console.error("Invalid response from Baseten API")
}
// Cache the fetched models to disk
await fs.writeFile(basetenModelsFilePath, JSON.stringify(models))
}
// If no API key is set or models is empty, throw an error to trigger fallback
if (Object.keys(models).length === 0) {
throw new Error("No Baseten API key set or no models fetched")
}
} catch (error) {
console.error("Error fetching Baseten models:", error)
@@ -125,8 +129,6 @@ export async function refreshBasetenModels(controller: Controller): Promise<Reco
cacheWritesPrice: (modelInfo as any).cacheWritesPrice || 0,
cacheReadsPrice: (modelInfo as any).cacheReadsPrice || 0,
description: (modelInfo as any).description || `${modelId} model`,
supportsReasoning: modelInfo.supportsReasoning || false,
thinkingConfig: modelInfo.supportsReasoning ? { maxBudget: ANTHROPIC_MAX_THINKING_BUDGET } : undefined,
}
}
}
@@ -147,8 +149,6 @@ export async function refreshBasetenModels(controller: Controller): Promise<Reco
cacheReadsPrice: model.cacheReadsPrice ?? 0,
description: model.description ?? "",
tiers: model.tiers,
supportsReasoning: model.supportsReasoning || false,
thinkingConfig: model.supportsReasoning ? { maxBudget: ANTHROPIC_MAX_THINKING_BUDGET } : undefined,
}
}
@@ -320,10 +320,6 @@ export async function updateSettings(controller: Controller, request: UpdateSett
}
}
if (request.backgroundEditEnabled !== undefined) {
controller.stateManager.setGlobalState("backgroundEditEnabled", !!request.backgroundEditEnabled)
}
if (request.autoCondenseThreshold !== undefined) {
const threshold = Math.min(1, Math.max(0, request.autoCondenseThreshold)) // Clamp to 0-1 range
controller.stateManager.setGlobalState("autoCondenseThreshold", threshold)
@@ -1,5 +1,4 @@
import { buildApiHandler } from "@core/api"
import { isBinaryFile } from "isbinaryfile"
import { HostProvider } from "@/hosts/host-provider"
import { formatContentBlockToMarkdown } from "@/integrations/misc/export-markdown"
import { ApiConfiguration } from "@/shared/api"
@@ -444,29 +443,9 @@ const BINARY_EXTENSIONS = new Set([
])
/**
* Check if a file is binary based on its extension or content.
* @param filePath - Absolute path to the file to check
* @returns Promise<boolean> - true if the file is binary, false if text or if detection fails
* Check if a file is binary based on its extension
*/
export async function detectBinaryFile(filePath: string): Promise<boolean> {
const lastDotIndex = filePath.lastIndexOf(".")
const lastSlashIndex = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"))
const ext = lastDotIndex > lastSlashIndex ? filePath.substring(lastDotIndex).toLowerCase() : ""
const isDotfile = lastDotIndex !== -1 && lastDotIndex === lastSlashIndex + 1
// Legacy/fast method: Check known binary extensions
if (ext && BINARY_EXTENSIONS.has(ext)) {
return true
}
// Use actual binary check for dotfiles or files without extensions. Returns true if file is binary.
if (!ext || isDotfile) {
try {
const result = await isBinaryFile(filePath)
return result
} catch {
return false
}
}
return false
export function isBinaryFile(filePath: string): boolean {
const ext = filePath.substring(filePath.lastIndexOf(".")).toLowerCase()
return BINARY_EXTENSIONS.has(ext)
}
+1 -5
View File
@@ -8,10 +8,6 @@
* @returns true if hooks are enabled and supported on this platform, false otherwise
*/
export function getHooksEnabledSafe(userSetting: boolean | undefined): boolean {
// Handle legacy object format: {user: boolean, featureFlag: boolean}, which
// can occur if the migration hasn't run yet or if reading from an old state.
const booleanValue = Boolean((userSetting as any)?.user ?? userSetting)
// Force hooks to false on Windows (not yet supported)
return process.platform === "win32" ? false : booleanValue
return process.platform === "win32" ? false : (userSetting ?? false)
}
@@ -2,7 +2,7 @@ You are Cline, a highly skilled software engineer with extensive knowledge in ma
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
====
@@ -68,8 +68,7 @@ When user is providing you with feedback on how you could improve, you can let t
RULES
- The current working directory is `/test/project` - this is the directory where all the tools will be executed from. Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser.
- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.
- The current working directory is `/test/project` - this is the directory where all the tools will be executed from.
====
@@ -2,7 +2,7 @@ You are Cline, a highly skilled software engineer with extensive knowledge in ma
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
====
@@ -67,7 +67,6 @@ When user is providing you with feedback on how you could improve, you can let t
RULES
- The current working directory is `/test/project` - this is the directory where all the tools will be executed from.
- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.
====
@@ -2,7 +2,7 @@ You are Cline, a highly skilled software engineer with extensive knowledge in ma
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
====
@@ -42,8 +42,7 @@ CAPABILITIES
RULES
- The current working directory is `/test/project` - this is the directory where all the tools will be executed from. Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser.
- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.
- The current working directory is `/test/project` - this is the directory where all the tools will be executed from.
====
@@ -2,7 +2,7 @@ You are Cline, a highly skilled software engineer with extensive knowledge in ma
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
====
@@ -68,7 +68,7 @@ When user is providing you with feedback on how you could improve, you can let t
RULES
- The current working directory is `/test/project` - this is the directory where all the tools will be executed from. Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser.
- The current working directory is `/test/project` - this is the directory where all the tools will be executed from.
====
@@ -608,6 +608,7 @@ RULES
- When using the replace_in_file tool, if you use multiple SEARCH/REPLACE blocks, list them in the order they appear in the file. For example if you need to make changes to both line 10 and line 50, first include the SEARCH/REPLACE block for line 10, followed by the SEARCH/REPLACE block for line 50.
- When using the replace_in_file tool, Do NOT add extra characters to the markers (e.g., ------- SEARCH> is INVALID). Do NOT forget to use the closing +++++++ REPLACE marker. Do NOT modify the marker format in any way. Malformed XML will cause complete tool failure and break the entire editing process.
- You may use multiple tools in a single response when the operations are independent (e.g., reading several files, creating independent files). For dependent operations where one result informs the next, use tools sequentially and wait for the user's response. Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser.
- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.
====
@@ -2,7 +2,7 @@ You are Cline, a software engineering AI. Your mission is to execute precisely w
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You should use a single tool at a time and wait for the result before proceeding. You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
When using tools, proceed directly with tool calls. Save explanations for the attempt_completion summary. Both attempt_completion and plan_mode_respond display to the user as assistant messages, so include your message content within the tool call itself rather than duplicating it outside.
@@ -240,7 +240,7 @@ OBJECTIVE
You accomplish a given task iteratively, breaking it down into clear steps and working through them methodically.
1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.
2. Work through these goals sequentially, utilizing available tools as necessary. Use a single tool at a time and wait for the result before proceeding. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. If one of the values for a required parameter is missing, ask the user to provide the missing parameters using the ask_followup_question tool (use your tools to gather information when possible to avoid unnecessary questions). Focus on required parameters only - proceed with defaults for optional parameters.
4. Once you've completed the user's task, use the attempt_completion tool to present the result. Provide a CLI command to showcase your work when applicable (e.g., `open index.html` for web development). Before calling attempt_completion, verify with the user that the feature works as expected.
5. For non-actionable tasks, use attempt_completion to provide a clear explanation or direct answer.
@@ -2,7 +2,7 @@ You are Cline, a software engineering AI. Your mission is to execute precisely w
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You should use a single tool at a time and wait for the result before proceeding. You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
When using tools, proceed directly with tool calls. Save explanations for the attempt_completion summary. Both attempt_completion and plan_mode_respond display to the user as assistant messages, so include your message content within the tool call itself rather than duplicating it outside.
@@ -238,7 +238,7 @@ OBJECTIVE
You accomplish a given task iteratively, breaking it down into clear steps and working through them methodically.
1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.
2. Work through these goals sequentially, utilizing available tools as necessary. Use a single tool at a time and wait for the result before proceeding. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. If one of the values for a required parameter is missing, ask the user to provide the missing parameters using the ask_followup_question tool (use your tools to gather information when possible to avoid unnecessary questions). Focus on required parameters only - proceed with defaults for optional parameters.
4. Once you've completed the user's task, use the attempt_completion tool to present the result. Provide a CLI command to showcase your work when applicable (e.g., `open index.html` for web development). Before calling attempt_completion, verify with the user that the feature works as expected.
5. For non-actionable tasks, use attempt_completion to provide a clear explanation or direct answer.
@@ -2,7 +2,7 @@ You are Cline, a software engineering AI. Your mission is to execute precisely w
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You should use a single tool at a time and wait for the result before proceeding. You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
When using tools, proceed directly with tool calls. Save explanations for the attempt_completion summary. Both attempt_completion and plan_mode_respond display to the user as assistant messages, so include your message content within the tool call itself rather than duplicating it outside.
@@ -218,7 +218,7 @@ OBJECTIVE
You accomplish a given task iteratively, breaking it down into clear steps and working through them methodically.
1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.
2. Work through these goals sequentially, utilizing available tools as necessary. Use a single tool at a time and wait for the result before proceeding. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. If one of the values for a required parameter is missing, ask the user to provide the missing parameters using the ask_followup_question tool (use your tools to gather information when possible to avoid unnecessary questions). Focus on required parameters only - proceed with defaults for optional parameters.
4. Once you've completed the user's task, use the attempt_completion tool to present the result. Provide a CLI command to showcase your work when applicable (e.g., `open index.html` for web development). Before calling attempt_completion, verify with the user that the feature works as expected.
5. For non-actionable tasks, use attempt_completion to provide a clear explanation or direct answer.
@@ -2,7 +2,7 @@ You are Cline, a software engineering AI. Your mission is to execute precisely w
TOOL USE
You have access to a set of tools that are executed upon the user's approval. You should use a single tool at a time and wait for the result before proceeding. You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
When using tools, proceed directly with tool calls. Save explanations for the attempt_completion summary. Both attempt_completion and plan_mode_respond display to the user as assistant messages, so include your message content within the tool call itself rather than duplicating it outside.
@@ -240,7 +240,7 @@ OBJECTIVE
You accomplish a given task iteratively, breaking it down into clear steps and working through them methodically.
1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.
2. Work through these goals sequentially, utilizing available tools as necessary. Use a single tool at a time and wait for the result before proceeding. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. If one of the values for a required parameter is missing, ask the user to provide the missing parameters using the ask_followup_question tool (use your tools to gather information when possible to avoid unnecessary questions). Focus on required parameters only - proceed with defaults for optional parameters.
4. Once you've completed the user's task, use the attempt_completion tool to present the result. Provide a CLI command to showcase your work when applicable (e.g., `open index.html` for web development). Before calling attempt_completion, verify with the user that the feature works as expected.
5. For non-actionable tasks, use attempt_completion to provide a clear explanation or direct answer.
@@ -3,23 +3,6 @@ import { SystemPromptSection } from "../templates/placeholders"
import { TemplateEngine } from "../templates/TemplateEngine"
import type { PromptVariant, SystemPromptContext } from "../types"
/**
* Checks if there are any enabled MCP servers in the context.
* This is a utility function to standardize MCP server detection across all prompt variants.
*
* @param context - The system prompt context
* @returns true if there are enabled MCP servers, false otherwise
*
* @example
* const hasMcp = hasEnabledMcpServers(context)
* if (hasMcp) {
* // Include MCP-specific instructions
* }
*/
export function hasEnabledMcpServers(context: SystemPromptContext): boolean {
return (context.mcpHub?.getServers() || []).length > 0
}
const MCP_TEMPLATE_TEXT = `MCP SERVERS
The Model Context Protocol (MCP) enables communication between the system and locally running MCP servers that provide additional tools and resources to extend your capabilities.
@@ -14,24 +14,6 @@ Default Shell: {{shell}}
Home Directory: {{homeDir}}
{{WORKSPACE_TITLE}}: {{workingDir}}`
/**
* Get the shell that will actually be used for command execution.
* When using background exec mode, commands run in the system default shell
* (cmd.exe on Windows, /bin/bash on Unix), not the VS Code configured shell.
*/
function getEffectiveShell(context: SystemPromptContext): string {
if (context.terminalExecutionMode === "backgroundExec") {
// Background exec uses the system default shell, not VS Code config
if (process.platform === "win32") {
return process.env.COMSPEC || "cmd.exe"
} else {
return process.env.SHELL || "/bin/bash"
}
}
// VS Code terminal mode (or undefined) uses the VS Code configured shell
return getShell()
}
export async function getSystemEnv(context: SystemPromptContext, isTesting = false) {
const currentWorkDir = context.cwd || process.cwd()
const workspaces = (await getWorkspacePaths({}))?.paths || [currentWorkDir]
@@ -48,7 +30,7 @@ export async function getSystemEnv(context: SystemPromptContext, isTesting = fal
: {
os: osName(),
ide: context.ide,
shell: getEffectiveShell(context),
shell: getShell(),
homeDir: osModule.homedir(),
workingDir: currentWorkDir,
workspaces: workspaces,
-2
View File
@@ -115,8 +115,6 @@ export interface SystemPromptContext {
readonly isSubagentsEnabledAndCliInstalled?: boolean
readonly isCliSubagent?: boolean
readonly enableNativeToolCalls?: boolean
readonly enableParallelToolCalling?: boolean
readonly terminalExecutionMode?: "vscodeTerminal" | "backgroundExec"
}
/**
@@ -4,9 +4,9 @@ import type { PromptVariant, SystemPromptContext } from "../../types"
const GEMINI_3_AGENT_ROLE_TEMPLATE = (_context: SystemPromptContext) =>
`You are Cline, a software engineering AI. Your mission is to execute precisely what is requested - implement exactly what was asked for, with the simplest solution that fulfills all requirements. Ask clarifying questions to ensure you understand the user's requirements and that they understand your approach before proceeding.`
const GEMINI_3_TOOL_USE_TEMPLATE = (context: SystemPromptContext) => `TOOL USE
const GEMINI_3_TOOL_USE_TEMPLATE = (_context: SystemPromptContext) => `TOOL USE
You have access to a set of tools that are executed upon the user's approval.${context.enableParallelToolCalling ? " You may use multiple tools in a single response when the operations are independent (e.g., reading several files, searching in parallel). For dependent operations where one result informs the next, use tools sequentially." : " You should use a single tool at a time and wait for the result before proceeding."} You will receive the results of all tool uses in the user's response.
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
When using tools, proceed directly with tool calls. Save explanations for the attempt_completion summary. Both attempt_completion and plan_mode_respond display to the user as assistant messages, so include your message content within the tool call itself rather than duplicating it outside.`
@@ -15,7 +15,7 @@ const GEMINI_3_OBJECTIVE_TEMPLATE = (context: SystemPromptContext) => `OBJECTIVE
You accomplish a given task iteratively, breaking it down into clear steps and working through them methodically.
1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.
2. Work through these goals sequentially, utilizing available tools as necessary. ${context.enableParallelToolCalling ? "You may call multiple independent tools in a single response to work efficiently." : "Use a single tool at a time and wait for the result before proceeding."} Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use.${context.yoloModeToggled !== true ? " If one of the values for a required parameter is missing, ask the user to provide the missing parameters using the ask_followup_question tool (use your tools to gather information when possible to avoid unnecessary questions)." : ""} Focus on required parameters only - proceed with defaults for optional parameters.
4. Once you've completed the user's task, use the attempt_completion tool to present the result. Provide a CLI command to showcase your work when applicable (e.g., \`open index.html\` for web development).${context.yoloModeToggled !== true ? " Before calling attempt_completion, verify with the user that the feature works as expected." : ""}
5. For non-actionable tasks, use attempt_completion to provide a clear explanation or direct answer.
@@ -1,9 +1,8 @@
import { hasEnabledMcpServers } from "../../components/mcp"
import { SystemPromptSection } from "../../templates/placeholders"
import type { SystemPromptContext } from "../../types"
const GLM_TOOL_USE_TEMPLATE = (context: SystemPromptContext) => {
const hasMcpServers = hasEnabledMcpServers(context)
const hasMcpServers = (context.mcpHub?.getServers() || []).length > 0
return `Begin every task by exploring the codebase (e.g., list_files, search_files, read_file) and outlining the required changes. Do not implement until exploration yields enough context to state objectives, approach, affected files, and risks. Briefly summarize the plan, then proceed with implementation.
@@ -1,4 +1,3 @@
import { hasEnabledMcpServers } from "../../components/mcp"
import { SystemPromptSection } from "../../templates/placeholders"
import type { SystemPromptContext } from "../../types"
@@ -78,7 +77,8 @@ const RULES = (context: SystemPromptContext) => `RULES
- When using the replace_in_file tool, you must include complete lines in your SEARCH blocks, not partial lines. The system requires exact line matches and cannot match partial lines. For example, if you want to match a line containing "const x = 5;", your SEARCH block must include the entire line, not just "x = 5" or other fragments.
- When using the replace_in_file tool, if you use multiple SEARCH/REPLACE blocks, list them in the order they appear in the file. For example if you need to make changes to both line 10 and line 50, first include the SEARCH/REPLACE block for line 10, followed by the SEARCH/REPLACE block for line 50.
- When using the replace_in_file tool, Do NOT add extra characters to the markers (e.g., ------- SEARCH> is INVALID). Do NOT forget to use the closing +++++++ REPLACE marker. Do NOT modify the marker format in any way. Malformed XML will cause complete tool failure and break the entire editing process.
- You may use multiple tools in a single response when the operations are independent (e.g., reading several files, creating independent files). For dependent operations where one result informs the next, use tools sequentially and wait for the user's response.{{BROWSER_WAIT_RULES}}${hasEnabledMcpServers(context) ? "\n- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations." : ""}`
- You may use multiple tools in a single response when the operations are independent (e.g., reading several files, creating independent files). For dependent operations where one result informs the next, use tools sequentially and wait for the user's response.{{BROWSER_WAIT_RULES}}
- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.`
export const GPT_5_TEMPLATE_OVERRIDES = {
BASE,
@@ -1,4 +1,3 @@
import { hasEnabledMcpServers } from "../../components/mcp"
import { SystemPromptSection } from "../../templates/placeholders"
import type { SystemPromptContext } from "../../types"
@@ -49,22 +48,13 @@ export const BASE = `{{${SystemPromptSection.AGENT_ROLE}}}
{{${SystemPromptSection.USER_INSTRUCTIONS}}}`
const RULES = (context: SystemPromptContext) => {
const hasMcpServers = hasEnabledMcpServers(context)
const RULES = (_context: SystemPromptContext) => `RULES
return `RULES
- The current working directory is \`{{CWD}}\` - this is the directory where all the tools will be executed from.`
- The current working directory is \`{{CWD}}\` - this is the directory where all the tools will be executed from.${
context.enableParallelToolCalling
? `
- You may use multiple tools in a single response when the operations are independent (e.g., reading several files, creating independent files). For dependent operations where one result informs the next, use tools sequentially and wait for the user's response.`
: ""
}{{BROWSER_WAIT_RULES}}${hasMcpServers ? "\n- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations." : ""}`
}
const TOOL_USE = (_context: SystemPromptContext) => `TOOL USE
const TOOL_USE = (context: SystemPromptContext) => `TOOL USE
You have access to a set of tools that are executed upon the user's approval.${context.enableParallelToolCalling ? " You may use multiple tools in a single response when the operations are independent (e.g., reading several files, searching in parallel). For dependent operations where one result informs the next, use tools sequentially." : ""} You will receive the results of all tool uses in the user's response.`
You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.`
const ACT_VS_PLAN = (context: SystemPromptContext) => `ACT MODE V.S. PLAN MODE
@@ -89,7 +79,7 @@ const OBJECTIVE = (context: SystemPromptContext) => `OBJECTIVE
You accomplish a given task iteratively, breaking it down into clear steps and working through them methodically.
1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.
2. Work through these goals sequentially, utilizing available tools ${context.enableParallelToolCalling ? "as necessary. You may call multiple independent tools in a single response to work efficiently." : "one at a time as necessary."} Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. BUT, if one of the values for a required parameter is missing, DO NOT invoke the tool (not even with fillers for the missing params)${context.yoloModeToggled !== true ? " and instead, ask the user to provide the missing parameters using the ask_followup_question tool" : ""}. DO NOT ask for more information on optional parameters if it is not provided.
4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. \`open index.html\` to show the website you've built.
5. If the task is not actionable, you may use the attempt_completion tool to explain to the user why the task cannot be completed, or provide a simple answer if that is what the user is looking for.`
-3
View File
@@ -322,8 +322,6 @@ export async function readGlobalStateFromDisk(context: ExtensionContext): Promis
const openTelemetryLogMaxQueueSize =
context.globalState.get<GlobalStateAndSettings["openTelemetryLogMaxQueueSize"]>("openTelemetryLogMaxQueueSize")
const subagentsEnabled = context.globalState.get<GlobalStateAndSettings["subagentsEnabled"]>("subagentsEnabled")
const backgroundEditEnabled =
context.globalState.get<GlobalStateAndSettings["backgroundEditEnabled"]>("backgroundEditEnabled")
// Get mode-related configurations
const mode = context.globalState.get<GlobalStateAndSettings["mode"]>("mode")
@@ -684,7 +682,6 @@ export async function readGlobalStateFromDisk(context: ExtensionContext): Promis
qwenCodeOauthPath,
customPrompt,
autoCondenseThreshold: autoCondenseThreshold || 0.75, // default to 0.75 if not set
backgroundEditEnabled: backgroundEditEnabled ?? false,
// Hooks require explicit user opt-in and are only supported on macOS/Linux
hooksEnabled: getHooksEnabledSafe(hooksEnabled),
subagentsEnabled: subagentsEnabled ?? false,
+23 -21
View File
@@ -69,16 +69,12 @@ import Mutex from "p-mutex"
import pWaitFor from "p-wait-for"
import * as path from "path"
import { ulid } from "ulid"
import * as vscode from "vscode"
import type { SystemPromptContext } from "@/core/prompts/system-prompt"
import { getSystemPrompt } from "@/core/prompts/system-prompt"
import { HostProvider } from "@/hosts/host-provider"
import { FileEditProvider } from "@/integrations/editor/FileEditProvider"
import {
CommandExecutor,
CommandExecutorCallbacks,
FullCommandExecutorConfig,
StandaloneTerminalManager,
} from "@/integrations/terminal"
import { CommandExecutorCallbacks, StandaloneTerminalManager } from "@/integrations/terminal"
import { CommandExecutor, FullCommandExecutorConfig } from "@/integrations/terminal/CommandExecutor"
import { ClineError, ClineErrorType, ErrorService } from "@/services/error"
import { telemetryService } from "@/services/telemetry"
import {
@@ -276,6 +272,7 @@ export class Task {
this.cancelTask = cancelTask
this.clineIgnoreController = new ClineIgnoreController(cwd)
this.taskLockAcquired = taskLockAcquired
// Determine terminal execution mode and create appropriate terminal manager
this.terminalExecutionMode = vscodeTerminalExecutionMode || "vscodeTerminal"
@@ -299,16 +296,12 @@ export class Task {
this.urlContentFetcher = new UrlContentFetcher(controller.context)
this.browserSession = new BrowserSession(stateManager)
this.contextManager = new ContextManager()
this.diffViewProvider = HostProvider.get().createDiffViewProvider()
this.streamHandler = new StreamResponseHandler()
this.cwd = cwd
this.stateManager = stateManager
this.workspaceManager = workspaceManager
// DiffViewProvider opens Diff Editor during edits while FileEditProvider performs
// edits in the background without stealing user's editor's focus.
const backgroundEditEnabled = this.stateManager.getGlobalSettingsKey("backgroundEditEnabled")
this.diffViewProvider = backgroundEditEnabled ? new FileEditProvider() : HostProvider.get().createDiffViewProvider()
// Set up MCP notification callback for real-time notifications
this.mcpHub.setNotificationCallback(async (serverName: string, _level: string, message: string) => {
// Display notification in chat immediately
@@ -513,7 +506,7 @@ export class Task {
},
updateBackgroundCommandState: (isRunning: boolean) =>
this.controller.updateBackgroundCommandState(isRunning, this.taskId),
updateClineMessage: async (index: number, updates: { commandCompleted?: boolean; text?: string }) => {
updateClineMessage: async (index: number, updates: { commandCompleted?: boolean }) => {
await this.messageStateHandler.updateClineMessage(index, updates)
},
getClineMessages: () => this.messageStateHandler.getClineMessages() as Array<{ ask?: string; say?: string }>,
@@ -1607,6 +1600,21 @@ export class Task {
}
}
/**
* Migrates the disableBrowserTool setting from VSCode configuration to browserSettings
*/
private async migrateDisableBrowserToolSetting(): Promise<void> {
const config = vscode.workspace.getConfiguration("cline")
const disableBrowserTool = config.get<boolean>("disableBrowserTool")
if (disableBrowserTool !== undefined) {
const browserSettings = this.stateManager.getGlobalSettingsKey("browserSettings")
browserSettings.disableToolUse = disableBrowserTool
// Remove from VSCode configuration
await config.update("disableBrowserTool", undefined, true)
}
}
private getCurrentProviderInfo(): ApiProviderInfo {
const model = this.api.getModel()
const apiConfig = this.stateManager.getApiConfiguration()
@@ -1697,6 +1705,7 @@ export class Task {
const providerInfo = this.getCurrentProviderInfo()
const ide = (await HostProvider.env.getHostVersion({})).platform || "Unknown"
await this.migrateDisableBrowserToolSetting()
const browserSettings = this.stateManager.getGlobalSettingsKey("browserSettings")
const disableBrowserTool = browserSettings.disableToolUse ?? false
// cline browser tool uses image recognition for navigation (requires model image support).
@@ -1783,8 +1792,6 @@ export class Task {
isSubagentsEnabledAndCliInstalled,
isCliSubagent,
enableNativeToolCalls: this.stateManager.getGlobalStateKey("nativeToolCallEnabled"),
enableParallelToolCalling: this.stateManager.getGlobalSettingsKey("enableParallelToolCalling"),
terminalExecutionMode: this.terminalExecutionMode,
}
const { systemPrompt, tools } = await getSystemPrompt(promptContext)
@@ -2218,12 +2225,7 @@ export class Task {
// Now, if it's the first request AND checkpoints are enabled AND tracker was successfully initialized,
// then say "checkpoint_created" and perform the commit.
if (
isFirstRequest &&
this.stateManager.getGlobalSettingsKey("enableCheckpointsSetting") &&
this.checkpointManager &&
!this.taskState.checkpointManagerErrorMessage
) {
if (isFirstRequest && this.stateManager.getGlobalSettingsKey("enableCheckpointsSetting") && this.checkpointManager) {
await this.say("checkpoint_created") // Now this is conditional
const lastCheckpointMessageIndex = findLastIndex(
this.messageStateHandler.getClineMessages(),
@@ -43,6 +43,10 @@ export class ApplyPatchHandler implements IFullyManagedTool {
private config?: TaskConfig
private pathResolver?: PathResolver
private providerOps?: FileProviderOperations
private partialPreviewState?: {
originalFiles: Record<string, string>
currentPreviewPath?: string
}
constructor(private validator: ToolValidator) {}
@@ -81,11 +85,19 @@ export class ApplyPatchHandler implements IFullyManagedTool {
}
}
private ensurePartialPreviewState(): { originalFiles: Record<string, string>; currentPreviewPath?: string } {
if (!this.partialPreviewState) {
this.partialPreviewState = { originalFiles: {} }
}
return this.partialPreviewState
}
private async previewPatchStream(rawInput: string, uiHelpers: StronglyTypedUIHelpers): Promise<void> {
const config = uiHelpers.getConfig()
const provider = config.services.diffViewProvider
this.initializeHelpers(config)
const state = this.ensurePartialPreviewState()
const lines = this.stripBashWrapper(rawInput.split("\n"))
// Extract the first operation path and type
@@ -199,6 +211,12 @@ export class ApplyPatchHandler implements IFullyManagedTool {
if (stream.content === undefined) {
return
}
try {
await provider.update(stream.content, false)
} catch {
// Ignore streaming errors
}
}
async execute(config: TaskConfig, block: ToolUse): Promise<ToolResponse> {
@@ -220,6 +238,7 @@ export class ApplyPatchHandler implements IFullyManagedTool {
// Ignore reset errors
}
}
this.partialPreviewState = undefined
try {
const lines = this.preprocessLines(rawInput)
@@ -2,7 +2,7 @@ import type { ToolUse } from "@core/assistant-message"
import {
buildDiffContent,
type ChangedFile,
detectBinaryFile,
isBinaryFile,
openDiffView,
setupCommentController,
streamAIExplanationComments,
@@ -160,7 +160,7 @@ export class GenerateExplanationToolHandler implements IToolHandler, IPartialBlo
const absolutePath = path.join(cwd, filePath)
// Skip binary files - they can't be displayed properly in diff view
if (await detectBinaryFile(absolutePath)) {
if (isBinaryFile(filePath)) {
continue
}
@@ -415,11 +415,6 @@ export class WriteToFileToolHandler implements IFullyManagedTool {
!block.partial, // Pass the partial flag correctly
)
} catch (error) {
// As we set the didAlreadyUseTool flag when the tool has failed once, we don't want to add the error message to the
// userMessages array again on each new streaming chunk received.
if (!config.enableParallelToolCalling && config.taskState.didAlreadyUseTool) {
return
}
// Full original behavior - comprehensive error handling even for partial blocks
await config.callbacks.say("diff_error", relPath)
+2 -2
View File
@@ -228,7 +228,7 @@ function createAuthSucceededHtml(redirectUri?: string): string {
<title>Cline - Authentication Success</title>
${redirect}
<style>
@import url('https://fonts.googleapis.com/css2?family=Azeret:wght@300;400;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Azeret+Mono:wght@300;400;700&display=swap');
* {
margin: 0;
@@ -237,7 +237,7 @@ function createAuthSucceededHtml(redirectUri?: string): string {
}
body {
font-family: 'Azeret', sans-serif;
font-family: 'Azeret Mono', monospace;
background-color: #ffffff;
color: #333333;
height: 100vh;
+2 -6
View File
@@ -1,15 +1,11 @@
import * as vscode from "vscode"
import { ErrorSettings } from "@/services/error"
import { EmptyRequest } from "@/shared/proto/index.cline"
import { GetTelemetrySettingsResponse, Setting } from "@/shared/proto/index.host"
export async function getTelemetrySettings(_: EmptyRequest): Promise<GetTelemetrySettingsResponse> {
const config = vscode.workspace.getConfiguration("telemetry")
const errorLevel = config?.get<ErrorSettings["level"]>("telemetryLevel") || "all"
if (vscode.env.isTelemetryEnabled) {
return { isEnabled: Setting.ENABLED, errorLevel }
return { isEnabled: Setting.ENABLED }
} else {
return { isEnabled: Setting.DISABLED, errorLevel }
return { isEnabled: Setting.DISABLED }
}
}

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