Compare commits
396 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd2d8f98a7 | |||
| 333468c9b6 | |||
| 0da6ddc001 | |||
| 612130366f | |||
| 85206cfded | |||
| 6f8ed7aa56 | |||
| f2c130a69e | |||
| c870bfb454 | |||
| 436bfdb535 | |||
| e2a3652d26 | |||
| 632aca225c | |||
| a1eaccdaef | |||
| f7e76f9bee | |||
| 0d04205dc4 | |||
| 4b9dbf11a0 | |||
| c5afbd743e | |||
| 6d1bfc3a1b | |||
| d18486a199 | |||
| 7417b1b671 | |||
| e4fbe331f4 | |||
| d122683e91 | |||
| fdc96d0545 | |||
| c0241f4060 | |||
| c3f523427b | |||
| 428e465d4d | |||
| 46ab08c9f8 | |||
| 0fd30e0d20 | |||
| e87a765773 | |||
| e80c3d3cd8 | |||
| 80b6ab6046 | |||
| 5155123b80 | |||
| 30af711d5e | |||
| b218b25e0b | |||
| bf27aa53ef | |||
| 4644a7159a | |||
| 63c63f38f5 | |||
| f1d8ca423d | |||
| 16b8ce7e44 | |||
| 0bbb4299ba | |||
| 197b17b3bd | |||
| 591b840531 | |||
| c35ba37402 | |||
| 850a6ab841 | |||
| 99c85710e9 | |||
| 9fc9785aa2 | |||
| 5c02f6eb13 | |||
| ac01e69555 | |||
| dbc18a8fce | |||
| b77398390b | |||
| c20052de00 | |||
| 1dac507b63 | |||
| e5aa48c2b5 | |||
| 63efb4aa5d | |||
| 5fcf83b627 | |||
| 0b308d610e | |||
| 60d3048aa0 | |||
| aed3ac6597 | |||
| 86e2a3e7ce | |||
| 47856c70d2 | |||
| f1a84ddbde | |||
| ebcc927cc7 | |||
| 1e108d87e0 | |||
| 7b62d7786e | |||
| 042f5c9823 | |||
| 2f8a4525a4 | |||
| 20774a4187 | |||
| be5bda2740 | |||
| 261fd9036f | |||
| 2334d4d531 | |||
| 6390d854f7 | |||
| 7d5c56a55a | |||
| 0dc760ac03 | |||
| 8c1241c8cd | |||
| 17686ae3d9 | |||
| 97460d2952 | |||
| e629ed0ef6 | |||
| 96788e9127 | |||
| d7716a514d | |||
| 7aaa5966d6 | |||
| bb1d068139 | |||
| 450945ae0e | |||
| 191e9635bd | |||
| c13a7a80b3 | |||
| d4a4adfa5f | |||
| 557e20224e | |||
| 0e9a326a6a | |||
| f5ecb6db0c | |||
| 8f1405b881 | |||
| 8a9e03c8ff | |||
| edba02b45e | |||
| cc36c67fc9 | |||
| d77032bc8a | |||
| 608dde94b3 | |||
| 47ff7c1620 | |||
| 1b7f971c34 | |||
| 12eadd3378 | |||
| b3e0ef9ed7 | |||
| fb94d8d3d4 | |||
| d11bd15d60 | |||
| 31c48898a6 | |||
| 5c9901d68a | |||
| 45b79dc3d7 | |||
| 26b6c7bdb6 | |||
| d0678a2ad1 | |||
| 09276ebf43 | |||
| af8b51b189 | |||
| 3e6b3f252b | |||
| f019c365a6 | |||
| f6fe843cfb | |||
| 01f26c21ae | |||
| 031c2f5b05 | |||
| c999e269db | |||
| 032c1bf792 | |||
| fa73d60b7a | |||
| 841bb7f1d7 | |||
| f01428884d | |||
| 2ce5548250 | |||
| 9f32cd247f | |||
| ace48198cc | |||
| 9f0240dfd7 | |||
| 5530cfe375 | |||
| af4d99e0bc | |||
| cd011a0e4a | |||
| d44184ab03 | |||
| 5d96704e92 | |||
| 49812eb332 | |||
| 57b72519ae | |||
| 1ed4d00a16 | |||
| c090f5b1a7 | |||
| 3c917ec99d | |||
| b315be397d | |||
| dc9e7916de | |||
| cb9d1e81b8 | |||
| eb5a452c9c | |||
| 6a90294a2a | |||
| 2e1334c10a | |||
| 18b77ee5e5 | |||
| 4bf8feecac | |||
| 2f3450f667 | |||
| 3e89c28727 | |||
| d065ac7b37 | |||
| 5a0a25601a | |||
| 48d822a030 | |||
| 417104505c | |||
| e21d3ff1bb | |||
| da5477f891 | |||
| 4903dfcb6e | |||
| 37b2f7fbc9 | |||
| 11fbe4b21d | |||
| e0844ac6e2 | |||
| f28d760675 | |||
| 8363d090e0 | |||
| 3409fa7442 | |||
| a20685d289 | |||
| 926c5e189e | |||
| 2a5ca9d312 | |||
| d06717342b | |||
| c904cfe376 | |||
| 924ca1278c | |||
| 2b0c0a659d | |||
| f4477e229d | |||
| 5207d5c68e | |||
| 51b535e2d6 | |||
| 4db61b1b36 | |||
| 6baf611322 | |||
| 97d635d606 | |||
| 8ce476ccaa | |||
| bce403476e | |||
| ac306c3719 | |||
| 1d8a9bf11e | |||
| d811a9d5f3 | |||
| 3abeff0379 | |||
| 2763266424 | |||
| 0c3dfb6aed | |||
| 0e73cd4c9f | |||
| 6bbe5c2499 | |||
| 00e9d6f523 | |||
| 010fd71140 | |||
| 17a95f0021 | |||
| 27c9971774 | |||
| 644d06c487 | |||
| 1be314dfed | |||
| 91940fbb4a | |||
| 279e371cf5 | |||
| 4eda267981 | |||
| 3441363805 | |||
| 5389be991e | |||
| 41fd610203 | |||
| e1e7470fdd | |||
| 2f0e0ee56d | |||
| 7f89ebf276 | |||
| 99ed1b7e86 | |||
| 45c9fcf575 | |||
| 4f73f4460b | |||
| fb872fd316 | |||
| 5292242a8e | |||
| 78b8aed50f | |||
| d01f7b4618 | |||
| dec215cd9c | |||
| 8ce7e132d2 | |||
| 6ab008b204 | |||
| 769523998d | |||
| 64a6bcc39b | |||
| 19ceafd4a4 | |||
| 4b5218b3b8 | |||
| b68d716675 | |||
| f820c6d00a | |||
| 1fcbbaa9d0 | |||
| 4005df6eae | |||
| 0e3cdab82b | |||
| ada6b0c955 | |||
| 921dd2ec8c | |||
| 4b212994a8 | |||
| 3fe3c866ea | |||
| e9136f60cc | |||
| 34bb95e04e | |||
| 7b65db55f1 | |||
| 07ebc2e4bd | |||
| 0193179597 | |||
| 20c0783c97 | |||
| 4df486fa5b | |||
| 5fc6d4e9e3 | |||
| 00d3bd8316 | |||
| 81d4ff947f | |||
| bcb368d097 | |||
| c1ded819bb | |||
| de02befb2f | |||
| 6617901078 | |||
| 31c7176de1 | |||
| 3c97c8dc19 | |||
| 187e40d2da | |||
| 8c38b1ccf8 | |||
| 4947a745c2 | |||
| 9e8c6df6b2 | |||
| 6586195b2d | |||
| 1aa944eff7 | |||
| 8e7b1c6884 | |||
| ee154826b6 | |||
| 852f307268 | |||
| 4e3fe004f4 | |||
| 2b63eed85e | |||
| 2ffdc50ea1 | |||
| 74808431e5 | |||
| 7a523fbaf6 | |||
| c22ea39dc1 | |||
| c9f23076c2 | |||
| a5f6c1d732 | |||
| 3c37a160ac | |||
| 5c3294051f | |||
| 4c2f28f2af | |||
| 6e016298cb | |||
| 0cd7bebfba | |||
| 363aac61fb | |||
| eeb1cc7da8 | |||
| f760f13de5 | |||
| dd52a4a39c | |||
| 639edb5db6 | |||
| 3eac9b04de | |||
| 09692d7d3a | |||
| c81fa0a9d6 | |||
| 326c9c9f99 | |||
| a4518b90c2 | |||
| 79f4d938e6 | |||
| 37152329cd | |||
| 1332d1d70d | |||
| 6a0d92d683 | |||
| e761a8c252 | |||
| c26d0a076d | |||
| c037619b90 | |||
| a575a76e8f | |||
| b1d15d4fe7 | |||
| a0708e57ee | |||
| ab5796fa72 | |||
| b15c364a62 | |||
| 29dcc4e1e1 | |||
| 126d066893 | |||
| fa3e095a79 | |||
| 4033c83b51 | |||
| 0b7ea86e9b | |||
| 1d9a0b5986 | |||
| c47ffe2861 | |||
| e85d918816 | |||
| 49be10ead8 | |||
| aa9573fb0a | |||
| b913e47332 | |||
| 5be7a1b3cf | |||
| c2e91aa9c9 | |||
| 68b93fcbea | |||
| 0b0e8c36cb | |||
| b0bd0e3974 | |||
| 42b7a1e450 | |||
| 14ccf33d25 | |||
| d9a340523c | |||
| 87b3e79b90 | |||
| 60f2e85fc7 | |||
| 1826d98019 | |||
| af69b30a36 | |||
| e62fbf6b0c | |||
| 64254fc97a | |||
| c312c4aef6 | |||
| fab49e810b | |||
| 2a20523e16 | |||
| 06585821d1 | |||
| 9e802b11da | |||
| afb77c5a8d | |||
| 0a4811222f | |||
| b4ce378e4b | |||
| 2f60a898af | |||
| 8ffd82eda3 | |||
| 81276fdf85 | |||
| 164e11aae1 | |||
| 9792f174b1 | |||
| a590200c64 | |||
| 297a45d73a | |||
| e84de0ab3c | |||
| 515cb81439 | |||
| 550428eabd | |||
| 22c22a1cfc | |||
| 8202479cec | |||
| bcbaa4518d | |||
| 9d799643ba | |||
| 6271c5da37 | |||
| 89aeb3db3d | |||
| 0caeea1b37 | |||
| 852a7c9198 | |||
| c4ef472aeb | |||
| e22c457d19 | |||
| c15287ace0 | |||
| d30f54a89c | |||
| 56b913d951 | |||
| 55a30e0ffa | |||
| a017f3dfd3 | |||
| 41ebe7c9d1 | |||
| 4d11f0d2fa | |||
| 4baa2474eb | |||
| c94e2cf913 | |||
| dbedc6cfaa | |||
| 2ac568e649 | |||
| b13d0e75ea | |||
| 4d395deefd | |||
| 834a5b1df2 | |||
| 3089233298 | |||
| f2b7347a5c | |||
| 04bfef75cf | |||
| 716e8f236b | |||
| f2ddab71f1 | |||
| 0b56a45a65 | |||
| d6ebd2438a | |||
| 4c07c7e5c5 | |||
| ab66a5fd93 | |||
| bd1d6159fc | |||
| 7f2d28716f | |||
| 653727db2a | |||
| d3c2f1878d | |||
| ba92be9401 | |||
| 66eb5a62ba | |||
| 499ee22b3b | |||
| 7abeae5019 | |||
| accf47cb52 | |||
| 556d3e6f79 | |||
| 9e35048db2 | |||
| 9b0f2b82ef | |||
| c2c23054b9 | |||
| 3baaa5c8b4 | |||
| abafcc7290 | |||
| d82aa0add9 | |||
| abe4721a0b | |||
| 60d55b69a8 | |||
| d18e0271d3 | |||
| af71f9da90 | |||
| 2a1c8826aa | |||
| 9a54f2d246 | |||
| d928d58a40 | |||
| 31859b5fda | |||
| 0d5d89e8c7 | |||
| af34451eec | |||
| 027a4f6386 | |||
| 49642882c5 | |||
| 21ed6bc432 | |||
| da2689f885 | |||
| 5049326f02 | |||
| b02ce46a57 | |||
| de974737c8 | |||
| d072156e9a | |||
| 4939309a09 | |||
| 1a07ca7906 | |||
| c1eefbad3f | |||
| 1bfdce9b84 | |||
| b002cdacdb | |||
| cf4005b25e | |||
| 1494d145d5 | |||
| 1ab4b3cc24 | |||
| 535b653228 | |||
| 1335fa5452 | |||
| b2a4395f71 | |||
| ae34a3a8c5 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
fix: Verify selected index is not -1 when checking if an option is selectable in the context menu
|
||||
@@ -0,0 +1 @@
|
||||
../../.clinerules/workflows/hotfix-release.md
|
||||
@@ -0,0 +1 @@
|
||||
../../.clinerules/workflows/release.md
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/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!"
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/claude-code-for-web-setup.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
# Networking & Proxy Support
|
||||
|
||||
To ensure Cline works correctly in all environments (VSCode, JetBrains, CLI) and with various network configurations (especially corporate proxies), strictly follow these guidelines for all network activity.
|
||||
|
||||
In extension code, do NOT use the global `fetch` or a default `axios` instance. (Note, `shared/net.ts` is exempt from these rules because it sets up the fetch wrappers.) In Webview code, you SHOULD use global `fetch`.
|
||||
|
||||
Global `fetch` and default `axios` do not automatically pick up proxy configurations in all environments (specifically JetBrains and CLI). You MUST use the provided utilities in `@/shared/net` which handle proxy agent configuration. In the webview, the browser/embedder handles proxies.
|
||||
|
||||
## Guidelines
|
||||
|
||||
### 1. Using `fetch`
|
||||
|
||||
Instead of `fetch(...)`, import the proxy-aware wrapper:
|
||||
|
||||
```typescript
|
||||
import { fetch } from '@/shared/net'
|
||||
|
||||
// Usage is identical to global fetch
|
||||
const response = await fetch('https://api.example.com/data')
|
||||
```
|
||||
|
||||
### 2. Using `axios`
|
||||
|
||||
When using `axios`, you must apply the settings from `getAxiosSettings()`:
|
||||
|
||||
```typescript
|
||||
import axios from 'axios'
|
||||
import { getAxiosSettings } from '@/shared/net'
|
||||
|
||||
const response = await axios.get('https://api.example.com/data', {
|
||||
headers: { 'Authorization': '...' },
|
||||
...getAxiosSettings() // <--- CRITICAL: Injects the proxy agent if needed
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Third-Party Clients (OpenAI, Ollama, etc.)
|
||||
|
||||
Most API client libraries allow you to customize the `fetch` implementation. You **MUST** pass the proxy-aware `fetch` to these clients.
|
||||
|
||||
**Example (OpenAI):**
|
||||
```typescript
|
||||
import OpenAI from "openai"
|
||||
import { fetch } from "@/shared/net"
|
||||
|
||||
this.client = new OpenAI({
|
||||
apiKey: '...',
|
||||
fetch, // <--- CRITICAL: Pass our fetch wrapper
|
||||
})
|
||||
```
|
||||
|
||||
### 4. Tests
|
||||
|
||||
Use `mockFetchForTesting` to mock the underlying fetch implementation.
|
||||
|
||||
**Example (callback):**
|
||||
|
||||
```
|
||||
import { mockFetchForTesting } from "@/shared/net"
|
||||
|
||||
...
|
||||
let mockFetch = ...
|
||||
mockFetchForTesting(mockFetch, () => {
|
||||
// This calls mockFetch
|
||||
fetch('https://foo.example').then(...)
|
||||
})
|
||||
// Original fetch is restored immediately when the call returns.
|
||||
```
|
||||
|
||||
**Example (Promise):**
|
||||
|
||||
```
|
||||
import { mockFetchForTesting } from "@/shared/net"
|
||||
|
||||
...
|
||||
let mockFetch = ...
|
||||
await mockFetchForTesting(mockFetch, async () => {
|
||||
await ...
|
||||
// This calls mockFetch
|
||||
await fetch('https://foo.example')
|
||||
...
|
||||
})
|
||||
// Original fetch is restored when the Promise from the callback settles
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
If you are adding a new network call or integration:
|
||||
1. Check `@/shared/net.ts` is imported.
|
||||
2. Ensure `fetch` or `getAxiosSettings` is being used.
|
||||
3. Verify that third-party clients are configured to use the custom fetch.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Address PR Comments
|
||||
|
||||
Review and address all comments on the current branch's PR.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Get the current branch name and find the associated PR:
|
||||
```bash
|
||||
gh pr view --json number,title,body
|
||||
```
|
||||
|
||||
2. Understand the PR context:
|
||||
- Get the full diff: `git diff origin/main...HEAD`
|
||||
- Read the changed files to understand what the PR is doing
|
||||
- Read related files if needed to understand the broader context
|
||||
- Understand the intent and spirit of the changes, not just the code
|
||||
|
||||
3. Fetch all PR comments:
|
||||
- Inline comments: `gh api repos/{owner}/{repo}/pulls/{pr_number}/comments`
|
||||
- General comments: `gh pr view {pr_number} --json comments,reviews`
|
||||
|
||||
4. Present a summary of all comments with your recommendation for each (apply, skip, or respond). Ignore bot noise (changeset-bot, CI status, etc.).
|
||||
|
||||
5. **Wait for my approval** before proceeding.
|
||||
|
||||
6. After approval:
|
||||
- Apply code changes and commit
|
||||
- Reply to comments that were addressed or intentionally skipped
|
||||
- Push commits
|
||||
@@ -0,0 +1,49 @@
|
||||
# Find Best Reviewers for Current Branch
|
||||
|
||||
Analyze my current branch to find the best people to review my PR based on **domain expertise** and git history.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Get the current branch name and verify it's not `main`
|
||||
2. Get the diff between the current branch and `origin/main`:
|
||||
- Use `git diff origin/main...HEAD --name-only` to get changed files
|
||||
- Use `git diff origin/main...HEAD` to understand the nature/spirit of the changes
|
||||
3. **Identify the domain/feature area** being changed:
|
||||
- Read the diff carefully to understand WHAT is being changed conceptually (e.g., "slash commands", "authentication", "API client", "UI components")
|
||||
- This semantic understanding is crucial for finding the right reviewers
|
||||
4. Find domain experts by searching for related files and their contributors:
|
||||
- Identify all files related to the feature/domain (not just the ones changed)
|
||||
- Example: if changing slash commands, find ALL slash-command related files across the codebase
|
||||
- Use `git log --format="%an <%ae>" -- <related-files-pattern>` to find who has expertise in that domain
|
||||
5. For additional context, also gather:
|
||||
- `git blame -L <start>,<end> origin/main -- <file-path>` for exact lines changed
|
||||
- Recent commit activity on related files
|
||||
6. Score and rank contributors by:
|
||||
- **Highest weight: Domain expertise** - who has the most commits to files in this feature area (even files not touched by this PR)
|
||||
- **Medium weight: Direct file expertise** - commits to the specific files being changed
|
||||
- **Lower weight: Line-level ownership** - authored the exact lines being modified
|
||||
7. Exclude myself (check against my git config user.email)
|
||||
8. Present the top 5 reviewers as an ordered list
|
||||
|
||||
## Output Format
|
||||
|
||||
Output an ordered list:
|
||||
|
||||
1. **Name** - Domain expert: 15 commits to slash-command related files, authored core parsing logic
|
||||
2. **Name** - 8 commits to affected files, recently added the feature being modified
|
||||
3. ...
|
||||
|
||||
## Commands Reference
|
||||
```bash
|
||||
git config user.email
|
||||
git diff origin/main...HEAD --name-only
|
||||
git diff origin/main...HEAD
|
||||
# Find related files for a domain (adjust pattern based on what you learn from the diff)
|
||||
find . -type f \( -name "*slash-command*" -o -name "*SlashCommand*" \) | head -20
|
||||
# Get contributors for related files
|
||||
find . -type f \( -name "*slash-command*" -o -name "*SlashCommand*" \) -print0 | xargs -0 git log --format="%an <%ae>" -- | sort | uniq -c | sort -rn
|
||||
git log --format="%an <%ae>" -- <file> | sort | uniq -c | sort -rn
|
||||
git blame -L 10,20 origin/main -- <file>
|
||||
```
|
||||
|
||||
Do NOT ask questions - analyze the changes, identify the domain, and output the reviewer list.
|
||||
@@ -0,0 +1,194 @@
|
||||
# Hotfix Release
|
||||
|
||||
Create a hotfix release by cherry-picking specific commits from main onto the latest release tag.
|
||||
|
||||
## Overview
|
||||
|
||||
This workflow helps you:
|
||||
1. Select specific commits from main to include in a hotfix
|
||||
2. Create a release notes commit on main (changelog + version bump)
|
||||
3. Cherry-pick everything onto the latest release tag
|
||||
4. Tag and push the new release
|
||||
|
||||
## Step 1: Setup and Gather Information
|
||||
|
||||
First, ensure we're on main and up to date:
|
||||
|
||||
```bash
|
||||
git checkout main && git pull origin main
|
||||
```
|
||||
|
||||
Get the latest release tag:
|
||||
|
||||
```bash
|
||||
git tag --sort=-v:refname | head -1
|
||||
```
|
||||
|
||||
## Step 2: Present Commits Since Last Release
|
||||
|
||||
Show all commits on main since the last release tag:
|
||||
|
||||
```bash
|
||||
LAST_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
git log ${LAST_TAG}..HEAD --oneline --format="%h %s (%an)"
|
||||
```
|
||||
|
||||
Also get the commit messages already on the tag (to identify previously cherry-picked commits). Note: Run these as separate commands to avoid shell parsing issues with parentheses in author names:
|
||||
|
||||
```bash
|
||||
LAST_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
PREV_TAG=$(git tag --sort=-v:refname | head -2 | tail -1)
|
||||
```
|
||||
|
||||
```bash
|
||||
git log $PREV_TAG..$LAST_TAG --oneline --format="%s"
|
||||
```
|
||||
|
||||
**Present the list** to the user in a numbered format with commit hash, subject, and author. For any commits whose subject line already appears in the tag's history (previously cherry-picked in an earlier hotfix) or are "Release Notes" commits, add `(already in previous hotfix)` or `(release notes - skip)` after them so the user knows to skip those.
|
||||
|
||||
Ask which commits to include in the hotfix.
|
||||
|
||||
Use the ask_followup_question tool to let the user specify which commits they want (by number or hash).
|
||||
|
||||
## Step 3: Analyze Selected Commits
|
||||
|
||||
For each selected commit:
|
||||
1. Get the full commit message: `git show --no-patch --format="%B" <hash>`
|
||||
2. Get the diff to understand the change: `git show <hash> --stat`
|
||||
3. Find the associated PR if any: `gh pr list --search "<hash>" --state merged --json number,title --jq '.[0]'`
|
||||
|
||||
Build a mental model of what these changes do for the changelog.
|
||||
|
||||
## Step 4: Determine New Version Number
|
||||
|
||||
Parse the current version from package.json and the last tag:
|
||||
|
||||
```bash
|
||||
LAST_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
echo "Last release: $LAST_TAG"
|
||||
cat package.json | grep '"version"'
|
||||
```
|
||||
|
||||
Hotfixes always increment the patch version (e.g., 3.40.0 -> 3.40.1, or 3.40.1 -> 3.40.2).
|
||||
|
||||
**Ask the user to confirm the new version number.**
|
||||
|
||||
## Step 5: Create Release Notes Commit on Main
|
||||
|
||||
On the main branch, create a commit that updates:
|
||||
|
||||
1. **CHANGELOG.md** - Add a new section for the hotfix version at the top:
|
||||
```markdown
|
||||
## [3.40.1]
|
||||
|
||||
- Description of fix 1
|
||||
- Description of fix 2
|
||||
```
|
||||
|
||||
Write clear, user-friendly descriptions based on your analysis of the commits.
|
||||
|
||||
2. **package.json** - Update the version field to the new version
|
||||
|
||||
3. **Delete changesets** for the commits being included in the hotfix. This prevents the changeset bot from including duplicate entries in the next regular release.
|
||||
|
||||
Find and delete the changeset files associated with the selected commits:
|
||||
```bash
|
||||
ls .changeset/
|
||||
```
|
||||
|
||||
Each changeset file in `.changeset/` corresponds to a PR. Read them to identify which ones belong to the commits you're hotfixing, then delete those files.
|
||||
|
||||
**Skip running `npm run install:all`** - the automation handles outdated lockfiles.
|
||||
|
||||
Commit with message format: `v{VERSION} Release Notes (hotfix)`
|
||||
|
||||
In the commit body, mention:
|
||||
- This is for a hotfix release
|
||||
- List the cherry-picked commits that will be included
|
||||
|
||||
```bash
|
||||
git add CHANGELOG.md package.json .changeset/
|
||||
git commit -m "v3.40.1 Release Notes (hotfix)
|
||||
|
||||
Hotfix release including:
|
||||
- <commit1-hash>: <description>
|
||||
- <commit2-hash>: <description>
|
||||
"
|
||||
```
|
||||
|
||||
Push to main:
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## Step 6: Build the Hotfix on the Tag
|
||||
|
||||
Checkout the last release tag (detached HEAD):
|
||||
|
||||
```bash
|
||||
LAST_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
git checkout $LAST_TAG
|
||||
```
|
||||
|
||||
Cherry-pick the selected commits in order:
|
||||
|
||||
```bash
|
||||
git cherry-pick <commit1-hash>
|
||||
git cherry-pick <commit2-hash>
|
||||
# ... etc
|
||||
```
|
||||
|
||||
Finally, cherry-pick the release notes commit you just pushed to main:
|
||||
|
||||
```bash
|
||||
# Get the hash of the release notes commit (should be HEAD of main)
|
||||
RELEASE_NOTES_COMMIT=$(git rev-parse main)
|
||||
git cherry-pick $RELEASE_NOTES_COMMIT
|
||||
```
|
||||
|
||||
## Step 7: Tag and Push
|
||||
|
||||
After all cherry-picks are applied successfully:
|
||||
|
||||
```bash
|
||||
# Tag the new release
|
||||
git tag v{VERSION}
|
||||
|
||||
# Push the tag to remote
|
||||
git push origin v{VERSION}
|
||||
```
|
||||
|
||||
## Step 8: Return to Main and Summary
|
||||
|
||||
Return to main branch:
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
```
|
||||
|
||||
**Copy a Slack announcement message to clipboard** with the version and PR links for each included fix:
|
||||
|
||||
```
|
||||
VS Code Hotfix v{VERSION} Published
|
||||
|
||||
- Description of fix 1 https://github.com/cline/cline/pull/{PR_NUMBER}
|
||||
- Description of fix 2 https://github.com/cline/cline/pull/{PR_NUMBER}
|
||||
```
|
||||
|
||||
Present a final summary:
|
||||
- New version: v{VERSION}
|
||||
- Tag pushed: yes
|
||||
- Commits included: (list them)
|
||||
- Slack message copied to clipboard: yes
|
||||
|
||||
Remind the user to:
|
||||
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/publish.yml (paste `v{VERSION}` as the tag)
|
||||
2. Post the Slack message to announce the hotfix
|
||||
|
||||
## Important Notes
|
||||
|
||||
- This workflow does NOT create a release branch - only tags
|
||||
- The release notes commit goes to main first, then gets cherry-picked to the tag
|
||||
- This keeps main's history accurate while allowing hotfix releases from tags
|
||||
- If cherry-pick conflicts occur, resolve them before continuing
|
||||
@@ -0,0 +1,232 @@
|
||||
# Release
|
||||
|
||||
Prepare and publish a release from the open changeset PR.
|
||||
|
||||
## Overview
|
||||
|
||||
This workflow helps you:
|
||||
1. Find and checkout the open changeset PR
|
||||
2. Clean up the changelog (fix version format, wordsmith entries)
|
||||
3. Push changes back to the PR branch
|
||||
4. Merge with proper commit message format
|
||||
5. Tag and push the release (after verifying the commit)
|
||||
6. Trigger the publish workflow
|
||||
7. Update GitHub release notes
|
||||
8. Provide final summary with Slack announcement
|
||||
|
||||
## Step 1: Find the Changeset PR
|
||||
|
||||
Look for the open changeset PR:
|
||||
|
||||
```bash
|
||||
gh pr list --search "Changeset version bump" --state open --json number,title,headRefName,url
|
||||
```
|
||||
|
||||
If no PR is found, inform the user there's no changeset PR ready. They may need to:
|
||||
- Merge PRs with changesets to main first
|
||||
- Manually trigger the Changeset Converter workflow at: https://github.com/cline/cline/actions/workflows/changeset-converter.yml
|
||||
|
||||
## Step 2: Gather PR Information
|
||||
|
||||
Get the PR details:
|
||||
|
||||
```bash
|
||||
PR_NUMBER=<number from step 1>
|
||||
gh pr view $PR_NUMBER --json body,files,headRefName
|
||||
```
|
||||
|
||||
Checkout the PR branch:
|
||||
|
||||
```bash
|
||||
git fetch origin changeset-release/main
|
||||
git checkout changeset-release/main
|
||||
```
|
||||
|
||||
If the branch has diverged from remote, reset to the remote version:
|
||||
|
||||
```bash
|
||||
git reset --hard origin/changeset-release/main
|
||||
```
|
||||
|
||||
## Step 3: Analyze the Changes
|
||||
|
||||
Read the current CHANGELOG.md to see what the automation generated:
|
||||
|
||||
```bash
|
||||
head -50 CHANGELOG.md
|
||||
```
|
||||
|
||||
Get the version from package.json:
|
||||
|
||||
```bash
|
||||
cat package.json | grep '"version"'
|
||||
```
|
||||
|
||||
**Present to the user:**
|
||||
- The version number that will be released
|
||||
- The raw changelog entries from the changeset PR
|
||||
- Whether this is a patch, minor, or major release
|
||||
|
||||
## Step 4: Clean Up the Changelog
|
||||
|
||||
The changelog needs these fixes:
|
||||
|
||||
1. **Add brackets to version number**: Change `## 3.44.1` to `## [3.44.1]`
|
||||
|
||||
2. **No category headers**: Don't use `### Added`, `### Fixed`, etc. Just a flat list of bullet points.
|
||||
|
||||
3. **Order entries from most important to least important**:
|
||||
- Lead with major new features or significant fixes users care about
|
||||
- End with minor fixes or internal changes
|
||||
|
||||
4. **Write user-friendly descriptions**:
|
||||
- This is for end users, not developers—explain what changed in plain language
|
||||
- Remove commit hashes from the beginning of lines (the automation adds these)
|
||||
- Look at the actual commit diffs (`git show <hash>`) and PRs to understand what changed
|
||||
- Write colorful descriptions that explain the value and impact, not just technical details
|
||||
- Consolidate related changes into single entries when appropriate
|
||||
|
||||
**Ask the user** to review the proposed changelog changes before applying them. Show them:
|
||||
- Current (raw) changelog section
|
||||
- Proposed (cleaned) changelog section
|
||||
|
||||
Once approved, apply the changes to CHANGELOG.md.
|
||||
|
||||
## Step 5: Commit and Push Changes
|
||||
|
||||
After making changelog edits:
|
||||
|
||||
```bash
|
||||
git add CHANGELOG.md
|
||||
git commit -m "Clean up changelog formatting"
|
||||
git push origin changeset-release/main
|
||||
```
|
||||
|
||||
## Step 6: Merge the PR
|
||||
|
||||
**Ask the user to confirm** they're ready to merge.
|
||||
|
||||
Merge the PR with the proper commit message format:
|
||||
|
||||
```bash
|
||||
VERSION=<version from package.json>
|
||||
gh pr merge $PR_NUMBER --squash --subject "v${VERSION} Release Notes" --body ""
|
||||
```
|
||||
|
||||
**If merge is blocked by branch protection:**
|
||||
- Users with admin privileges can add the `--admin` flag to bypass
|
||||
- Users without admin privileges need to get the PR approved through normal review first before merging
|
||||
|
||||
## Step 7: Tag the Release
|
||||
|
||||
After the merge completes, checkout main and pull:
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull origin main
|
||||
```
|
||||
|
||||
**IMPORTANT: Verify the latest commit is the release commit before tagging:**
|
||||
|
||||
```bash
|
||||
git log -1 --oneline
|
||||
```
|
||||
|
||||
Confirm the commit message matches `v{VERSION} Release Notes` (e.g., `v3.44.1 Release Notes`). Do NOT blindly tag HEAD without verification.
|
||||
|
||||
Once verified, tag and push:
|
||||
|
||||
```bash
|
||||
VERSION=<version>
|
||||
git tag v${VERSION}
|
||||
git push origin v${VERSION}
|
||||
```
|
||||
|
||||
## Step 8: Trigger Publish Workflow
|
||||
|
||||
**Copy the tag to clipboard** so the user can easily paste it into the GitHub Actions workflow:
|
||||
|
||||
```bash
|
||||
echo -n "v{VERSION}" | pbcopy
|
||||
```
|
||||
|
||||
**Tell the user to trigger the publish workflow:**
|
||||
1. Go to: https://github.com/cline/cline/actions/workflows/publish.yml
|
||||
2. Select **"release"** for release-type
|
||||
3. Paste **`v{VERSION}`** as the tag (already in clipboard)
|
||||
|
||||
**Wait for the user** to confirm the publish workflow has completed before proceeding.
|
||||
|
||||
## Step 9: Update GitHub Release Notes
|
||||
|
||||
Once the user confirms the publish workflow is done, fetch the auto-generated release content:
|
||||
|
||||
```bash
|
||||
VERSION=<version>
|
||||
gh release view v${VERSION} --json body --jq '.body'
|
||||
```
|
||||
|
||||
The auto-generated release has:
|
||||
- `## What's Changed` - PR list (we'll replace this with our changelog)
|
||||
- `## New Contributors` - First-time contributors (keep this if present)
|
||||
- `**Full Changelog**` - Comparison link (keep this)
|
||||
|
||||
Build the new release body:
|
||||
1. Start with `## What's Changed` header
|
||||
2. Add our changelog content (from CHANGELOG.md for this version)
|
||||
3. Keep the `## New Contributors` section if it exists
|
||||
4. Keep the `**Full Changelog**` link
|
||||
|
||||
Update the release:
|
||||
|
||||
```bash
|
||||
gh release edit v${VERSION} --notes "<new body content>"
|
||||
```
|
||||
|
||||
Verify the release was updated:
|
||||
|
||||
```bash
|
||||
gh release view v${VERSION}
|
||||
```
|
||||
|
||||
## Step 10: Final Summary
|
||||
|
||||
**Copy a Slack announcement message to clipboard** (include the full changelog, not just highlights):
|
||||
|
||||
```bash
|
||||
echo "VS Code v{VERSION} Released
|
||||
|
||||
- Changelog entry 1
|
||||
- Changelog entry 2
|
||||
- Changelog entry 3" | pbcopy
|
||||
```
|
||||
|
||||
**Present a final summary:**
|
||||
- Version released: v{VERSION}
|
||||
- PR merged: #{PR_NUMBER}
|
||||
- Tag pushed: v{VERSION}
|
||||
- Release: https://github.com/cline/cline/releases/tag/v{VERSION}
|
||||
- Slack message copied to clipboard
|
||||
|
||||
**Final reminder:**
|
||||
Post the Slack message to announce the release
|
||||
|
||||
## Handling Edge Cases
|
||||
|
||||
### No changesets found
|
||||
If the changeset PR body shows no changes, inform the user they need to merge PRs with changesets first.
|
||||
|
||||
### Merge conflicts
|
||||
If there are conflicts on the changeset branch, help the user resolve them:
|
||||
```bash
|
||||
git fetch origin main
|
||||
git rebase origin/main
|
||||
# resolve conflicts
|
||||
git push origin changeset-release/main --force-with-lease
|
||||
```
|
||||
|
||||
### User wants to add more changes
|
||||
If the user wants to include additional PRs before releasing:
|
||||
1. Ask them to merge those PRs to main first
|
||||
2. The changeset automation will update the PR automatically
|
||||
3. Re-run this workflow after the PR is updated
|
||||
@@ -1,4 +1,4 @@
|
||||
/docs/
|
||||
/.github/ @saoudrizwan @garoth @sjf
|
||||
/README.md @saoudrizwan @nickbaumann98
|
||||
/src/core/storage/ @celestial-vault
|
||||
/src/core/storage/ @celestial-vault @abeatrix
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install changeset
|
||||
run: npm ci
|
||||
|
||||
# Check if there are any new changesets to process
|
||||
- name: Check for changesets
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
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.
|
||||
@@ -0,0 +1,272 @@
|
||||
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
|
||||
@@ -74,8 +74,8 @@ jobs:
|
||||
CLINE_ENVIRONMENT: production
|
||||
# OpenTelemetry production defaults (can be overridden at runtime)
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: console,otlp
|
||||
OTEL_METRICS_EXPORTER: console,otlp
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
|
||||
|
||||
@@ -36,6 +36,8 @@ 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
|
||||
@@ -60,11 +62,11 @@ jobs:
|
||||
|
||||
- name: Install root dependencies
|
||||
if: steps.root-cache.outputs.cache-hit != 'true'
|
||||
run: npm ci --include=optional
|
||||
run: npm install --include=optional
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
if: steps.webview-cache.outputs.cache-hit != 'true'
|
||||
run: cd webview-ui && npm ci --include=optional
|
||||
run: cd webview-ui && npm install --include=optional
|
||||
|
||||
- name: Install Publishing Tools
|
||||
run: npm install -g @vscode/vsce ovsx
|
||||
@@ -99,8 +101,8 @@ jobs:
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
# OpenTelemetry production defaults (can be overridden at runtime)
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: console,otlp
|
||||
OTEL_METRICS_EXPORTER: console,otlp
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
|
||||
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
|
||||
@@ -116,22 +118,31 @@ jobs:
|
||||
echo "Successfully published release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
|
||||
fi
|
||||
|
||||
# - 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: 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: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ steps.validate_tag.outputs.tag }}
|
||||
files: "*.vsix"
|
||||
# body: ${{ steps.changelog.outputs.content }}
|
||||
generate_release_notes: true
|
||||
body: |
|
||||
${{ steps.changelog.outputs.content }}
|
||||
|
||||
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.validate_tag.outputs.tag }}
|
||||
prerelease: ${{ github.event.inputs.release-type == 'pre-release' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Trigger Jetbrains Plugin <-> Cline Tests
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -22,7 +22,24 @@ jobs:
|
||||
owner: cline
|
||||
repositories: intellij-plugin
|
||||
|
||||
- name: Sanitize untrusted inputs
|
||||
id: sanitize
|
||||
env:
|
||||
RAW_BRANCH_NAME: ${{ github.head_ref }}
|
||||
RAW_PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
run: |
|
||||
# Sanitize branch name for JSON
|
||||
BRANCH_NAME_JSON=$(jq -n --arg b "$RAW_BRANCH_NAME" '$b')
|
||||
echo "branch_name=$BRANCH_NAME_JSON" >> $GITHUB_OUTPUT
|
||||
|
||||
# Sanitize PR title for JSON
|
||||
PR_TITLE_JSON=$(jq -n --arg t "$RAW_PR_TITLE" '$t')
|
||||
echo "pr_title=$PR_TITLE_JSON" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Trigger IntelliJ Plugin Integration Test
|
||||
env:
|
||||
BRANCH_NAME: ${{ steps.sanitize.outputs.branch_name }}
|
||||
PR_TITLE: ${{ steps.sanitize.outputs.pr_title }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{ steps.app-token.outputs.token }}" \
|
||||
@@ -35,10 +52,10 @@ jobs:
|
||||
"event_type": "cline-pr-check",
|
||||
"client_payload": {
|
||||
"pr_number": "${{ github.event.number }}",
|
||||
"branch_name": "${{ github.head_ref }}",
|
||||
"branch_name": $BRANCH_NAME,
|
||||
"action": "${{ github.event.action }}",
|
||||
"sha": "${{ github.event.pull_request.head.sha }}",
|
||||
"pr_title": ${{ toJSON(github.event.pull_request.title) }},
|
||||
"pr_title": $PR_TITLE,
|
||||
"pr_url": "${{ github.event.pull_request.html_url }}"
|
||||
}
|
||||
}
|
||||
@@ -47,7 +64,6 @@ jobs:
|
||||
- name: Log trigger details
|
||||
run: |
|
||||
echo "Triggered IntelliJ Plugin integration test for:"
|
||||
echo " PR #${{ github.event.number }}: ${{ github.event.pull_request.title }}"
|
||||
echo " Branch: ${{ github.head_ref }}"
|
||||
echo " PR #${{ github.event.number }}"
|
||||
echo " Action: ${{ github.event.action }}"
|
||||
echo " SHA: ${{ github.event.pull_request.head.sha }}"
|
||||
|
||||
@@ -8,12 +8,14 @@ tmp
|
||||
|
||||
.DS_Store
|
||||
.idea
|
||||
.husky/_/
|
||||
|
||||
pnpm-lock.yaml
|
||||
|
||||
.clineignore
|
||||
.venv
|
||||
.actrc
|
||||
CLAUDE.local.md
|
||||
|
||||
webview-ui/src/**/*.js
|
||||
webview-ui/src/**/*.js.map
|
||||
@@ -28,6 +30,8 @@ coverage-unit
|
||||
*evals.env
|
||||
.env
|
||||
|
||||
.worktrees
|
||||
|
||||
## Generated files ##
|
||||
src/generated/
|
||||
src/shared/proto/
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extensions", // Avoid conflicts with installed extensions
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
@@ -33,7 +36,10 @@
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extensions", // Avoid conflicts with installed extensions
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
@@ -54,7 +60,10 @@
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extensions", // Avoid conflicts with installed extensions
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
@@ -77,7 +86,10 @@
|
||||
"--user-data-dir=${workspaceFolder}/dist/tmp/user",
|
||||
"--profile-temp",
|
||||
"--sync=off",
|
||||
"--disable-extensions", // Avoid conflicts with installed extensions
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
@@ -165,6 +177,27 @@
|
||||
},
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
},
|
||||
{
|
||||
"name": "Open Storybook",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"storybook"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/webview-ui",
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"serverReadyAction": {
|
||||
"pattern": "Local:.*http://localhost:([0-9]+)",
|
||||
"uriFormat": "http://localhost:%s",
|
||||
"action": "openExternally"
|
||||
},
|
||||
"env": {
|
||||
"IS_DEV": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -27,5 +27,7 @@
|
||||
"source.removeUnused.biome": "always",
|
||||
"source.removeUnusedImports": "always",
|
||||
"source.organizeImports.biome": "always"
|
||||
}
|
||||
},
|
||||
// Disable auto-forwarding ports to prevent Simple Browser from opening the Vite dev server
|
||||
"remote.autoForwardPorts": false
|
||||
}
|
||||
|
||||
@@ -263,6 +263,26 @@
|
||||
"watch"
|
||||
],
|
||||
"command": "rm -rf ${workspaceFolder}/dist/tmp/user && mkdir -p ${workspaceFolder}/dist/tmp/user"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "storybook",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"isBackground": false,
|
||||
"label": "npm: storybook",
|
||||
"dependsOn": [
|
||||
"npm: protos",
|
||||
"npm: build:webview"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"options": {
|
||||
"env": {
|
||||
"IS_DEV": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
|
||||
@@ -40,9 +40,6 @@ buf.yaml
|
||||
.changeset/
|
||||
.clinerules/
|
||||
|
||||
# Include specific file needed for Background Exec mode
|
||||
!standalone/runtime-files/vscode/enhanced-terminal.js
|
||||
|
||||
# Ignore all webview-ui files except the build directory (https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/frameworks/hello-world-react-cra/.vscodeignore)
|
||||
webview-ui/src/**
|
||||
webview-ui/public/**
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.gitignore
|
||||
@@ -1,14 +1,231 @@
|
||||
# Changelog
|
||||
|
||||
## 3.37.1
|
||||
## [3.47.0]
|
||||
|
||||
- cf8dd1c: Comprehensive changes to better support GPT 5.1 - System prompt, tools, deep-planning, focus chain, etc.
|
||||
- 02abbcf: Add AGENTS.md support
|
||||
- 855db7d: feat(models): Add free minimax/mimax-m2 model to the model picker
|
||||
### Added
|
||||
- Added experimental support for Background Edits (allows editing files in background without opening the diff view)
|
||||
- Updated free model to MiniMax M2.1 (replacing MiniMax M2)
|
||||
- Added support for Azure based identity authentication in OpenAI Compatible provider and Azure OpenAI
|
||||
- Add `supportsReasoning` property to Baseten models
|
||||
|
||||
### Fixed
|
||||
|
||||
- Prevent expired token usage in authenticated requests
|
||||
- Exclude binary files without extensions from diffs
|
||||
- Preserve file endings and trailing newlines
|
||||
- Fix Cerebras rate limiting
|
||||
- Fix Auto Compact for Claude Code provider
|
||||
- Make Workspace and Favorites history filters independent
|
||||
- Fix remote MCP server connection failures (404 response handling)
|
||||
- Disable native tool calling for Deepseek 3.2 speciale
|
||||
- Show notification instead of opening sidebar on update
|
||||
- Fix Baseten model selector
|
||||
|
||||
### Refactored
|
||||
|
||||
- Modify prompts for parallel tool usage in Claude and Gemini 3 models
|
||||
|
||||
## [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
|
||||
|
||||
## [3.44.2]
|
||||
|
||||
- Polished the model picker UI with checkmarks for selected models, tooltips on Plan/Act tabs, and consistent arrow pointers across all popup modals
|
||||
- Improved WhatsNew modal responsiveness and cleaned up redundant UI elements
|
||||
- Fixed GLM models outputting garbled text in thinking tags—reasoning is now properly disabled for these models
|
||||
|
||||
## [3.44.1]
|
||||
|
||||
- Fixed a critical bug where local MCP servers stopped connecting after v3.42.0—all user-configured stdio-based MCP servers should now work again
|
||||
- Fixed remotely configured API keys not being extracted correctly for enterprise users
|
||||
- Added support for dynamic tool instructions that adapt based on runtime context, laying groundwork for future context-aware features
|
||||
|
||||
## [3.44.0]
|
||||
|
||||
## Added
|
||||
|
||||
- Updating minor version to show a proper banner for the release
|
||||
|
||||
## [3.43.1]
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix GLM-4.6 Model reference id
|
||||
|
||||
## [3.43.0]
|
||||
|
||||
### Added
|
||||
|
||||
- GLM-4.6
|
||||
- kat-coder-pro
|
||||
- Add parsing of env variable patterns to the mcpconfig.json
|
||||
|
||||
### Fixed
|
||||
|
||||
- TLS Proxy support issues for VSCode
|
||||
- Add supportsReasoning flag to OpenAI reasoning models
|
||||
- Fix thinking not available for some models in the OpenAI provider
|
||||
- Fix invalid signature field issues when switching between Gemini and Anthropic providers
|
||||
- Extract OpenRouter model filtering into reusable utility and use it in different model pickers
|
||||
- Fix a11y for auto approve checkbox
|
||||
- Improve ModelPickerModal provider list layout
|
||||
|
||||
### Refactored
|
||||
|
||||
- Migrate WhatsNewModal to new shared dialogue component
|
||||
|
||||
## [3.42.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Expose `getAvailableSlashCommands` rpc endpoint to UI clients
|
||||
- Made slash command menu and context menu accessible and screenreader-friendly
|
||||
- Made expanding/collapsing UI components accessible
|
||||
|
||||
### Fixed
|
||||
|
||||
- Devstral OpenRouter model ID and routing issues
|
||||
- Incorrect pricing display for Devstral model in the extension
|
||||
|
||||
## [3.41.0]
|
||||
|
||||
### Added
|
||||
|
||||
- OpenAI GPT-5.2
|
||||
- Devstral-2512 (formerly stealth model "Microwave")
|
||||
- Improvements to chat modal model picker
|
||||
- Amazon Nova 2 Lite
|
||||
- DeepSeek 3.2 to native tool calling allow list
|
||||
- Responses API support for Codex models in OpenAI provider (requires native tool calling)
|
||||
- Xmas Special Santa Cline
|
||||
- Welcome screen UI enhancements
|
||||
|
||||
### Fixed
|
||||
|
||||
- Initial checkpoint commit now non-blocking for improved responsiveness in large repositories
|
||||
- Gemini Vertex models erroring when thinking parameters are not supported
|
||||
- Restrictive file permissions for secrets.json
|
||||
- Ollama streaming requests not aborting when task is cancelled
|
||||
|
||||
### Refactored
|
||||
|
||||
- OpenAI provider to centralize temperature configuration and include missing GPT-5 model settings
|
||||
- OpenAI native handler to use metadata for model capabilities
|
||||
- Vertex provider to use metadata for model capabilities
|
||||
|
||||
## [3.40.2]
|
||||
|
||||
- Fix logout on network errors during token refresh (e.g., opening laptop while offline)
|
||||
|
||||
## [3.40.1]
|
||||
|
||||
- Fix cost calculation display for Anthropic API requests
|
||||
|
||||
## [3.40.0]
|
||||
|
||||
- Fix highlighted text flashing when task header is collapsed
|
||||
- Add X-Cerebras-3rd-Party-Integration header to Cerebras API requests
|
||||
- Add microwave family system prompt configuration
|
||||
- Remove tooltips from auto approve menu
|
||||
- Fix Standalone, ensure cwd is the install dir to find resources reliably
|
||||
- Fix a bug where terminal commands with double quotes are broken when "Terminal Execution Mode" is set to "Background Exec"
|
||||
- Add support for slash commands anywhere in a message, not just at the beginning. This matches the behavior of @ mentions for a more flexible input experience.
|
||||
- Add bottom padding to the last message to fix last response text getting cut off by auto approve settings bar.
|
||||
- Add default thinking level for Gemini 3 Pro models in Gemini provider
|
||||
|
||||
## [3.39.2]
|
||||
|
||||
- Fix for microwave model and thinking settings
|
||||
|
||||
## [3.39.1]
|
||||
|
||||
- Fix Openrouter and Cline Provider model info
|
||||
|
||||
## [3.39.0]
|
||||
|
||||
- Add Explain Changes feature
|
||||
- Add microwave Stealth model
|
||||
- Add Tabbed Model Picker with Recommended and Free tabs
|
||||
- Add support to View remote rules and workflows in the editor
|
||||
- Enable NTC (Native Tool Calling) by default
|
||||
- Bug fixes and improvements for LiteLLM provider
|
||||
|
||||
## [3.38.3]
|
||||
|
||||
- Task export feature now opens the task directory, allowing easy access to the full task files
|
||||
- Add Grok 4.1 and Grok Code to XAI provider
|
||||
- Enabled native tool calling for Baseten and Kimi K2 models
|
||||
- Add thinking level to Gemini 3.0 Pro preview
|
||||
- Expanded Hooks functionality
|
||||
- Removed Task Timeline from Task Header
|
||||
- Bug fix for slash commands
|
||||
- Bug fixes for Vertex provider
|
||||
- Bug fixes for thinking/reasoning issues across multiple providers when using native tool calling
|
||||
- Bug fixes for terminal usage on Windows devices
|
||||
|
||||
## [3.38.2]
|
||||
|
||||
- Add Claude Opus 4.5
|
||||
|
||||
## [3.38.1]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed handling of 'signature' field in sanitizeAnthropicContentBlock to properly preserve it when thinking is enabled, as required by Anthropic's API.
|
||||
|
||||
## [3.38.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Gemini 3 Pro Preview model
|
||||
- AquaVoice Avalon model for voice-to-text dictation
|
||||
|
||||
### Fixed
|
||||
|
||||
- Automatic context truncation when AWS Bedrock token usage rate limits are exceeded
|
||||
- Removed new_task tool from system prompts, updated slash command prompts, and added helper function for native tool calling validation
|
||||
|
||||
## [3.37.1]
|
||||
|
||||
- Comprehensive changes to better support GPT 5.1 - System prompt, tools, deep-planning, focus chain, etc.
|
||||
- Add AGENTS.md support
|
||||
- feat(models): Add free minimax/mimax-m2 model to the model picker
|
||||
|
||||
## [3.37.0]
|
||||
|
||||
## Added
|
||||
### Added
|
||||
|
||||
- GPT-5.1 with model-specific prompting: tailored system prompts, tool usage, focus chain, and deep-planning optimizations
|
||||
- Nous Research provider with Hermes 4 model family and custom system prompts
|
||||
@@ -18,7 +235,7 @@
|
||||
- Expanded HTTP proxy support throughout the codebase
|
||||
- Improved focus chain prompting for frontier models (Anthropic, OpenAI, Gemini, xAI)
|
||||
|
||||
## Fixed
|
||||
### Fixed
|
||||
|
||||
- Duplicate tool results prevention through existence checking
|
||||
- XML entity escaping in model content processor
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file is the secret sauce for working effectively in this codebase. It captures tribal knowledge—the nuanced, non-obvious patterns that make the difference between a quick fix and hours of back-and-forth & human intervention.
|
||||
|
||||
**When to add to this file:**
|
||||
- User had to intervene, correct, or hand-hold
|
||||
- Multiple back-and-forth attempts were needed to get something working
|
||||
- You discovered something that required reading many files to understand
|
||||
- A change touched files you wouldn't have guessed
|
||||
- Something worked differently than you expected
|
||||
- User explicitly asks to "add this to CLAUDE.md"
|
||||
|
||||
**Proactively suggest additions** when any of the above happen—don't wait to be asked.
|
||||
|
||||
**What NOT to add:** Stuff you can figure out from reading a few files, obvious patterns, or standard practices. This file should be high-signal, not comprehensive.
|
||||
|
||||
## 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.
|
||||
|
||||
**Proto files live in `proto/`** (e.g., `proto/cline/task.proto`, `proto/cline/ui.proto`)
|
||||
- Each feature domain has its own `.proto` file
|
||||
- For simple data, use shared types in `proto/cline/common.proto` (`StringRequest`, `Empty`, `Int64Request`)
|
||||
- For complex data, define custom messages in the feature's `.proto` file
|
||||
- Naming: Services `PascalCaseService`, RPCs `camelCase`, Messages `PascalCase`
|
||||
- For streaming responses, use `stream` keyword (see `subscribeToAuthCallback` in `account.proto`)
|
||||
|
||||
**Run `npm run protos`** after any proto changes—generates types in:
|
||||
- `src/shared/proto/` - Shared type definitions
|
||||
- `src/generated/grpc-js/` - Service implementations
|
||||
- `src/generated/nice-grpc/` - Promise-based clients
|
||||
- `src/generated/hosts/` - Generated handlers
|
||||
|
||||
**Adding new enum values** (like a new `ClineSay` type) requires updating conversion mappings in `src/shared/proto-conversions/cline-message.ts`
|
||||
|
||||
**Adding new RPC methods** requires:
|
||||
- Handler in `src/core/controller/<domain>/`
|
||||
- Call from webview via generated client: `UiServiceClient.scrollToSettings(StringRequest.create({ value: "browser" }))`
|
||||
|
||||
**Example—the `explain-changes` feature touched:**
|
||||
- `proto/cline/task.proto` - Added `ExplainChangesRequest` message and `explainChanges` RPC
|
||||
- `proto/cline/ui.proto` - Added `GENERATE_EXPLANATION = 29` to `ClineSay` enum
|
||||
- `src/shared/ExtensionMessage.ts` - Added `ClineSayGenerateExplanation` type
|
||||
- `src/shared/proto-conversions/cline-message.ts` - Added mapping for new say type
|
||||
- `src/core/controller/task/explainChanges.ts` - Handler implementation
|
||||
- `webview-ui/src/components/chat/ChatRow.tsx` - UI rendering
|
||||
|
||||
## Adding Tools to System Prompt
|
||||
This is tricky—multiple prompt variants and configs. **Always search for existing similar tools first and follow their pattern.** Look at the full chain from prompt definition → variant configs → handler → UI before implementing.
|
||||
|
||||
1. **Add to `ClineDefaultTool` enum** in `src/shared/tools.ts`
|
||||
2. **Tool definition** in `src/core/prompts/system-prompt/tools/` (create file like `generate_explanation.ts`)
|
||||
- Define variants for each `ModelFamily` (generic, next-gen, xs, etc.)
|
||||
- Export variants array (e.g., `export const my_tool_variants = [GENERIC, NATIVE_NEXT_GEN, XS]`)
|
||||
- **Fallback behavior**: If a variant isn't defined for a model family, `ClineToolSet.getToolByNameWithFallback()` automatically falls back to GENERIC. So you only need to export `[GENERIC]` unless the tool needs model-specific behavior.
|
||||
3. **Register in `src/core/prompts/system-prompt/tools/init.ts`** - Import and spread into `allToolVariants`
|
||||
4. **Add to variant configs** - Each model family has its own config in `src/core/prompts/system-prompt/variants/*/config.ts`. Add your tool's enum to the `.tools()` list:
|
||||
- `generic/config.ts`, `next-gen/config.ts`, `gpt-5/config.ts`, `native-gpt-5/config.ts`, `native-gpt-5-1/config.ts`, `native-next-gen/config.ts`, `gemini-3/config.ts`, `glm/config.ts`, `hermes/config.ts`, `xs/config.ts`
|
||||
- **Important**: If you add to a variant's config, make sure the tool spec exports a variant for that ModelFamily (or relies on GENERIC fallback)
|
||||
5. **Create handler** in `src/core/task/tools/handlers/`
|
||||
6. **Wire up in `ToolExecutor.ts`** if needed for execution flow
|
||||
7. **Add to tool parsing** in `src/core/assistant-message/index.ts` if needed
|
||||
8. **If tool has UI feedback**: add `ClineSay` enum in proto, update `src/shared/ExtensionMessage.ts`, update `src/shared/proto-conversions/cline-message.ts`, update `webview-ui/src/components/chat/ChatRow.tsx`
|
||||
|
||||
## Modifying System Prompt
|
||||
**Read these first:** `src/core/prompts/system-prompt/README.md`, `tools/README.md`, `__tests__/README.md`
|
||||
|
||||
System prompt is modular: **components** (reusable sections) + **variants** (model-specific configs) + **templates** (with `{{PLACEHOLDER}}` resolution).
|
||||
|
||||
**Key directories:**
|
||||
- `components/` - Shared sections: `rules.ts`, `capabilities.ts`, `editing_files.ts`, etc.
|
||||
- `variants/` - Model-specific: `generic/`, `next-gen/`, `xs/`, `gpt-5/`, `gemini-3/`, `hermes/`, `glm/`, etc.
|
||||
- `templates/` - Template engine and placeholder definitions
|
||||
|
||||
**Variant tiers (ask user which to modify):**
|
||||
- **Next-gen** (Claude 4, GPT-5, Gemini 2.5): `next-gen/`, `native-next-gen/`, `native-gpt-5/`, `native-gpt-5-1/`, `gemini-3/`, `gpt-5/`
|
||||
- **Standard** (default fallback): `generic/`
|
||||
- **Local/small models**: `xs/`, `hermes/`, `glm/`
|
||||
|
||||
**How overrides work:** Variants can override components via `componentOverrides` in their `config.ts`, or provide a custom template in `template.ts` (e.g., `next-gen/template.ts` exports `rules_template`). If no override, the shared component from `components/` is used.
|
||||
|
||||
**Example: Adding a rule to RULES section**
|
||||
1. Check if variant overrides rules: look for `rules_template` in `variants/*/template.ts` or `componentOverrides.RULES` in `config.ts`
|
||||
2. If shared: modify `components/rules.ts`
|
||||
3. If overridden: modify that variant's template
|
||||
4. XS variant is special—has heavily condensed inline content in `template.ts`
|
||||
|
||||
**After any changes, regenerate snapshots:**
|
||||
```bash
|
||||
UPDATE_SNAPSHOTS=true npm run test:unit
|
||||
```
|
||||
Snapshots live in `__tests__/__snapshots__/`. Tests validate across model families and context variations (browser, MCP, focus chain).
|
||||
|
||||
## Modifying Default Slash Commands
|
||||
Three places need updates:
|
||||
- `src/core/slash-commands/index.ts` - Command definitions
|
||||
- `src/core/prompts/commands.ts` - System prompt integration
|
||||
- `webview-ui/src/utils/slash-commands.ts` - Webview autocomplete
|
||||
|
||||
## ChatRow Cancelled/Interrupted States
|
||||
When a ChatRow displays a loading/in-progress state (spinner), you must handle what happens when the task is cancelled. This is non-obvious because cancellation doesn't update the message content—you have to infer it from context.
|
||||
|
||||
**The pattern:**
|
||||
1. A message has a `status` field (e.g., `"generating"`, `"complete"`, `"error"`) stored in `message.text` as JSON
|
||||
2. When cancelled mid-operation, the status stays `"generating"` forever—no one updates it
|
||||
3. To detect cancellation, check TWO conditions:
|
||||
- `!isLast` — if this message is no longer the last message, something else happened after it (interrupted)
|
||||
- `lastModifiedMessage?.ask === "resume_task" || "resume_completed_task"` — task was just cancelled and is waiting to resume
|
||||
|
||||
**Example from `generate_explanation`:**
|
||||
```tsx
|
||||
const wasCancelled =
|
||||
explanationInfo.status === "generating" &&
|
||||
(!isLast ||
|
||||
lastModifiedMessage?.ask === "resume_task" ||
|
||||
lastModifiedMessage?.ask === "resume_completed_task")
|
||||
const isGenerating = explanationInfo.status === "generating" && !wasCancelled
|
||||
```
|
||||
|
||||
**Why both checks?**
|
||||
- `!isLast` catches: cancelled → resumed → did other stuff → this old message is stale
|
||||
- `lastModifiedMessage?.ask === "resume_task"` catches: just cancelled, hasn't resumed yet, this message is still technically "last"
|
||||
|
||||
**See also:** `BrowserSessionRow.tsx` uses similar pattern with `isLastApiReqInterrupted` and `isLastMessageResume`.
|
||||
|
||||
**Backend side:** When streaming is cancelled, clean up properly (close tabs, clear comments, etc.) by checking `taskState.abort` after the streaming function returns.
|
||||
@@ -141,6 +141,11 @@ For example, when working with a local web server, you can use 'Restore Workspac
|
||||
|
||||
To contribute to the project, start with our [Contributing Guide](CONTRIBUTING.md) to learn the basics. You can also join our [Discord](https://discord.gg/cline) to chat with other contributors in the `#contributors` channel. If you're looking for full-time work, check out our open positions on our [careers page](https://cline.bot/join-us)!
|
||||
|
||||
## Enterprise
|
||||
|
||||
Get the same Cline experience with enterprise-grade controls: SSO (SAML/OIDC), global policies and configuration, observability with audit trails, private networking (VPC/private link), and self-hosted or on-prem deployments, and enterprise support. Learn more at our [enterprise page](https://cline.bot/enterprise) or [talk to us](https://cline.bot/contact-sales).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>
|
||||
<json>
|
||||
<![CDATA[
|
||||
{
|
||||
"fontFamily": "cline-bot",
|
||||
"majorVersion": 1,
|
||||
"minorVersion": 0,
|
||||
"fontURL": "https://cline.bot",
|
||||
"designerURL": "https://cline.bot",
|
||||
"licenseURL": "https://cline.bot",
|
||||
"version": "Version 1.0",
|
||||
"fontId": "cline-bot",
|
||||
"psName": "cline-bot",
|
||||
"subFamily": "Regular",
|
||||
"fullName": "cline-bot",
|
||||
"description": "Font generated by IcoMoon."
|
||||
}
|
||||
]]>
|
||||
</json>
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="cline-bot" horiz-adv-x="1024">
|
||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||
<glyph unicode="" glyph-name="cline" data-tags="cline" horiz-adv-x="977" d="M964.553 383.11l-60.285 121.406v69.495c0 115.545-92.939 209.321-207.647 209.321h-102.986c7.536 15.071 11.722 32.654 11.722 51.074 0 64.471-51.912 116.383-115.545 116.383s-115.545-51.912-115.545-116.383 4.186-35.166 11.722-51.074h-102.986c-114.708 0-207.647-93.776-207.647-209.321v-69.495l-61.959-121.406c-5.861-11.722-5.861-26.793 0-38.515l61.959-119.732v-69.495c0-115.545 92.939-209.321 207.647-209.321h415.294c114.708 0 207.647 93.776 207.647 209.321v69.495l60.285 119.732c5.861 11.722 5.861 25.956 0 38.515v0zM426.178 284.311c0-52.749-42.702-95.451-94.613-95.451s-94.613 42.702-94.613 95.451v169.132c0 52.749 42.702 95.451 94.613 95.451s94.613-42.702 94.613-95.451v-169.132zM731.787 284.311c0-52.749-42.702-95.451-94.613-95.451s-94.613 42.702-94.613 95.451v169.132c0 52.749 42.702 95.451 94.613 95.451s94.613-42.702 94.613-95.451v-169.132z" />
|
||||
</font></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -70,3 +70,4 @@ Apache-2.0 - see [LICENSE](https://github.com/cline/cline/blob/main/LICENSE) for
|
||||
- Report issues: [GitHub Issues](https://github.com/cline/cline/issues)
|
||||
- Community: [GitHub Discussions](https://github.com/cline/cline/discussions)
|
||||
- Documentation: [docs.cline.bot](https://docs.cline.bot)
|
||||
- Cline CLI Architecture: [architecture.md](./architecture.md)
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
# Cline CLI Architecture
|
||||
|
||||
The CLI is a **standalone terminal interface** for the Cline AI coding assistant, written in Go. It provides the same autonomous coding capabilities as the VS Code extension but runs entirely in the terminal.
|
||||
|
||||
## High-Level Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ User Terminal │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ cline (Go binary) │
|
||||
│ cmd/cline/main.go │
|
||||
│ • Cobra CLI commands (task, auth, config, instance, etc.) │
|
||||
│ • Interactive input via Bubble Tea │
|
||||
│ • Streaming output with markdown rendering │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│ gRPC (50052) │ starts subprocess
|
||||
▼ ▼
|
||||
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||
│ cline-core │◄────────────────►│ cline-host │
|
||||
│ (Node.js) │ gRPC (51052) │ (Go binary) │
|
||||
│ │ │ cmd/cline-host/main.go│
|
||||
│ • AI/LLM orchestration │ │ │
|
||||
│ • Tool execution │ │ • Workspace paths │
|
||||
│ • Task state mgmt │ │ • File diff editing │
|
||||
│ • Message handling │ │ • Clipboard access │
|
||||
└─────────────────────────┘ │ • Environment info │
|
||||
│ └─────────────────────────┘
|
||||
│ SQLite (self-registration)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ ~/.cline/data/locks/locks.db │
|
||||
│ (Instance registry - core self-registers on startup) │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Entry Points (`cmd/`)
|
||||
|
||||
### `cmd/cline/main.go` - Main CLI
|
||||
|
||||
Cobra-based CLI with commands:
|
||||
|
||||
- **Root**: `cline [prompt]` - Start a task directly
|
||||
- **task**: Create, send, view, list, pause, restore tasks
|
||||
- **auth**: Authentication setup and provider configuration
|
||||
- **config**: Read/write settings
|
||||
- **instance**: Manage running Cline instances
|
||||
- **logs**: View and clean log files
|
||||
- **doctor**: System health check
|
||||
|
||||
### `cmd/cline-host/main.go` - Host Bridge Service
|
||||
|
||||
Separate gRPC server providing host environment operations to cline-core:
|
||||
|
||||
- Workspace paths
|
||||
- File diff editing
|
||||
- Clipboard access
|
||||
- Shutdown coordination
|
||||
|
||||
---
|
||||
|
||||
## `pkg/cli/` Subsystems
|
||||
|
||||
### 1. `auth/` - Authentication System
|
||||
|
||||
Handles authentication with Cline service and BYO (Bring Your Own) API providers.
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------------- | ------------------------------------------------------------------------ |
|
||||
| `auth_cline_provider.go` | OAuth login flow - opens browser, subscribes to auth callback stream |
|
||||
| `auth_menu.go` | Interactive menu showing auth options based on current state |
|
||||
| `auth_subscription.go` | gRPC stream subscription for auth status updates |
|
||||
| `wizard_byo.go` | Interactive wizard for configuring BYO providers |
|
||||
| `wizard_byo_bedrock.go` | AWS Bedrock-specific credential setup |
|
||||
| `wizard_byo_oca.go` | Oracle Code Assist setup |
|
||||
| `providers_list.go` | Retrieves configured providers from core state |
|
||||
| `providers_byo.go` | Provider selection UI and field configuration |
|
||||
| `models_*.go` | Model listing (static lists + dynamic fetch from OpenRouter/OpenAI/Ollama) |
|
||||
|
||||
**Flow**: User runs `cline auth` → Menu shows options → For BYO: wizard guides through provider/key/model selection → Config saved via gRPC to core.
|
||||
|
||||
---
|
||||
|
||||
### 2. `clerror/` - Error Handling
|
||||
|
||||
Parses and classifies API errors from the Cline service.
|
||||
|
||||
**Error Types:**
|
||||
|
||||
- `ErrorTypeAuth` - 401, bad API key
|
||||
- `ErrorTypeBalance` - Insufficient credits
|
||||
- `ErrorTypeRateLimit` - 429, quota exceeded
|
||||
- `ErrorTypeNetwork` - Connection issues
|
||||
- `ErrorTypeUnknown` - Catch-all
|
||||
|
||||
Extracts billing details (balance, spent, buy credits URL) from error responses.
|
||||
|
||||
---
|
||||
|
||||
### 3. `config/` - Configuration Management
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------- | -------------------------------------------------------------------------- |
|
||||
| `manager.go` | gRPC interface for reading/writing settings via `UpdateSettingsCli` RPC |
|
||||
| `settings_renderer.go`| Pretty-prints config values, censors sensitive fields (keys, secrets) |
|
||||
|
||||
Supports dot-notation paths: `cline config get auto-approval-settings.actions.read-files`
|
||||
|
||||
---
|
||||
|
||||
### 4. `display/` - Terminal Display System
|
||||
|
||||
The most complex subsystem - handles all visual output.
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------- | -------------------------------------------------------------------- |
|
||||
| `renderer.go` | Central coordinator with lipgloss styles, color methods, markdown delegation |
|
||||
| `streaming.go` | Real-time streaming display with deduplication |
|
||||
| `segment_streamer.go` | Streaming segments (header + body) with context-aware headers |
|
||||
| `typewriter.go` | Character-by-character animation with variable delays |
|
||||
| `markdown_renderer.go` | Glamour wrapper for terminal markdown rendering |
|
||||
| `tool_renderer.go` | Tool operation formatting ("Cline is editing `file.ts`") |
|
||||
| `tool_result_parser.go` | Parses structured tool results (file lists, search results) |
|
||||
| `banner.go` | Session startup banner with version/model/workspace |
|
||||
| `deduplicator.go` | MD5-based deduplication with 2-second window |
|
||||
| `system_renderer.go` | Rich error/warning boxes for balance errors, auth failures |
|
||||
| `ansi.go` | TTY detection, line clearing with escape codes |
|
||||
|
||||
---
|
||||
|
||||
### 5. `global/` - Global State Management
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------ | -------------------------------------------------------------------------- |
|
||||
| `global.go` | Global config (paths, verbosity, output format), initialization |
|
||||
| `registry.go` | Instance discovery via SQLite, health checking, default instance management|
|
||||
| `cline-clients.go` | Starts cline-core + cline-host processes, port allocation, cleanup |
|
||||
|
||||
**Instance lifecycle:**
|
||||
|
||||
1. Find available port pair
|
||||
2. Start `cline-host` on port+1000
|
||||
3. Start `cline-core` on port
|
||||
4. Wait for core to self-register in SQLite
|
||||
5. Set as default if first instance
|
||||
|
||||
---
|
||||
|
||||
### 6. `handlers/` - Message Handlers
|
||||
|
||||
Routes incoming messages from cline-core to appropriate renderers.
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------ | --------------------------------------------------------------------- |
|
||||
| `handler.go` | Handler registry with priority-based routing |
|
||||
| `ask_handlers.go` | Approval requests: tool, command, followup, api_req_failed, etc. |
|
||||
| `say_handlers.go` | Status messages: text, reasoning, command_output, tool, checkpoint, etc. |
|
||||
|
||||
Uses `DisplayContext` providing renderer access, state, and context flags (isLast, isPartial, isStreamingMode).
|
||||
|
||||
---
|
||||
|
||||
### 7. `output/` - Output Coordination
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------- | ----------------------------------------------------------------------- |
|
||||
| `coordinator.go` | Coordinates streaming output with interactive input (saves/restores input state) |
|
||||
| `input_model.go` | Bubble Tea model for rich input (message, approval, feedback types) |
|
||||
| `slash_completion.go` | Autocomplete dropdown for slash commands |
|
||||
|
||||
**Key pattern:** When output needs to print while input is visible, the coordinator saves input state, clears the form, prints, then restores input.
|
||||
|
||||
---
|
||||
|
||||
### 8. `slash/` - Slash Command Registry
|
||||
|
||||
Central registry for commands like `/plan`, `/act`, `/cancel`:
|
||||
|
||||
- **CLI-local commands**: Handled directly by CLI
|
||||
- **Backend commands**: Fetched from core via gRPC, filtered by `CliCompatible` flag
|
||||
|
||||
---
|
||||
|
||||
### 9. `sqlite/` - Instance Locking
|
||||
|
||||
Manages the distributed locking system:
|
||||
|
||||
- **Instance locks**: Track running Cline instances by address
|
||||
- **File locks**: Coordinate file access across instances
|
||||
- SQLite database created by cline-core, CLI reads/writes for discovery
|
||||
|
||||
---
|
||||
|
||||
### 10. `task/` - Task Management
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------- | -------------------------------------------------------------------- |
|
||||
| `manager.go` | Core orchestrator: create, cancel, resume, restore tasks; stream handling |
|
||||
| `stream_coordinator.go` | Deduplication and turn management for dual streams |
|
||||
| `input_handler.go` | Interactive input during follow mode (polling, approval detection) |
|
||||
| `history_handler.go` | Direct disk access to `taskHistory.json` |
|
||||
| `settings_parser.go` | Parse settings from CLI flags |
|
||||
| `follow_options.go` | Configuration for follow behavior |
|
||||
|
||||
**Streaming:** Task manager subscribes to two gRPC streams:
|
||||
|
||||
1. `SubscribeToState` - Full state updates
|
||||
2. `SubscribeToPartialMessage` - Streaming AI responses
|
||||
|
||||
---
|
||||
|
||||
### 11. `terminal/` - Terminal Handling
|
||||
|
||||
Enhanced keyboard protocol support and terminal configuration:
|
||||
|
||||
- Enables modifyOtherKeys and Kitty keyboard protocol
|
||||
- Detects terminal type (VS Code, iTerm, Ghostty, Kitty, etc.)
|
||||
- Auto-configures shift+enter keybindings for various terminals
|
||||
|
||||
---
|
||||
|
||||
### 12. `types/` - Type Definitions
|
||||
|
||||
| File | Purpose |
|
||||
| -------------- | ----------------------------------------------------------------- |
|
||||
| `messages.go` | `ClineMessage`, `AskType`, `SayType`, `ToolType` enums, proto conversion |
|
||||
| `state.go` | `ConversationState` with thread-safe message access |
|
||||
| `history.go` | `HistoryItem` matching taskHistory.json format |
|
||||
|
||||
---
|
||||
|
||||
### 13. `updater/` - Auto-Update
|
||||
|
||||
Background auto-update checking:
|
||||
|
||||
- 24-hour check interval (cached)
|
||||
- Queries npm registry for newer versions
|
||||
- Supports `latest` and `nightly` channels
|
||||
- Runs `npm install -g cline` to update
|
||||
|
||||
---
|
||||
|
||||
## `pkg/common/` - Shared Types
|
||||
|
||||
| File | Purpose |
|
||||
| --------------- | ------------------------------------------------------------ |
|
||||
| `constants.go` | `SETTINGS_SUBFOLDER`, `DEFAULT_CLINE_CORE_PORT` |
|
||||
| `schema.go` | SQL queries for instance/file locks |
|
||||
| `types.go` | `CoreInstanceInfo`, `LockRow`, `DefaultCoreInstance` |
|
||||
| `utils.go` | Port checking, health checks, address normalization, retry logic |
|
||||
|
||||
---
|
||||
|
||||
## `pkg/generated/` - Auto-Generated
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `providers.go` | Provider definitions (Anthropic, OpenAI, Bedrock, etc.) with field metadata and model specs - generated from TypeScript sources |
|
||||
| `field_overrides.go` | Manual overrides for field filtering |
|
||||
|
||||
---
|
||||
|
||||
## `pkg/hostbridge/` - CLI-to-Core Bridge
|
||||
|
||||
This is the **reverse bridge** allowing cline-core to request host environment operations:
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------- | ---------------------------------------------------- |
|
||||
| `grpc_server.go` | Main server registering all services |
|
||||
| `simple_workspace.go` | Workspace service: returns CWD as workspace path |
|
||||
| `diff.go` | In-memory file diff editing with line-based operations |
|
||||
| `env.go` | Clipboard access, version info, shutdown coordination |
|
||||
| `window.go` | UI stubs (no-ops or console output) |
|
||||
|
||||
**Why this exists:** The same cline-core logic runs in VS Code and CLI. In VS Code, the "host" is the extension with editor APIs. In CLI, hostbridge emulates these capabilities with terminal-appropriate implementations.
|
||||
|
||||
---
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
1. **Two-process model:** `cline` CLI manages instances; `cline-core` is the actual AI engine (Node.js). This allows reusing the same core as the VS Code extension.
|
||||
|
||||
2. **Self-registration via SQLite:** `cline-core` registers itself in a SQLite database on startup. The CLI discovers instances by reading this database, enabling multi-instance support.
|
||||
|
||||
3. **Host bridge abstraction:** The `cline-host` process provides platform-specific operations (clipboard, workspace paths) via gRPC, allowing `cline-core` to remain host-agnostic.
|
||||
|
||||
4. **Streaming-first UI:** The CLI uses gRPC streaming to display AI responses in real-time with typewriter-style rendering.
|
||||
|
||||
5. **Dual stream handling:** Task manager subscribes to both state updates and partial messages, using deduplication to prevent duplicate rendering.
|
||||
@@ -14,8 +14,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
port int
|
||||
verbose bool
|
||||
port int
|
||||
verbose bool
|
||||
workspaces []string
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -28,6 +29,7 @@ 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)
|
||||
@@ -39,7 +41,7 @@ func runServer(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
|
||||
// Create gRPC hostbridge service
|
||||
service := hostbridge.NewGrpcServer(port, verbose)
|
||||
service := hostbridge.NewGrpcServer(port, verbose, workspaces)
|
||||
|
||||
// Handle graceful shutdown
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
@@ -25,12 +26,13 @@ var (
|
||||
outputFormat string
|
||||
|
||||
// Task creation flags (for root command)
|
||||
images []string
|
||||
files []string
|
||||
mode string
|
||||
settings []string
|
||||
yolo bool
|
||||
oneshot bool
|
||||
images []string
|
||||
files []string
|
||||
mode string
|
||||
settings []string
|
||||
yolo bool
|
||||
oneshot bool
|
||||
workspaces []string
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -70,12 +72,23 @@ 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)
|
||||
instance, err := global.Clients.StartNewInstance(ctx, allWorkspaces...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start new instance: %w", err)
|
||||
}
|
||||
@@ -131,8 +144,8 @@ see the manual page: man cline`,
|
||||
|
||||
// If no prompt from args or stdin, show interactive input
|
||||
if prompt == "" {
|
||||
// Pass the mode flag to banner so it shows correct mode
|
||||
prompt, err = promptForInitialTask(ctx, instanceAddress, mode)
|
||||
// Pass the mode flag and workspaces to banner so it shows correct info
|
||||
prompt, err = promptForInitialTask(ctx, instanceAddress, mode, allWorkspaces)
|
||||
if err != nil {
|
||||
// Check if user cancelled - exit cleanly without error
|
||||
if err == huh.ErrUserAborted {
|
||||
@@ -152,13 +165,14 @@ 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,
|
||||
Images: images,
|
||||
Files: files,
|
||||
Mode: mode,
|
||||
Settings: settings,
|
||||
Yolo: yolo,
|
||||
Address: instanceAddress,
|
||||
Verbose: verbose,
|
||||
Workspaces: allWorkspaces,
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -175,6 +189,7 @@ 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())
|
||||
@@ -189,9 +204,9 @@ see the manual page: man cline`,
|
||||
}
|
||||
}
|
||||
|
||||
func promptForInitialTask(ctx context.Context, instanceAddress, modeFlag string) (string, error) {
|
||||
func promptForInitialTask(ctx context.Context, instanceAddress, modeFlag string, workspaces []string) (string, error) {
|
||||
// Show session banner before the initial input
|
||||
showSessionBanner(ctx, instanceAddress, modeFlag)
|
||||
showSessionBanner(ctx, instanceAddress, modeFlag, workspaces)
|
||||
|
||||
var prompt string
|
||||
|
||||
@@ -233,7 +248,7 @@ func promptForInitialTask(ctx context.Context, instanceAddress, modeFlag string)
|
||||
}
|
||||
|
||||
// showSessionBanner displays session info before initial prompt
|
||||
func showSessionBanner(ctx context.Context, instanceAddress, modeFlag string) {
|
||||
func showSessionBanner(ctx context.Context, instanceAddress, modeFlag string, workspaces []string) {
|
||||
bannerInfo := display.BannerInfo{
|
||||
Version: global.CliVersion,
|
||||
Mode: modeFlag, // Use the mode from command flag, not state
|
||||
@@ -244,10 +259,7 @@ func showSessionBanner(ctx context.Context, instanceAddress, modeFlag string) {
|
||||
bannerInfo.Mode = "plan"
|
||||
}
|
||||
|
||||
// Get current working directory (this is what Cline will use)
|
||||
if cwd, err := os.Getwd(); err == nil {
|
||||
bannerInfo.Workdir = cwd
|
||||
}
|
||||
bannerInfo.Workdirs = workspaces
|
||||
|
||||
// Get provider/model using auth functions (same logic as auth menu)
|
||||
manager, err := cli.NewTaskManagerForAddress(ctx, instanceAddress)
|
||||
@@ -345,4 +357,37 @@ 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,6 +1,6 @@
|
||||
module github.com/cline/cli
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
|
||||
@@ -70,6 +70,10 @@ 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:
|
||||
@@ -78,6 +82,28 @@ These options apply to all subcommands:
|
||||
|
||||
: Output format. Options: **rich** (default), **json**, **plain**
|
||||
|
||||
When you use **-F json**, the CLI prints each client message as JSON.
|
||||
|
||||
Each message is a **ClineMessage** object.
|
||||
|
||||
Required fields:
|
||||
|
||||
- **type**: "ask" or "say"
|
||||
- **text**: message text
|
||||
- **ts**: Unix epoch timestamp in milliseconds
|
||||
|
||||
Optional fields (omitted when empty):
|
||||
|
||||
- **reasoning**: reasoning text
|
||||
- **say**: say subtype (present when type is "say")
|
||||
- **ask**: ask subtype (present when type is "ask")
|
||||
- **partial**: streaming flag
|
||||
- **images**: list of image URIs
|
||||
- **files**: list of file paths
|
||||
- **lastCheckpointHash**: git checkpoint hash
|
||||
- **isCheckpointCheckedOut**: checkpoint checkout flag
|
||||
- **isOperationOutsideWorkspace**: workspace safety flag
|
||||
|
||||
**-h**, **\--help**
|
||||
|
||||
: Display help information for the command.
|
||||
|
||||
@@ -47,7 +47,7 @@ func GetProviderConfigurations(ctx context.Context, manager *task.Manager) (*Pro
|
||||
}
|
||||
|
||||
// Parse state_json as map[string]interface{}
|
||||
var stateData map[string]interface{}
|
||||
var stateData map[string]any
|
||||
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]interface{})
|
||||
apiConfig, ok := stateData["apiConfiguration"].(map[string]any)
|
||||
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 := checkAPIKeyExists(r.apiConfig, provider)
|
||||
hasCreds := checkCredentialsExists(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: checkAPIKeyExists(r.apiConfig, provider),
|
||||
HasAPIKey: checkCredentialsExists(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
|
||||
hasAPIKey := checkAPIKeyExists(stateData, provider)
|
||||
hasCredentials := checkCredentialsExists(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: hasAPIKey,
|
||||
HasAPIKey: hasCredentials,
|
||||
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,27 @@ func getProviderSpecificModelID(stateData map[string]interface{}, mode string, p
|
||||
return modelID
|
||||
}
|
||||
|
||||
// checkAPIKeyExists checks if API key field exists in state (never retrieve actual key)
|
||||
func checkAPIKeyExists(stateData map[string]interface{}, provider cline.ApiProvider) bool {
|
||||
// checkCredentialsExists checks if API key field exists in state (never retrieve actual key)
|
||||
func checkCredentialsExists(stateData map[string]interface{}, provider cline.ApiProvider) bool {
|
||||
// Get field mapping from centralized function
|
||||
fields, err := GetProviderFields(provider)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
keyField := fields.APIKeyField
|
||||
|
||||
// Check if the key exists and is not empty
|
||||
if value, ok := stateData[keyField]; ok {
|
||||
if value, ok := stateData[fields.APIKeyField]; ok {
|
||||
if str, ok := value.(string); ok && str != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := stateData[fields.UseProfileField]; ok {
|
||||
if hasProfileField, ok := value.(bool); ok && hasProfileField {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -438,13 +442,13 @@ func DetectAllConfiguredProviders(ctx context.Context, manager *task.Manager) ([
|
||||
stateJSON := state.StateJson
|
||||
|
||||
// Parse state_json as map[string]interface{}
|
||||
var stateData map[string]interface{}
|
||||
var stateData map[string]any
|
||||
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]interface{})
|
||||
apiConfig, ok := stateData["apiConfiguration"].(map[string]any)
|
||||
if !ok {
|
||||
verboseLog("[DEBUG] No apiConfiguration found in state")
|
||||
verboseLog("[DEBUG] Available keys in stateData: %v", getMapKeys(stateData))
|
||||
@@ -469,36 +473,38 @@ func DetectAllConfiguredProviders(ctx context.Context, manager *task.Manager) ([
|
||||
|
||||
// Check each BYO provider for API key presence
|
||||
providersToCheck := []struct {
|
||||
provider cline.ApiProvider
|
||||
keyField string
|
||||
provider cline.ApiProvider
|
||||
keyFields []string
|
||||
}{
|
||||
{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"},
|
||||
{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"}},
|
||||
}
|
||||
|
||||
for _, providerCheck := range providersToCheck {
|
||||
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))
|
||||
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)
|
||||
}
|
||||
} 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,6 +54,7 @@ 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
|
||||
@@ -96,6 +97,7 @@ func GetProviderFields(provider cline.ApiProvider) (ProviderFields, error) {
|
||||
|
||||
case cline.ApiProvider_BEDROCK:
|
||||
return ProviderFields{
|
||||
UseProfileField: "awsUseProfile",
|
||||
APIKeyField: "awsAccessKey",
|
||||
PlanModeModelIDField: "planModeApiModelId",
|
||||
ActModeModelIDField: "actModeApiModelId",
|
||||
|
||||
@@ -517,7 +517,7 @@ func (pw *ProviderWizard) applyModelChange(provider cline.ApiProvider, modelID s
|
||||
ModelInfo: modelInfo,
|
||||
}
|
||||
|
||||
return UpdateProviderPartial(pw.ctx, pw.manager, provider, updates, false)
|
||||
return UpdateProviderPartial(pw.ctx, pw.manager, provider, updates, true)
|
||||
}
|
||||
|
||||
// SwitchToBYOProvider switches to a BYO provider that's already configured.
|
||||
|
||||
@@ -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,7 +130,12 @@ func ApplyBedrockConfig(ctx context.Context, manager *task.Manager, config *Bedr
|
||||
// Build the API configuration with all Bedrock fields
|
||||
apiConfig := &cline.ModelsApiConfiguration{}
|
||||
|
||||
// Set model ID fields
|
||||
// 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
|
||||
apiConfig.PlanModeApiModelId = proto.String(modelID)
|
||||
apiConfig.ActModeApiModelId = proto.String(modelID)
|
||||
apiConfig.PlanModeAwsBedrockCustomModelBaseId = proto.String(modelID)
|
||||
@@ -166,6 +171,8 @@ 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",
|
||||
|
||||
@@ -92,7 +92,6 @@ func (m *Manager) ListSettings(ctx context.Context) error {
|
||||
"telemetrySetting",
|
||||
"planActSeparateModelsSetting",
|
||||
"enableCheckpointsSetting",
|
||||
"mcpMarketplaceEnabled",
|
||||
"shellIntegrationTimeout",
|
||||
"terminalReuseEnabled",
|
||||
"mcpResponsesCollapsed",
|
||||
@@ -111,6 +110,7 @@ func (m *Manager) ListSettings(ctx context.Context) error {
|
||||
"dictationSettings",
|
||||
"autoCondenseThreshold",
|
||||
"autoApprovalSettings",
|
||||
"hooksEnabled",
|
||||
}
|
||||
|
||||
// Render each field using the renderer
|
||||
|
||||
@@ -77,10 +77,9 @@ func RenderField(key string, value interface{}, censor bool) error {
|
||||
case "mode", "telemetrySetting", "preferredLanguage", "customPrompt",
|
||||
"defaultTerminalProfile", "mcpDisplayMode", "openaiReasoningEffort",
|
||||
"planActSeparateModelsSetting", "enableCheckpointsSetting",
|
||||
"mcpMarketplaceEnabled", "terminalReuseEnabled",
|
||||
"mcpResponsesCollapsed", "strictPlanModeEnabled",
|
||||
"terminalReuseEnabled", "mcpResponsesCollapsed", "strictPlanModeEnabled",
|
||||
"useAutoCondense", "yoloModeToggled", "shellIntegrationTimeout",
|
||||
"terminalOutputLineLimit", "autoCondenseThreshold":
|
||||
"terminalOutputLineLimit", "autoCondenseThreshold", "hooksEnabled":
|
||||
fmt.Printf("%s: %s\n", camelToKebab(key), formatValue(value, key, censor))
|
||||
return nil
|
||||
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
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
|
||||
Workdir string
|
||||
Mode string
|
||||
Version string
|
||||
Provider string
|
||||
ModelID string
|
||||
Workdirs []string // workspace directories
|
||||
Mode string
|
||||
}
|
||||
|
||||
// RenderSessionBanner renders a nice banner showing version, model, and workspace info
|
||||
@@ -81,131 +78,22 @@ func RenderSessionBanner(info BannerInfo) string {
|
||||
|
||||
// Model line - dim gray
|
||||
if info.Provider != "" && info.ModelID != "" {
|
||||
lines = append(lines, dimStyle.Render(info.Provider+"/"+shortenPath(info.ModelID, 30)))
|
||||
lines = append(lines, dimStyle.Render(info.Provider+"/"+common.ShortenPath(info.ModelID, 30)))
|
||||
}
|
||||
|
||||
// Workspace line - dim gray
|
||||
if info.Workdir != "" {
|
||||
lines = append(lines, dimStyle.Render(shortenPath(info.Workdir, 45)))
|
||||
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"))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package display
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cline/cli/pkg/cli/types"
|
||||
)
|
||||
|
||||
// HookRenderer renders hook status messages in a CLI-native style.
|
||||
//
|
||||
// Goals:
|
||||
// - Match ToolRenderer’s markdown look
|
||||
// - Keep executions ungrouped
|
||||
// - Render status + high-signal metadata (script paths, error summary)
|
||||
//
|
||||
// Note: hook stdout/stderr currently arrives as separate `hook_output_stream` messages.
|
||||
// The CLI suppresses those by default and prints them only in --verbose mode.
|
||||
// Future work could group streamed output under the corresponding hook block.
|
||||
//
|
||||
// It returns markdown (or rendered markdown when enabled); callers should print the
|
||||
// returned string.
|
||||
|
||||
type HookRenderer struct {
|
||||
mdRenderer *MarkdownRenderer
|
||||
outputFormat string
|
||||
}
|
||||
|
||||
func NewHookRenderer(mdRenderer *MarkdownRenderer, outputFormat string) *HookRenderer {
|
||||
return &HookRenderer{mdRenderer: mdRenderer, outputFormat: outputFormat}
|
||||
}
|
||||
|
||||
func (hr *HookRenderer) RenderHookStatus(h types.HookMessage) string {
|
||||
statusText := strings.TrimSpace(h.Status)
|
||||
if statusText == "" {
|
||||
statusText = "unknown"
|
||||
}
|
||||
|
||||
// Header: aligned with ToolRenderer’s phrasing so transcripts scan consistently.
|
||||
// Example: "### Cline hook completed: PreToolUse (tool: read_file) (exit 0)"
|
||||
var headerBuilder strings.Builder
|
||||
headerBuilder.WriteString(fmt.Sprintf("### Cline hook %s: %s", statusText, h.HookName))
|
||||
if h.ToolName != "" {
|
||||
headerBuilder.WriteString(" ")
|
||||
headerBuilder.WriteString(fmt.Sprintf("(tool: %s)", h.ToolName))
|
||||
}
|
||||
if statusText == "failed" && h.ExitCode != 0 {
|
||||
headerBuilder.WriteString(" ")
|
||||
headerBuilder.WriteString(fmt.Sprintf("(exit %d)", h.ExitCode))
|
||||
}
|
||||
header := headerBuilder.String()
|
||||
|
||||
var lines []string
|
||||
lines = append(lines, header)
|
||||
|
||||
// Pending tool info (PreToolUse): show one high-signal line directly under the header.
|
||||
if h.PendingToolInfo != nil {
|
||||
if pending := hr.formatPendingToolInfo(h.PendingToolInfo); pending != "" {
|
||||
lines = append(lines, fmt.Sprintf("- Pending: %s", pending))
|
||||
}
|
||||
}
|
||||
|
||||
// Script paths: one per line.
|
||||
paths := make([]string, 0, len(h.ScriptPaths))
|
||||
for _, p := range h.ScriptPaths {
|
||||
p = strings.TrimSpace(p)
|
||||
if p != "" {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
// Fallback when no script paths are provided.
|
||||
lines = append(lines, "- *(no hook scripts found)*")
|
||||
} else {
|
||||
for _, p := range paths {
|
||||
lines = append(lines, fmt.Sprintf("- Running hook: `%s`", p))
|
||||
}
|
||||
}
|
||||
|
||||
// On failure, show a minimal summary (full stderr reserved for verbose).
|
||||
if statusText == "failed" && h.Error != nil {
|
||||
if msg := strings.TrimSpace(h.Error.Message); msg != "" {
|
||||
lines = append(lines, fmt.Sprintf("- Error: %s", msg))
|
||||
}
|
||||
// If we have a specific script path, include it as a hint.
|
||||
if sp := strings.TrimSpace(h.Error.ScriptPath); sp != "" {
|
||||
lines = append(lines, fmt.Sprintf("- Script: `%s`", sp))
|
||||
}
|
||||
}
|
||||
|
||||
markdown := strings.Join(lines, "\n")
|
||||
return hr.renderMarkdown(markdown)
|
||||
}
|
||||
|
||||
func (hr *HookRenderer) formatPendingToolInfo(info *types.ToolInfo) string {
|
||||
if info == nil {
|
||||
return ""
|
||||
}
|
||||
tool := strings.TrimSpace(info.Tool)
|
||||
if tool == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Keep this intentionally compact and readable.
|
||||
// Format: "<tool> <identifier>" where identifier is the most relevant param.
|
||||
var ident string
|
||||
switch {
|
||||
case strings.TrimSpace(info.Path) != "":
|
||||
ident = strings.TrimSpace(info.Path)
|
||||
case strings.TrimSpace(info.Command) != "":
|
||||
ident = strings.TrimSpace(info.Command)
|
||||
case strings.TrimSpace(info.Url) != "":
|
||||
ident = strings.TrimSpace(info.Url)
|
||||
case strings.TrimSpace(info.McpTool) != "" && strings.TrimSpace(info.McpServer) != "":
|
||||
ident = fmt.Sprintf("%s %s", strings.TrimSpace(info.McpServer), strings.TrimSpace(info.McpTool))
|
||||
case strings.TrimSpace(info.ResourceUri) != "":
|
||||
ident = strings.TrimSpace(info.ResourceUri)
|
||||
case strings.TrimSpace(info.Regex) != "":
|
||||
ident = strings.TrimSpace(info.Regex)
|
||||
default:
|
||||
ident = ""
|
||||
}
|
||||
|
||||
if ident != "" {
|
||||
return fmt.Sprintf("%s %s", tool, ident)
|
||||
}
|
||||
return tool
|
||||
}
|
||||
|
||||
func (hr *HookRenderer) renderMarkdown(markdown string) string {
|
||||
// Align with ToolRenderer: in plain mode or non-TTY, return markdown as-is.
|
||||
if hr.outputFormat == "plain" || !isTTY() {
|
||||
return markdown
|
||||
}
|
||||
if hr.mdRenderer == nil {
|
||||
return markdown
|
||||
}
|
||||
rendered, err := hr.mdRenderer.Render(markdown)
|
||||
if err != nil {
|
||||
return markdown
|
||||
}
|
||||
return rendered
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package display
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/cline/cli/pkg/cli/types"
|
||||
)
|
||||
|
||||
func TestHookRenderer_RenderHookStatus_FailedShowsErrorAndScript(t *testing.T) {
|
||||
hr := NewHookRenderer(nil, "plain")
|
||||
|
||||
msg := hr.RenderHookStatus(types.HookMessage{
|
||||
HookName: "PreToolUse",
|
||||
ToolName: "execute_command",
|
||||
Status: "failed",
|
||||
ExitCode: 2,
|
||||
ScriptPaths: []string{"repo/.clinerules/hooks/PreToolUse"},
|
||||
Error: &types.HookError{
|
||||
Message: "boom",
|
||||
ScriptPath: "repo/.clinerules/hooks/PreToolUse",
|
||||
},
|
||||
})
|
||||
|
||||
if !strings.Contains(msg, "### Cline hook failed: PreToolUse") {
|
||||
t.Fatalf("expected header in rendered output, got: %q", msg)
|
||||
}
|
||||
if !strings.Contains(msg, "- Error: boom") {
|
||||
t.Fatalf("expected error line in rendered output, got: %q", msg)
|
||||
}
|
||||
if !strings.Contains(msg, "- Script: `repo/.clinerules/hooks/PreToolUse`") {
|
||||
t.Fatalf("expected script line in rendered output, got: %q", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHookRenderer_RenderHookStatus_PendingToolInfoAppearsDirectlyUnderHeader(t *testing.T) {
|
||||
hr := NewHookRenderer(nil, "plain")
|
||||
|
||||
msg := hr.RenderHookStatus(types.HookMessage{
|
||||
HookName: "PreToolUse",
|
||||
ToolName: "write_to_file",
|
||||
Status: "running",
|
||||
PendingToolInfo: &types.ToolInfo{
|
||||
Tool: "write_to_file",
|
||||
Path: "src/foo.ts",
|
||||
},
|
||||
ScriptPaths: []string{"repo/.clinerules/hooks/PreToolUse"},
|
||||
})
|
||||
|
||||
header := "### Cline hook running: PreToolUse"
|
||||
pending := "- Pending: write_to_file src/foo.ts"
|
||||
runningHook := "- Running hook: `repo/.clinerules/hooks/PreToolUse`"
|
||||
|
||||
headerIdx := strings.Index(msg, header)
|
||||
if headerIdx == -1 {
|
||||
t.Fatalf("expected header %q in output, got: %q", header, msg)
|
||||
}
|
||||
pendingIdx := strings.Index(msg, pending)
|
||||
if pendingIdx == -1 {
|
||||
t.Fatalf("expected pending line %q in output, got: %q", pending, msg)
|
||||
}
|
||||
runningIdx := strings.Index(msg, runningHook)
|
||||
if runningIdx == -1 {
|
||||
t.Fatalf("expected running hook line %q in output, got: %q", runningHook, msg)
|
||||
}
|
||||
if !(headerIdx < pendingIdx && pendingIdx < runningIdx) {
|
||||
t.Fatalf("expected header < pending < runningHook ordering, got indexes header=%d pending=%d running=%d\nfull=%q", headerIdx, pendingIdx, runningIdx, msg)
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,12 @@ func NewStreamingSegment(sayType, prefix string, mdRenderer *MarkdownRenderer, s
|
||||
// Render rich header immediately when creating segment (if in rich mode and TTY)
|
||||
if shouldMarkdown && outputFormat != "plain" && isTTY() {
|
||||
header := ss.generateRichHeader()
|
||||
rendered, _ := mdRenderer.Render(header)
|
||||
output.Println("")
|
||||
output.Print(rendered)
|
||||
// Skip empty headers.
|
||||
if strings.TrimSpace(header) != "" {
|
||||
rendered, _ := mdRenderer.Render(header)
|
||||
output.Println("")
|
||||
output.Print(rendered)
|
||||
}
|
||||
}
|
||||
|
||||
return ss
|
||||
@@ -110,6 +113,9 @@ func (ss *StreamingSegment) renderFinal(currentBuffer string) {
|
||||
if err := json.Unmarshal([]byte(currentBuffer), &tool); err == nil {
|
||||
bodyContent = ss.toolRenderer.GenerateToolContentBody(&tool)
|
||||
}
|
||||
} else if ss.sayType == string(types.SayTypeHookStatus) {
|
||||
// Hooks are rendered via the state stream; nothing to render here.
|
||||
bodyContent = ""
|
||||
} else if ss.sayType == string(types.SayTypeCommand) {
|
||||
// Command output
|
||||
bodyContent = "```shell\n" + currentBuffer + "\n```"
|
||||
@@ -160,6 +166,10 @@ func (ss *StreamingSegment) generateRichHeader() string {
|
||||
|
||||
case string(types.SayTypeTool):
|
||||
return ss.generateToolHeader()
|
||||
|
||||
case string(types.SayTypeHookStatus):
|
||||
// Hooks are rendered from the state stream; don’t emit a partial-stream header.
|
||||
return ""
|
||||
|
||||
case "ask":
|
||||
// Check the specific ask type
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package display
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cline/cli/pkg/cli/types"
|
||||
)
|
||||
|
||||
func TestStreamingSegment_generateRichHeader_HookIsEmpty(t *testing.T) {
|
||||
ss := &StreamingSegment{
|
||||
sayType: string(types.SayTypeHookStatus),
|
||||
prefix: "HOOK",
|
||||
msg: &types.ClineMessage{},
|
||||
}
|
||||
|
||||
header := ss.generateRichHeader()
|
||||
if header != "" {
|
||||
t.Fatalf("expected empty header for hook segments to avoid double-render, got: %q", header)
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,18 @@ func (s *StreamingDisplay) HandlePartialMessage(msg *types.ClineMessage) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// Render hooks from the state stream only (not partial stream) to avoid duplicates.
|
||||
//
|
||||
// Rationale: hook status messages are often updated/reordered by the backend (e.g. PreToolUse
|
||||
// hooks are moved above the corresponding tool message). The state stream represents the
|
||||
// authoritative, “final” message ordering, while the partial stream is best-effort for
|
||||
// incremental display.
|
||||
//
|
||||
// Only suppress *partial* hook messages; complete ones still flow through dedupe.
|
||||
if msg.Partial && msg.Say == string(types.SayTypeHookStatus) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check for deduplication
|
||||
if s.dedupe.IsDuplicate(msg) {
|
||||
return nil
|
||||
@@ -91,7 +103,11 @@ func (s *StreamingDisplay) HandlePartialMessage(msg *types.ClineMessage) error {
|
||||
|
||||
func (s *StreamingDisplay) shouldRenderMarkdown(sayType string) bool {
|
||||
switch sayType {
|
||||
case string(types.SayTypeReasoning), string(types.SayTypeText), string(types.SayTypeCompletionResult), string(types.SayTypeTool), "ask":
|
||||
case string(types.SayTypeReasoning),
|
||||
string(types.SayTypeText),
|
||||
string(types.SayTypeCompletionResult),
|
||||
string(types.SayTypeTool),
|
||||
"ask":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -110,6 +126,8 @@ func (s *StreamingDisplay) getPrefix(sayType string) string {
|
||||
return "ASK"
|
||||
case string(types.SayTypeCommand):
|
||||
return "TERMINAL"
|
||||
case string(types.SayTypeHookStatus):
|
||||
return "HOOK"
|
||||
default:
|
||||
return strings.ToUpper(sayType)
|
||||
}
|
||||
|
||||
@@ -264,6 +264,6 @@ func (sr *SystemMessageRenderer) RenderInfo(title, message string) error {
|
||||
func (sr *SystemMessageRenderer) RenderCheckpoint(timestamp string, id int64) error {
|
||||
markdown := fmt.Sprintf("## [%s] Checkpoint created `%d`", timestamp, id)
|
||||
rendered := sr.renderer.RenderMarkdown(markdown)
|
||||
fmt.Printf(rendered)
|
||||
fmt.Print(rendered)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -161,6 +161,14 @@ func (tr *ToolRenderer) generateToolHeader(tool *types.ToolMessage, verbTense st
|
||||
}
|
||||
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
||||
|
||||
case string(types.ToolTypeWebSearch):
|
||||
if verbTense == "wants to" {
|
||||
action = "wants to search for"
|
||||
} else {
|
||||
action = "is searching for"
|
||||
}
|
||||
return fmt.Sprintf("### Cline %s `%s`", action, tool.Path)
|
||||
|
||||
case string(types.ToolTypeListCodeDefinitionNames):
|
||||
if verbTense == "wants to" {
|
||||
action = "wants to list code definitions in"
|
||||
@@ -207,8 +215,8 @@ func (tr *ToolRenderer) GenerateToolContentPreview(tool *types.ToolMessage) stri
|
||||
previewMd := fmt.Sprintf("```\n%s\n```", preview)
|
||||
return tr.renderMarkdown(previewMd)
|
||||
|
||||
case string(types.ToolTypeReadFile), string(types.ToolTypeWebFetch), string(types.ToolTypeFileDeleted):
|
||||
// No preview for read/fetch operations
|
||||
case string(types.ToolTypeReadFile), string(types.ToolTypeWebFetch), string(types.ToolTypeWebSearch), string(types.ToolTypeFileDeleted):
|
||||
// No preview for read/fetch/search operations
|
||||
return ""
|
||||
|
||||
default:
|
||||
@@ -243,7 +251,8 @@ func (tr *ToolRenderer) GenerateToolContentBody(tool *types.ToolMessage) string
|
||||
string(types.ToolTypeListFilesRecursive),
|
||||
string(types.ToolTypeListCodeDefinitionNames),
|
||||
string(types.ToolTypeSearchFiles),
|
||||
string(types.ToolTypeWebFetch):
|
||||
string(types.ToolTypeWebFetch),
|
||||
string(types.ToolTypeWebSearch):
|
||||
// Use parser for structured output
|
||||
preview := toolParser.ParseToolResult(tool)
|
||||
return tr.renderMarkdown(preview)
|
||||
|
||||
@@ -221,83 +221,12 @@ func (p *ToolResultParser) ParseCodeDefinitions(content string) string {
|
||||
|
||||
// ParseWebFetch formats webFetch tool results with content preview
|
||||
func (p *ToolResultParser) ParseWebFetch(content, url string) string {
|
||||
if content == "" {
|
||||
return fmt.Sprintf("*Fetched content from %s (empty response)*", url)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
lines := strings.Split(content, "\n")
|
||||
|
||||
var result strings.Builder
|
||||
|
||||
// Try to extract title
|
||||
var title string
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmed, "#") && !strings.HasPrefix(trimmed, "##") {
|
||||
title = strings.TrimSpace(strings.TrimPrefix(trimmed, "#"))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if title != "" {
|
||||
result.WriteString(fmt.Sprintf("**Title:** %s\n\n", title))
|
||||
}
|
||||
|
||||
// Show preview of content
|
||||
result.WriteString("**Preview:**\n")
|
||||
|
||||
charCount := 0
|
||||
maxChars := 500
|
||||
previewLines := []string{}
|
||||
|
||||
for _, line := range lines {
|
||||
// Skip markdown headers
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if charCount+len(trimmed) > maxChars {
|
||||
break
|
||||
}
|
||||
|
||||
previewLines = append(previewLines, trimmed)
|
||||
charCount += len(trimmed)
|
||||
}
|
||||
|
||||
result.WriteString(strings.Join(previewLines, " "))
|
||||
result.WriteString("...\n\n")
|
||||
|
||||
// Extract sections
|
||||
sections := []string{}
|
||||
for _, line := range lines {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmed, "##") {
|
||||
section := strings.TrimSpace(strings.TrimPrefix(trimmed, "##"))
|
||||
sections = append(sections, section)
|
||||
if len(sections) >= 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(sections) > 0 {
|
||||
result.WriteString("**Sections Found:**\n")
|
||||
for _, section := range sections {
|
||||
result.WriteString(fmt.Sprintf("- %s\n", section))
|
||||
}
|
||||
result.WriteString("\n")
|
||||
}
|
||||
|
||||
// Word count estimate
|
||||
wordCount := len(strings.Fields(content))
|
||||
result.WriteString(fmt.Sprintf("*[Full content: ~%s]*", p.formatWordCount(wordCount)))
|
||||
|
||||
return result.String()
|
||||
// ParseWebSearch formats webSearch tool results
|
||||
func (p *ToolResultParser) ParseWebSearch(content, query string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// detectLanguage returns syntax highlighting language based on file extension
|
||||
@@ -365,6 +294,8 @@ func (p *ToolResultParser) ParseToolResult(tool *types.ToolMessage) string {
|
||||
return p.ParseCodeDefinitions(tool.Content)
|
||||
case "webFetch":
|
||||
return p.ParseWebFetch(tool.Content, tool.Path)
|
||||
case "webSearch":
|
||||
return p.ParseWebSearch(tool.Content, tool.Path)
|
||||
default:
|
||||
return tool.Content
|
||||
}
|
||||
|
||||
@@ -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) (*common.CoreInstanceInfo, error) {
|
||||
func (c *ClineClients) StartNewInstance(ctx context.Context, workspaces ...string) (*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) (*common.CoreInstan
|
||||
}
|
||||
|
||||
// Start cline-host first
|
||||
hostCmd, err := startClineHost(hostPort, corePort)
|
||||
hostCmd, err := startClineHost(hostPort, workspaces)
|
||||
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) (*common.CoreInstan
|
||||
}
|
||||
|
||||
// StartNewInstanceAtPort starts a new Cline instance at the specified port and waits for self-registration
|
||||
func (c *ClineClients) StartNewInstanceAtPort(ctx context.Context, corePort int) (*common.CoreInstanceInfo, error) {
|
||||
func (c *ClineClients) StartNewInstanceAtPort(ctx context.Context, corePort int, workspaces ...string) (*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, corePort)
|
||||
hostCmd, err := startClineHost(hostPort, workspaces)
|
||||
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, corePort int) (*exec.Cmd, error) {
|
||||
func startClineHost(hostPort int, workspaces []string) (*exec.Cmd, error) {
|
||||
if Config.Verbose {
|
||||
fmt.Printf("Starting cline-host on port %d\n", hostPort)
|
||||
}
|
||||
@@ -255,10 +255,18 @@ func startClineHost(hostPort, corePort int) (*exec.Cmd, error) {
|
||||
binDir := path.Dir(execPath)
|
||||
clineHostPath := path.Join(binDir, "cline-host")
|
||||
|
||||
// Start the cline-host process
|
||||
cmd := exec.Command(clineHostPath,
|
||||
// Build command arguments
|
||||
args := []string{
|
||||
"--verbose",
|
||||
"--port", fmt.Sprintf("%d", hostPort))
|
||||
"--port", fmt.Sprintf("%d", hostPort),
|
||||
}
|
||||
|
||||
for _, ws := range workspaces {
|
||||
args = append(args, "--workspace", ws)
|
||||
}
|
||||
|
||||
// Start the cline-host process
|
||||
cmd := exec.Command(clineHostPath, args...)
|
||||
|
||||
// Create logs directory in ~/.cline/logs
|
||||
logsDir := path.Join(Config.ConfigPath, "logs")
|
||||
@@ -333,7 +341,7 @@ func KillInstanceByAddress(ctx context.Context, registry *ClientRegistry, addres
|
||||
if Config.Verbose {
|
||||
fmt.Printf("Waiting for instance to clean up registry entry...\n")
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
for range 5 {
|
||||
time.Sleep(1 * time.Second)
|
||||
if !registry.HasInstanceAtAddress(address) {
|
||||
if Config.Verbose {
|
||||
@@ -408,15 +416,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 {
|
||||
@@ -475,15 +483,16 @@ 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),
|
||||
"GRPC_TRACE=all",
|
||||
"GRPC_VERBOSITY=DEBUG",
|
||||
// These control gRPC debug logging
|
||||
//"GRPC_TRACE=all",
|
||||
//"GRPC_VERBOSITY=DEBUG",
|
||||
"NODE_ENV=development",
|
||||
)
|
||||
cmd.Env = env
|
||||
|
||||
|
||||
if Config.Verbose {
|
||||
fmt.Printf("NODE_PATH set to: %s\n", nodePath)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ type DisplayContext struct {
|
||||
State *types.ConversationState
|
||||
Renderer *display.Renderer
|
||||
ToolRenderer *display.ToolRenderer
|
||||
HookRenderer *display.HookRenderer
|
||||
SystemRenderer *display.SystemMessageRenderer
|
||||
IsLast bool
|
||||
IsPartial bool
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/cline/cli/pkg/cli/clerror"
|
||||
"github.com/cline/cli/pkg/cli/types"
|
||||
"github.com/cline/cli/pkg/cli/output"
|
||||
"github.com/cline/cli/pkg/cli/types"
|
||||
)
|
||||
|
||||
// SayHandler handles SAY type messages
|
||||
@@ -90,6 +90,10 @@ func (h *SayHandler) Handle(msg *types.ClineMessage, dc *DisplayContext) error {
|
||||
return h.handleInfo(msg, dc)
|
||||
case string(types.SayTypeTaskProgress):
|
||||
return h.handleTaskProgress(msg, dc)
|
||||
case string(types.SayTypeHookStatus):
|
||||
return h.handleHookStatus(msg, dc)
|
||||
case string(types.SayTypeHookOutputStream):
|
||||
return h.handleHookOutputStream(msg, dc)
|
||||
default:
|
||||
return h.handleDefault(msg, dc)
|
||||
}
|
||||
@@ -242,18 +246,17 @@ func (h *SayHandler) handleCompletionResult(msg *types.ClineMessage, dc *Display
|
||||
}
|
||||
|
||||
func formatUserMessage(text string) string {
|
||||
lines := strings.Split(text, "\n")
|
||||
|
||||
// Wrap each line in backticks
|
||||
for i, line := range lines {
|
||||
if line != "" {
|
||||
lines[i] = fmt.Sprintf("`%s`", line)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
lines := strings.Split(text, "\n")
|
||||
|
||||
// Wrap each line in backticks
|
||||
for i, line := range lines {
|
||||
if line != "" {
|
||||
lines[i] = fmt.Sprintf("`%s`", line)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// handleUserFeedback handles user feedback messages
|
||||
func (h *SayHandler) handleUserFeedback(msg *types.ClineMessage, dc *DisplayContext) error {
|
||||
@@ -517,5 +520,16 @@ func (h *SayHandler) handleTaskProgress(msg *types.ClineMessage, dc *DisplayCont
|
||||
|
||||
// handleDefault handles unknown SAY message types
|
||||
func (h *SayHandler) handleDefault(msg *types.ClineMessage, dc *DisplayContext) error {
|
||||
// Debug: log unhandled say types to help identify missing cases using output.Printf for CLI consistency
|
||||
if dc.Verbose {
|
||||
output.Printf("[DEBUG] Unhandled SAY type: '%s' (text preview: %s)\n", msg.Say, truncateForDisplay(msg.Text, 50))
|
||||
}
|
||||
return dc.Renderer.RenderMessage("SAY", msg.Text, true)
|
||||
}
|
||||
|
||||
func truncateForDisplay(text string, maxLen int) string {
|
||||
if len(text) <= maxLen {
|
||||
return text
|
||||
}
|
||||
return text[:maxLen] + "..."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/cline/cli/pkg/cli/output"
|
||||
"github.com/cline/cli/pkg/cli/types"
|
||||
)
|
||||
|
||||
// Hook-specific SAY handlers and helpers.
|
||||
// Kept in a separate file to keep say_handlers.go focused on routing.
|
||||
|
||||
// handleHookStatus handles hook execution status messages.
|
||||
func (h *SayHandler) handleHookStatus(msg *types.ClineMessage, dc *DisplayContext) error {
|
||||
hook, err := parseHookMessage(msg.Text)
|
||||
if err != nil {
|
||||
// Fallback to basic output if JSON parsing fails
|
||||
return dc.Renderer.RenderMessage("HOOK", msg.Text, true)
|
||||
}
|
||||
|
||||
logHookDebug(hook, dc)
|
||||
hook.ScriptPaths = formatHookPaths(hook.ScriptPaths)
|
||||
|
||||
return renderHookStatus(hook, dc)
|
||||
}
|
||||
|
||||
// handleHookOutputStream handles streaming output from hooks.
|
||||
//
|
||||
// Hook stdout/stderr currently arrives line-by-line from the backend as
|
||||
// `hook_output_stream` messages. The CLI intentionally suppresses these by default
|
||||
// to keep the transcript high-signal.
|
||||
//
|
||||
// In --verbose mode, we print each non-empty line prefixed with "HOOK>" for easy grepping.
|
||||
// Future work could associate these lines with a specific hook execution and render them
|
||||
// as a grouped section under the hook status header.
|
||||
func (h *SayHandler) handleHookOutputStream(msg *types.ClineMessage, dc *DisplayContext) error {
|
||||
if !dc.Verbose {
|
||||
return nil
|
||||
}
|
||||
|
||||
line := strings.TrimRight(msg.Text, "\n")
|
||||
if strings.TrimSpace(line) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
output.Printf("HOOK> %s\n", line)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseHookMessage(jsonText string) (types.HookMessage, error) {
|
||||
var hook types.HookMessage
|
||||
if err := json.Unmarshal([]byte(jsonText), &hook); err != nil {
|
||||
return types.HookMessage{}, err
|
||||
}
|
||||
return hook, nil
|
||||
}
|
||||
|
||||
func logHookDebug(hook types.HookMessage, dc *DisplayContext) {
|
||||
if dc.Verbose {
|
||||
output.Printf("[DEBUG] Hook parsed: name=%s, status=%s, toolName=%s, scriptPaths=%v\n",
|
||||
hook.HookName, hook.Status, hook.ToolName, hook.ScriptPaths)
|
||||
}
|
||||
}
|
||||
|
||||
func formatHookPaths(paths []string) []string {
|
||||
if len(paths) == 0 {
|
||||
return paths
|
||||
}
|
||||
formatted := make([]string, 0, len(paths))
|
||||
for _, p := range paths {
|
||||
if strings.TrimSpace(p) == "" {
|
||||
continue
|
||||
}
|
||||
formatted = append(formatted, formatHookPath(p))
|
||||
}
|
||||
return formatted
|
||||
}
|
||||
|
||||
func renderHookStatus(hook types.HookMessage, dc *DisplayContext) error {
|
||||
if dc.HookRenderer != nil {
|
||||
rendered := dc.HookRenderer.RenderHookStatus(hook)
|
||||
// Match ToolRenderer’s spacing: one leading newline, one trailing newline.
|
||||
output.Print("\n")
|
||||
output.Print(rendered)
|
||||
output.Print("\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback: if HookRenderer not available
|
||||
return dc.Renderer.RenderMessage("HOOK", fmt.Sprintf("%s %s", hook.HookName, hook.Status), true)
|
||||
}
|
||||
|
||||
func formatHookPath(fullPath string) string {
|
||||
// Normalize for display and prefix checks. This is display-only; do not use for IO.
|
||||
normalized := normalizeSlashes(fullPath)
|
||||
|
||||
// If this is a repo-scoped hook script (i.e. lives under <repo>/.clinerules/hooks/),
|
||||
// always include the repo name for disambiguation even in single-repo workspaces.
|
||||
//
|
||||
// This intentionally runs before workspace-relative formatting, which would otherwise
|
||||
// collapse to ".clinerules/hooks/..." and lose the repo context.
|
||||
if p, ok := tryRepoScopedHooksPath(normalized); ok {
|
||||
return p
|
||||
}
|
||||
|
||||
// Prefer workspace-relative paths first for readability, since most hook scripts
|
||||
// live inside the current project.
|
||||
if p, ok := tryWorkspaceRelativeHookPath(normalized); ok {
|
||||
return p
|
||||
}
|
||||
|
||||
// Follow existing CLI pattern: resolve home via os.UserHomeDir.
|
||||
if p, ok := tryHomeTildePath(normalized); ok {
|
||||
return p
|
||||
}
|
||||
|
||||
// Secondary heuristic: if hook lives under <repo>/.clinerules, collapse to repo-relative.
|
||||
if p, ok := tryRepoRelativeHookPath(normalized); ok {
|
||||
return p
|
||||
}
|
||||
|
||||
return fallbackLastComponents(normalized, 3)
|
||||
}
|
||||
|
||||
func normalizeSlashes(p string) string {
|
||||
return filepath.ToSlash(p)
|
||||
}
|
||||
|
||||
func tryWorkspaceRelativeHookPath(normalizedPath string) (string, bool) {
|
||||
root, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// filepath.Rel expects OS-native paths, so we need to convert the normalized path
|
||||
// back to OS-native format before calling Rel, then normalize the result for display.
|
||||
targetOS := filepath.FromSlash(normalizedPath)
|
||||
rel, err := filepath.Rel(root, targetOS)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
// If it's not within the workspace, Rel will start with "..".
|
||||
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||
return "", false
|
||||
}
|
||||
return normalizeSlashes(rel), true
|
||||
}
|
||||
|
||||
func tryHomeTildePath(normalizedPath string) (string, bool) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil || strings.TrimSpace(homeDir) == "" {
|
||||
return "", false
|
||||
}
|
||||
homeDir = normalizeSlashes(homeDir)
|
||||
if !strings.HasPrefix(normalizedPath, homeDir) {
|
||||
return "", false
|
||||
}
|
||||
rel := strings.TrimPrefix(normalizedPath, homeDir)
|
||||
rel = strings.TrimPrefix(rel, "/")
|
||||
return "~/" + rel, true
|
||||
}
|
||||
|
||||
func tryRepoRelativeHookPath(normalizedPath string) (string, bool) {
|
||||
parts := strings.Split(normalizedPath, "/")
|
||||
for i, part := range parts {
|
||||
if part == ".clinerules" && i > 0 {
|
||||
repoName := parts[i-1]
|
||||
return repoName + "/" + strings.Join(parts[i:], "/"), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// tryRepoScopedHooksPath returns a repo-prefixed path like
|
||||
// "myrepo/.clinerules/hooks/PreToolUse" when the given path points to a hook script
|
||||
// under a repo's .clinerules/hooks directory.
|
||||
//
|
||||
// This is more specific than tryRepoRelativeHookPath and is used to ensure hook script
|
||||
// paths always include repo context.
|
||||
func tryRepoScopedHooksPath(normalizedPath string) (string, bool) {
|
||||
// Fast path check to avoid split work.
|
||||
if !strings.Contains(normalizedPath, "/.clinerules/hooks/") {
|
||||
return "", false
|
||||
}
|
||||
return tryRepoRelativeHookPath(normalizedPath)
|
||||
}
|
||||
|
||||
func fallbackLastComponents(normalizedPath string, n int) string {
|
||||
parts := strings.Split(normalizedPath, "/")
|
||||
if len(parts) >= n {
|
||||
return strings.Join(parts[len(parts)-n:], "/")
|
||||
}
|
||||
return normalizedPath
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatHookPath_PrefersWorkspaceRelative(t *testing.T) {
|
||||
// Create a stable workspace root (avoid TempDir's nested ".../001" patterns)
|
||||
// so that workspace-relative formatting is deterministic.
|
||||
root := filepath.Join(t.TempDir(), "workspace")
|
||||
if err := os.MkdirAll(root, 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll: %v", err)
|
||||
}
|
||||
oldWd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Getwd: %v", err)
|
||||
}
|
||||
defer func() { _ = os.Chdir(oldWd) }()
|
||||
if err := os.Chdir(root); err != nil {
|
||||
t.Fatalf("Chdir: %v", err)
|
||||
}
|
||||
|
||||
inside := filepath.Join(root, ".clinerules", "hooks", "pre.sh")
|
||||
got := formatHookPath(inside)
|
||||
// Repo-scoped hook scripts should always include the repo name (the directory
|
||||
// immediately containing .clinerules) even when running inside that repo.
|
||||
expected := "workspace/" + filepath.ToSlash(filepath.Join(".clinerules", "hooks", "pre.sh"))
|
||||
if got != expected {
|
||||
t.Fatalf("expected formatted path to be %q. got=%q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatHookPath_FallsBackToLastComponents(t *testing.T) {
|
||||
// Use an obviously non-workspace path (relative, but not prefixed with cwd).
|
||||
got := formatHookPath("/var/tmp/foo/bar/baz.sh")
|
||||
if got != "foo/bar/baz.sh" {
|
||||
t.Fatalf("expected last 3 components fallback, got=%q", got)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatHookPath_HomeDirToTilde(t *testing.T) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil || home == "" {
|
||||
t.Skip("home dir not available; skipping")
|
||||
}
|
||||
|
||||
got := formatHookPath(home + "/Documents/Cline/Hooks/TaskStart")
|
||||
want := "~/Documents/Cline/Hooks/TaskStart"
|
||||
if got != want {
|
||||
t.Fatalf("expected %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatHookPath_WorkspaceRepoRelative(t *testing.T) {
|
||||
got := formatHookPath("/Users/alice/dev/repo-name/.clinerules/hooks/TaskStart")
|
||||
want := "repo-name/.clinerules/hooks/TaskStart"
|
||||
if got != want {
|
||||
t.Fatalf("expected %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatHookPath_FallbackLast3Components(t *testing.T) {
|
||||
got := formatHookPath("/a/b/c/d/e")
|
||||
want := "c/d/e"
|
||||
if got != want {
|
||||
t.Fatalf("expected %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
@@ -407,7 +407,7 @@ func newInstanceListCommand() *cobra.Command {
|
||||
|
||||
fmt.Print(strings.TrimLeft(rendered, "\n"))
|
||||
}
|
||||
fmt.Println("\n")
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,4 +503,4 @@ func newInstanceNewCommand() *cobra.Command {
|
||||
cmd.Flags().BoolVarP(&setDefault, "default", "d", false, "set as default instance")
|
||||
|
||||
return cmd
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,13 @@ import (
|
||||
"github.com/charmbracelet/bubbles/textarea"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/cline/cli/pkg/cli/slash"
|
||||
)
|
||||
|
||||
// InputType represents the type of input being collected
|
||||
type InputType int
|
||||
|
||||
const INPUT_WIDTH = 46
|
||||
const INPUT_WIDTH = 46
|
||||
|
||||
const (
|
||||
InputTypeMessage InputType = iota
|
||||
@@ -24,11 +25,11 @@ const (
|
||||
|
||||
// InputSubmitMsg is sent when the user submits input
|
||||
type InputSubmitMsg struct {
|
||||
Value string
|
||||
InputType InputType
|
||||
Approved bool // For approval type
|
||||
NeedsFeedback bool // For approval type
|
||||
NoAskAgain bool // For approval type - indicates "don't ask again" was selected
|
||||
Value string
|
||||
InputType InputType
|
||||
Approved bool // For approval type
|
||||
NeedsFeedback bool // For approval type
|
||||
NoAskAgain bool // For approval type - indicates "don't ask again" was selected
|
||||
}
|
||||
|
||||
// InputCancelMsg is sent when the user cancels input (Ctrl+C)
|
||||
@@ -36,8 +37,8 @@ type InputCancelMsg struct{}
|
||||
|
||||
// ChangeInputTypeMsg changes the current input type
|
||||
type ChangeInputTypeMsg struct {
|
||||
InputType InputType
|
||||
Title string
|
||||
InputType InputType
|
||||
Title string
|
||||
Placeholder string
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ type InputModel struct {
|
||||
placeholder string
|
||||
currentMode string // "plan" or "act"
|
||||
width int
|
||||
lastHeight int // Track height for cleanup on submit
|
||||
lastHeight int // Track height for cleanup on submit
|
||||
|
||||
// For approval type
|
||||
approvalOptions []string
|
||||
@@ -66,6 +67,9 @@ type InputModel struct {
|
||||
|
||||
// Styles (huh-inspired theme)
|
||||
styles fieldStyles
|
||||
|
||||
// Slash command autocomplete dropdown
|
||||
completion CompletionModel
|
||||
}
|
||||
|
||||
// fieldStyles holds the styling for the input field
|
||||
@@ -115,12 +119,17 @@ func newFieldStyles() fieldStyles {
|
||||
|
||||
// NewInputModel creates a new input model
|
||||
func NewInputModel(inputType InputType, title, placeholder, currentMode string) InputModel {
|
||||
return NewInputModelWithRegistry(inputType, title, placeholder, currentMode, nil)
|
||||
}
|
||||
|
||||
// NewInputModelWithRegistry creates a new input model with slash command autocomplete support
|
||||
func NewInputModelWithRegistry(inputType InputType, title, placeholder, currentMode string, registry *slash.Registry) InputModel {
|
||||
ta := textarea.New()
|
||||
ta.Placeholder = placeholder
|
||||
ta.Focus()
|
||||
ta.CharLimit = 0
|
||||
ta.ShowLineNumbers = false
|
||||
ta.Prompt = "" // Remove prompt prefix (this is what adds the inner border!)
|
||||
ta.Prompt = "" // Remove prompt prefix (this is what adds the inner border!)
|
||||
ta.SetHeight(5)
|
||||
// Don't set width here - let WindowSizeMsg handle it
|
||||
ta.SetWidth(INPUT_WIDTH)
|
||||
@@ -138,11 +147,11 @@ func NewInputModel(inputType InputType, title, placeholder, currentMode string)
|
||||
cursorColor = lipgloss.Color("39") // Blue for act
|
||||
}
|
||||
|
||||
ta.FocusedStyle.CursorLine = lipgloss.NewStyle() // No cursor line highlighting
|
||||
ta.FocusedStyle.EndOfBuffer = lipgloss.NewStyle() // No end-of-buffer styling
|
||||
ta.FocusedStyle.CursorLine = lipgloss.NewStyle() // No cursor line highlighting
|
||||
ta.FocusedStyle.EndOfBuffer = lipgloss.NewStyle() // No end-of-buffer styling
|
||||
ta.FocusedStyle.Placeholder = styles.placeholder
|
||||
ta.FocusedStyle.Text = styles.textArea
|
||||
ta.FocusedStyle.Prompt = lipgloss.NewStyle() // No prompt styling
|
||||
ta.FocusedStyle.Prompt = lipgloss.NewStyle() // No prompt styling
|
||||
ta.Cursor.Style = lipgloss.NewStyle().Foreground(cursorColor)
|
||||
ta.Cursor.TextStyle = styles.textArea
|
||||
|
||||
@@ -154,6 +163,7 @@ func NewInputModel(inputType InputType, title, placeholder, currentMode string)
|
||||
currentMode: currentMode,
|
||||
width: 0, // Will be set by first WindowSizeMsg
|
||||
styles: styles,
|
||||
completion: NewCompletionModel(registry),
|
||||
}
|
||||
|
||||
// For approval type, set up options
|
||||
@@ -217,13 +227,6 @@ func (m *InputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return m, nil
|
||||
|
||||
default:
|
||||
// Forward all other messages to textarea (including blink ticks)
|
||||
if !m.suspended && (m.inputType == InputTypeMessage || m.inputType == InputTypeFeedback) {
|
||||
m.textarea, cmd = m.textarea.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
case tea.KeyMsg:
|
||||
if m.suspended {
|
||||
return m, nil
|
||||
@@ -231,6 +234,31 @@ func (m *InputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
// Handle keys for text input types (Message/Feedback)
|
||||
if m.inputType == InputTypeMessage || m.inputType == InputTypeFeedback {
|
||||
// When completion menu is visible, let it handle navigation keys first
|
||||
if m.completion.Visible() {
|
||||
// ctrl+c always cancels, even with dropdown open
|
||||
if msg.String() == "ctrl+c" {
|
||||
return m, func() tea.Msg { return InputCancelMsg{} }
|
||||
}
|
||||
|
||||
var handled bool
|
||||
m.completion, cmd, handled = m.completion.Update(msg)
|
||||
if handled {
|
||||
// Check if a completion was selected
|
||||
if applied := m.completion.Apply(); applied != "" {
|
||||
m.textarea.SetValue(applied)
|
||||
m.textarea.CursorEnd()
|
||||
}
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
// Key not handled by completion - pass to textarea and update completion
|
||||
m.textarea, cmd = m.textarea.Update(msg)
|
||||
m.completion.CheckInput(m.textarea.Value())
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
// Normal key handling when completion menu is NOT visible
|
||||
switch msg.String() {
|
||||
case "ctrl+c":
|
||||
return m, func() tea.Msg { return InputCancelMsg{} }
|
||||
@@ -239,6 +267,11 @@ func (m *InputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
// Open external editor (like huh does)
|
||||
return m, m.openEditor()
|
||||
|
||||
case "tab":
|
||||
// Tab without dropdown visible - do nothing special
|
||||
m.textarea, cmd = m.textarea.Update(msg)
|
||||
return m, cmd
|
||||
|
||||
case "enter":
|
||||
// Intercept enter for submit (textarea handles alt+enter and ctrl+j for newlines)
|
||||
return m.handleSubmit()
|
||||
@@ -249,8 +282,9 @@ func (m *InputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
// Pass all other keys to textarea (including alt+enter, ctrl+j for newlines)
|
||||
// Pass all other keys to textarea, then check for slash completion
|
||||
m.textarea, cmd = m.textarea.Update(msg)
|
||||
m.completion.CheckInput(m.textarea.Value())
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
@@ -276,6 +310,13 @@ func (m *InputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// Forward all other messages to textarea (including blink ticks)
|
||||
if !m.suspended && (m.inputType == InputTypeMessage || m.inputType == InputTypeFeedback) {
|
||||
m.textarea, cmd = m.textarea.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
@@ -365,6 +406,11 @@ func (m *InputModel) View() string {
|
||||
case InputTypeMessage, InputTypeFeedback:
|
||||
parts = append(parts, m.textarea.View())
|
||||
|
||||
// Render completion dropdown if visible
|
||||
if m.completion.Visible() {
|
||||
parts = append(parts, m.completion.View())
|
||||
}
|
||||
|
||||
case InputTypeApproval:
|
||||
var options []string
|
||||
for i, option := range m.approvalOptions {
|
||||
@@ -411,7 +457,7 @@ func (m *InputModel) Clone() *InputModel {
|
||||
ta.ShowLineNumbers = false
|
||||
ta.Prompt = ""
|
||||
ta.SetHeight(5)
|
||||
ta.SetWidth(INPUT_WIDTH)
|
||||
ta.SetWidth(INPUT_WIDTH)
|
||||
ta.Focus()
|
||||
|
||||
// Configure keybindings
|
||||
@@ -446,6 +492,7 @@ func (m *InputModel) Clone() *InputModel {
|
||||
selectedOption: m.selectedOption,
|
||||
pendingApproval: m.pendingApproval, // Preserve approval decision
|
||||
styles: m.styles,
|
||||
completion: NewCompletionModel(m.completion.registry), // Preserve registry, start fresh state
|
||||
}
|
||||
|
||||
return clone
|
||||
@@ -495,3 +542,8 @@ func (m *InputModel) openEditor() tea.Cmd {
|
||||
return editorFinishedMsg{content: content, err: err}
|
||||
})
|
||||
}
|
||||
|
||||
// SetSlashRegistry sets the slash command registry for autocomplete
|
||||
func (m *InputModel) SetSlashRegistry(registry *slash.Registry) {
|
||||
m.completion.SetRegistry(registry)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/cline/cli/pkg/cli/slash"
|
||||
)
|
||||
|
||||
const maxVisibleCompletions = 7
|
||||
|
||||
// completionStyles holds the styling for the completion dropdown
|
||||
type completionStyles struct {
|
||||
menu lipgloss.Style
|
||||
selected lipgloss.Style
|
||||
normalName lipgloss.Style
|
||||
description lipgloss.Style
|
||||
scrollIndicator lipgloss.Style
|
||||
}
|
||||
|
||||
// newCompletionStyles creates the default styles for the completion dropdown
|
||||
func newCompletionStyles() completionStyles {
|
||||
return completionStyles{
|
||||
menu: lipgloss.NewStyle().
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("238")).
|
||||
Padding(0, 1),
|
||||
selected: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("62")).
|
||||
Foreground(lipgloss.Color("230")),
|
||||
normalName: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.AdaptiveColor{Light: "235", Dark: "252"}),
|
||||
description: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("243")),
|
||||
scrollIndicator: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("243")),
|
||||
}
|
||||
}
|
||||
|
||||
// CompletionModel is a Bubbletea model for slash command autocomplete dropdown
|
||||
type CompletionModel struct {
|
||||
registry *slash.Registry
|
||||
visible bool
|
||||
matches []slash.Command
|
||||
index int // selected item (0-based)
|
||||
scroll int // scroll offset for long lists
|
||||
styles completionStyles
|
||||
|
||||
// pendingApply holds the command to apply after selection
|
||||
pendingApply string
|
||||
}
|
||||
|
||||
// NewCompletionModel creates a new completion model with the given registry
|
||||
func NewCompletionModel(registry *slash.Registry) CompletionModel {
|
||||
return CompletionModel{
|
||||
registry: registry,
|
||||
styles: newCompletionStyles(),
|
||||
}
|
||||
}
|
||||
|
||||
// SetRegistry sets the slash command registry
|
||||
func (m *CompletionModel) SetRegistry(registry *slash.Registry) {
|
||||
m.registry = registry
|
||||
}
|
||||
|
||||
// Visible returns whether the completion dropdown is currently visible
|
||||
func (m CompletionModel) Visible() bool {
|
||||
return m.visible
|
||||
}
|
||||
|
||||
// Update handles key messages for the completion dropdown.
|
||||
// Returns the updated model, any commands, and whether the key was handled.
|
||||
// If handled is true, the parent should NOT pass the key to the textarea.
|
||||
func (m CompletionModel) Update(msg tea.Msg) (CompletionModel, tea.Cmd, bool) {
|
||||
if !m.visible {
|
||||
return m, nil, false
|
||||
}
|
||||
|
||||
keyMsg, ok := msg.(tea.KeyMsg)
|
||||
if !ok {
|
||||
return m, nil, false
|
||||
}
|
||||
|
||||
switch keyMsg.String() {
|
||||
case "up":
|
||||
m.navigateUp()
|
||||
return m, nil, true
|
||||
|
||||
case "down":
|
||||
m.navigateDown()
|
||||
return m, nil, true
|
||||
|
||||
case "tab", "enter":
|
||||
// Select the current completion
|
||||
if len(m.matches) > 0 {
|
||||
selected := m.matches[m.index]
|
||||
m.pendingApply = "/" + selected.Name + " "
|
||||
}
|
||||
m.Hide()
|
||||
return m, nil, true
|
||||
|
||||
case "esc":
|
||||
m.Hide()
|
||||
return m, nil, true
|
||||
}
|
||||
|
||||
// Key not handled by completion - let parent process it
|
||||
return m, nil, false
|
||||
}
|
||||
|
||||
// CheckInput updates the completion state based on the current input value.
|
||||
// Call this after each input change to show/hide/update the dropdown.
|
||||
func (m *CompletionModel) CheckInput(value string) {
|
||||
if m.registry == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Only activate if input starts with "/" (first character requirement)
|
||||
if !strings.HasPrefix(value, "/") {
|
||||
m.Hide()
|
||||
return
|
||||
}
|
||||
|
||||
// Extract the command being typed (everything after "/" until space/newline)
|
||||
rest := value[1:] // Everything after the "/"
|
||||
|
||||
// If there's whitespace, the command is complete - hide dropdown
|
||||
if idx := strings.IndexAny(rest, " \n\t"); idx != -1 {
|
||||
m.Hide()
|
||||
return
|
||||
}
|
||||
|
||||
// Update matches based on prefix
|
||||
m.updateMatches(rest)
|
||||
m.visible = len(m.matches) > 0
|
||||
}
|
||||
|
||||
// Apply returns the command string to insert (if any) and clears the pending state.
|
||||
// The parent should call this after Update returns handled=true for tab/enter.
|
||||
func (m *CompletionModel) Apply() string {
|
||||
result := m.pendingApply
|
||||
m.pendingApply = ""
|
||||
return result
|
||||
}
|
||||
|
||||
// Hide hides the completion dropdown and resets state
|
||||
func (m *CompletionModel) Hide() {
|
||||
m.visible = false
|
||||
m.matches = nil
|
||||
m.index = 0
|
||||
m.scroll = 0
|
||||
}
|
||||
|
||||
// View renders the completion dropdown
|
||||
func (m CompletionModel) View() string {
|
||||
if !m.visible || len(m.matches) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var lines []string
|
||||
|
||||
// Calculate visible range
|
||||
endIdx := min(m.scroll+maxVisibleCompletions, len(m.matches))
|
||||
|
||||
// Show scroll indicator if there are items above
|
||||
if m.scroll > 0 {
|
||||
lines = append(lines, m.styles.scrollIndicator.Render(" ↑ more"))
|
||||
}
|
||||
|
||||
// Find the longest command name for alignment
|
||||
maxNameLen := 0
|
||||
for _, cmd := range m.matches {
|
||||
nameLen := len(cmd.Name) + 1 // +1 for the "/"
|
||||
if nameLen > maxNameLen {
|
||||
maxNameLen = nameLen
|
||||
}
|
||||
}
|
||||
// Cap at reasonable width
|
||||
if maxNameLen > 15 {
|
||||
maxNameLen = 15
|
||||
}
|
||||
|
||||
// Render visible items
|
||||
for i := m.scroll; i < endIdx; i++ {
|
||||
cmd := m.matches[i]
|
||||
name := "/" + cmd.Name
|
||||
desc := cmd.Description
|
||||
|
||||
// Truncate description if too long
|
||||
maxDescLen := 35
|
||||
if len(desc) > maxDescLen {
|
||||
desc = desc[:maxDescLen-3] + "..."
|
||||
}
|
||||
|
||||
// Pad name for alignment
|
||||
paddedName := fmt.Sprintf("%-*s", maxNameLen, name)
|
||||
|
||||
if i == m.index {
|
||||
// Selected item - highlight the entire line
|
||||
line := fmt.Sprintf("> %s %s", paddedName, desc)
|
||||
lines = append(lines, m.styles.selected.Render(line))
|
||||
} else {
|
||||
// Normal item
|
||||
line := fmt.Sprintf(" %s %s", m.styles.normalName.Render(paddedName), m.styles.description.Render(desc))
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
// Show scroll indicator if there are items below
|
||||
if endIdx < len(m.matches) {
|
||||
lines = append(lines, m.styles.scrollIndicator.Render(" ↓ more"))
|
||||
}
|
||||
|
||||
return m.styles.menu.Render(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
// updateMatches filters commands by prefix and updates the matches list
|
||||
func (m *CompletionModel) updateMatches(prefix string) {
|
||||
if m.registry == nil {
|
||||
m.matches = nil
|
||||
return
|
||||
}
|
||||
m.matches = m.registry.GetMatching(prefix)
|
||||
// Reset selection if out of bounds
|
||||
if m.index >= len(m.matches) {
|
||||
m.index = 0
|
||||
m.scroll = 0
|
||||
}
|
||||
m.adjustScroll()
|
||||
}
|
||||
|
||||
// navigateUp moves selection up in the dropdown
|
||||
func (m *CompletionModel) navigateUp() {
|
||||
if len(m.matches) == 0 {
|
||||
return
|
||||
}
|
||||
m.index--
|
||||
if m.index < 0 {
|
||||
m.index = len(m.matches) - 1
|
||||
}
|
||||
m.adjustScroll()
|
||||
}
|
||||
|
||||
// navigateDown moves selection down in the dropdown
|
||||
func (m *CompletionModel) navigateDown() {
|
||||
if len(m.matches) == 0 {
|
||||
return
|
||||
}
|
||||
m.index++
|
||||
if m.index >= len(m.matches) {
|
||||
m.index = 0
|
||||
}
|
||||
m.adjustScroll()
|
||||
}
|
||||
|
||||
// adjustScroll ensures the selected item is visible in the dropdown
|
||||
func (m *CompletionModel) adjustScroll() {
|
||||
if m.index < m.scroll {
|
||||
m.scroll = m.index
|
||||
} else if m.index >= m.scroll+maxVisibleCompletions {
|
||||
m.scroll = m.index - maxVisibleCompletions + 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package slash
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/cline/grpc-go/client"
|
||||
"github.com/cline/grpc-go/cline"
|
||||
)
|
||||
|
||||
// Command represents a slash command available for autocomplete
|
||||
type Command struct {
|
||||
Name string
|
||||
Description string
|
||||
Section string // "default", "custom", or "cli"
|
||||
CLICompatible bool
|
||||
}
|
||||
|
||||
// Registry holds available slash commands for autocomplete
|
||||
type Registry struct {
|
||||
mu sync.RWMutex
|
||||
commands []Command
|
||||
}
|
||||
|
||||
// CLI-local commands (handled by CLI, not sent to backend)
|
||||
var cliLocalCommands = []Command{
|
||||
{Name: "plan", Description: "Switch to plan mode", Section: "cli", CLICompatible: true},
|
||||
{Name: "act", Description: "Switch to act mode", Section: "cli", CLICompatible: true},
|
||||
{Name: "cancel", Description: "Cancel the current task", Section: "cli", CLICompatible: true},
|
||||
{Name: "exit", Description: "Exit follow mode", Section: "cli", CLICompatible: true},
|
||||
}
|
||||
|
||||
// NewRegistry creates a new slash command registry
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
commands: make([]Command, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// FetchFromBackend fetches available commands from cline-core backend
|
||||
func (r *Registry) FetchFromBackend(ctx context.Context, c *client.ClineClient) error {
|
||||
resp, err := c.Slash.GetAvailableSlashCommands(ctx, &cline.EmptyRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
// Start with CLI-local commands
|
||||
r.commands = append([]Command{}, cliLocalCommands...)
|
||||
|
||||
// Add backend commands (only CLI-compatible ones)
|
||||
for _, cmd := range resp.Commands {
|
||||
if cmd.CliCompatible {
|
||||
r.commands = append(r.commands, Command{
|
||||
Name: cmd.Name,
|
||||
Description: cmd.Description,
|
||||
Section: cmd.Section,
|
||||
CLICompatible: cmd.CliCompatible,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCommands returns all available commands
|
||||
func (r *Registry) GetCommands() []Command {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
// Return a copy to avoid race conditions
|
||||
result := make([]Command, len(r.commands))
|
||||
copy(result, r.commands)
|
||||
return result
|
||||
}
|
||||
|
||||
// GetMatching returns commands that start with the given prefix (case-insensitive)
|
||||
func (r *Registry) GetMatching(prefix string) []Command {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
prefix = strings.ToLower(prefix)
|
||||
var matches []Command
|
||||
for _, cmd := range r.commands {
|
||||
if strings.HasPrefix(strings.ToLower(cmd.Name), prefix) {
|
||||
matches = append(matches, cmd)
|
||||
}
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
// IsValid checks if a command name is valid
|
||||
func (r *Registry) IsValid(name string) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
name = strings.ToLower(name)
|
||||
for _, cmd := range r.commands {
|
||||
if strings.ToLower(cmd.Name) == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCLILocal checks if a command is handled locally by CLI (not sent to backend)
|
||||
func (r *Registry) IsCLILocal(name string) bool {
|
||||
name = strings.ToLower(name)
|
||||
for _, cmd := range cliLocalCommands {
|
||||
if strings.ToLower(cmd.Name) == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasCommands returns true if the registry has any commands loaded
|
||||
func (r *Registry) HasCommands() bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return len(r.commands) > 0
|
||||
}
|
||||
@@ -20,13 +20,14 @@ 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
|
||||
Images []string
|
||||
Files []string
|
||||
Mode string
|
||||
Settings []string
|
||||
Yolo bool
|
||||
Address string
|
||||
Verbose bool
|
||||
Workspaces []string
|
||||
}
|
||||
|
||||
func NewTaskCommand() *cobra.Command {
|
||||
@@ -394,7 +395,7 @@ func newTaskViewCommand() *cobra.Command {
|
||||
return taskManager.FollowConversation(ctx, taskManager.GetCurrentInstance(), false)
|
||||
} else if followComplete {
|
||||
// Follow until completion
|
||||
return taskManager.FollowConversationUntilCompletion(ctx)
|
||||
return taskManager.FollowConversationUntilCompletion(ctx, task.DefaultFollowOptions())
|
||||
} else {
|
||||
// Default: show snapshot
|
||||
return taskManager.ShowConversation(ctx)
|
||||
@@ -668,7 +669,10 @@ func CreateAndFollowTask(ctx context.Context, prompt string, opts TaskOptions) e
|
||||
// If yolo mode is enabled, follow until completion (non-interactive)
|
||||
// Otherwise, follow in interactive mode
|
||||
if opts.Yolo {
|
||||
return taskManager.FollowConversationUntilCompletion(ctx)
|
||||
// Skip active task check since we just created the task
|
||||
return taskManager.FollowConversationUntilCompletion(ctx, task.FollowOptions{
|
||||
SkipActiveTaskCheck: true,
|
||||
})
|
||||
} else {
|
||||
return taskManager.FollowConversation(ctx, taskManager.GetCurrentInstance(), true)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package task
|
||||
|
||||
// FollowOptions contains options for following a conversation
|
||||
type FollowOptions struct {
|
||||
// SkipActiveTaskCheck skips the check for an active task
|
||||
// This is useful when following a task that was just created to avoid race conditions
|
||||
SkipActiveTaskCheck bool
|
||||
}
|
||||
|
||||
// DefaultFollowOptions returns the default options for following a conversation
|
||||
func DefaultFollowOptions() FollowOptions {
|
||||
return FollowOptions{
|
||||
SkipActiveTaskCheck: false,
|
||||
}
|
||||
}
|
||||
@@ -38,13 +38,13 @@ type InputHandler struct {
|
||||
// NewInputHandler creates a new input handler
|
||||
func NewInputHandler(manager *Manager, coordinator *StreamCoordinator, cancelFunc context.CancelFunc) *InputHandler {
|
||||
return &InputHandler{
|
||||
manager: manager,
|
||||
coordinator: coordinator,
|
||||
cancelFunc: cancelFunc,
|
||||
isRunning: false,
|
||||
pollTicker: time.NewTicker(500 * time.Millisecond),
|
||||
resultChan: make(chan output.InputSubmitMsg, 1),
|
||||
cancelChan: make(chan struct{}, 1),
|
||||
manager: manager,
|
||||
coordinator: coordinator,
|
||||
cancelFunc: cancelFunc,
|
||||
isRunning: false,
|
||||
pollTicker: time.NewTicker(500 * time.Millisecond),
|
||||
resultChan: make(chan output.InputSubmitMsg, 1),
|
||||
cancelChan: make(chan struct{}, 1),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +251,8 @@ func determineAutoApprovalAction(msg *types.ClineMessage) (string, error) {
|
||||
types.ToolTypeListFilesRecursive,
|
||||
types.ToolTypeListCodeDefinitionNames,
|
||||
types.ToolTypeSearchFiles,
|
||||
types.ToolTypeWebFetch:
|
||||
types.ToolTypeWebFetch,
|
||||
types.ToolTypeWebSearch:
|
||||
return "read_files", nil
|
||||
case types.ToolTypeEditedExistingFile,
|
||||
types.ToolTypeNewFileCreated:
|
||||
@@ -280,11 +281,12 @@ func determineAutoApprovalAction(msg *types.ClineMessage) (string, error) {
|
||||
func (ih *InputHandler) promptForInput(ctx context.Context) (string, bool, error) {
|
||||
currentMode := ih.manager.GetCurrentMode()
|
||||
|
||||
model := output.NewInputModel(
|
||||
model := output.NewInputModelWithRegistry(
|
||||
output.InputTypeMessage,
|
||||
"Cline is ready for your message...",
|
||||
"/plan or /act to switch modes\nctrl+e to open editor",
|
||||
"/plan or /act to switch modes\nctrl+e to open editor\ntab to autocomplete commands",
|
||||
currentMode,
|
||||
ih.manager.GetSlashRegistry(),
|
||||
)
|
||||
|
||||
return ih.runInputProgram(ctx, model)
|
||||
@@ -294,12 +296,13 @@ func (ih *InputHandler) promptForInput(ctx context.Context) (string, bool, error
|
||||
func (ih *InputHandler) promptForApproval(ctx context.Context, msg *types.ClineMessage) (bool, string, error) {
|
||||
// Store the approval message for later use in determining auto-approval action
|
||||
ih.approvalMessage = msg
|
||||
|
||||
model := output.NewInputModel(
|
||||
|
||||
model := output.NewInputModelWithRegistry(
|
||||
output.InputTypeApproval,
|
||||
"Let Cline use this tool?",
|
||||
"",
|
||||
ih.manager.GetCurrentMode(),
|
||||
ih.manager.GetSlashRegistry(), // Pass registry for feedback input after approval
|
||||
)
|
||||
|
||||
message, shouldSend, err := ih.runInputProgram(ctx, model)
|
||||
@@ -394,7 +397,7 @@ func (ih *InputHandler) runInputProgram(ctx context.Context, model output.InputM
|
||||
// Need to collect feedback - will be handled by model state change
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
|
||||
// Check if NoAskAgain was selected
|
||||
if result.NoAskAgain && result.Approved && ih.approvalMessage != nil {
|
||||
// Determine which auto-approval action to enable
|
||||
@@ -410,7 +413,7 @@ func (ih *InputHandler) runInputProgram(ctx context.Context, model output.InputM
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Store approval state for when feedback comes back
|
||||
ih.feedbackApproval = false
|
||||
ih.feedbackApproved = result.Approved
|
||||
@@ -440,6 +443,7 @@ func (w *inputProgramWrapper) Init() tea.Cmd {
|
||||
}
|
||||
|
||||
func (w *inputProgramWrapper) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case output.InputSubmitMsg:
|
||||
// Handle input submission - clear the screen before quitting
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/cline/cli/pkg/cli/display"
|
||||
"github.com/cline/cli/pkg/cli/global"
|
||||
"github.com/cline/cli/pkg/cli/handlers"
|
||||
"github.com/cline/cli/pkg/cli/slash"
|
||||
"github.com/cline/cli/pkg/cli/types"
|
||||
"github.com/cline/grpc-go/client"
|
||||
"github.com/cline/grpc-go/cline"
|
||||
@@ -32,10 +33,12 @@ type Manager struct {
|
||||
clientAddress string
|
||||
state *types.ConversationState
|
||||
renderer *display.Renderer
|
||||
hookRenderer *display.HookRenderer
|
||||
toolRenderer *display.ToolRenderer
|
||||
systemRenderer *display.SystemMessageRenderer
|
||||
streamingDisplay *display.StreamingDisplay
|
||||
handlerRegistry *handlers.HandlerRegistry
|
||||
slashRegistry *slash.Registry
|
||||
isStreamingMode bool
|
||||
isInteractive bool
|
||||
currentMode string // "plan" or "act"
|
||||
@@ -46,6 +49,7 @@ func NewManager(client *client.ClineClient) *Manager {
|
||||
state := types.NewConversationState()
|
||||
renderer := display.NewRenderer(global.Config.OutputFormat)
|
||||
toolRenderer := display.NewToolRenderer(renderer.GetMdRenderer(), global.Config.OutputFormat)
|
||||
hookRenderer := display.NewHookRenderer(renderer.GetMdRenderer(), global.Config.OutputFormat)
|
||||
systemRenderer := display.NewSystemMessageRenderer(renderer, renderer.GetMdRenderer(), global.Config.OutputFormat)
|
||||
streamingDisplay := display.NewStreamingDisplay(state, renderer)
|
||||
|
||||
@@ -59,10 +63,12 @@ func NewManager(client *client.ClineClient) *Manager {
|
||||
clientAddress: "", // Will be set when client is provided
|
||||
state: state,
|
||||
renderer: renderer,
|
||||
hookRenderer: hookRenderer,
|
||||
toolRenderer: toolRenderer,
|
||||
systemRenderer: systemRenderer,
|
||||
streamingDisplay: streamingDisplay,
|
||||
handlerRegistry: registry,
|
||||
slashRegistry: slash.NewRegistry(),
|
||||
currentMode: "plan", // Default mode
|
||||
}
|
||||
}
|
||||
@@ -76,6 +82,10 @@ func NewManagerForAddress(ctx context.Context, address string) (*Manager, error)
|
||||
|
||||
manager := NewManager(client)
|
||||
manager.clientAddress = address
|
||||
|
||||
// Fetch slash commands from backend (non-blocking, errors are logged)
|
||||
manager.fetchSlashCommands(ctx)
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
@@ -93,9 +103,25 @@ func NewManagerForDefault(ctx context.Context) (*Manager, error) {
|
||||
manager.clientAddress = global.Clients.GetRegistry().GetDefaultInstance()
|
||||
}
|
||||
|
||||
// Fetch slash commands from backend (non-blocking, errors are logged)
|
||||
manager.fetchSlashCommands(ctx)
|
||||
|
||||
return manager, nil
|
||||
}
|
||||
|
||||
// fetchSlashCommands fetches available slash commands from the backend
|
||||
// This is non-blocking and errors are logged but don't prevent manager creation
|
||||
func (m *Manager) fetchSlashCommands(ctx context.Context) {
|
||||
if err := m.slashRegistry.FetchFromBackend(ctx, m.client); err != nil {
|
||||
if global.Config.Verbose {
|
||||
m.renderer.RenderDebug("Failed to fetch slash commands: %v", err)
|
||||
}
|
||||
// Non-fatal: CLI-local commands are still available
|
||||
} else if global.Config.Verbose {
|
||||
m.renderer.RenderDebug("Loaded %d slash commands", len(m.slashRegistry.GetCommands()))
|
||||
}
|
||||
}
|
||||
|
||||
// SwitchToInstance switches the manager to use a different Cline instance
|
||||
func (m *Manager) SwitchToInstance(ctx context.Context, address string) error {
|
||||
m.mu.Lock()
|
||||
@@ -280,8 +306,8 @@ func (m *Manager) CheckSendEnabled(ctx context.Context) error {
|
||||
|
||||
// Error types which we allow sending on
|
||||
errorTypes := []string{
|
||||
string(types.AskTypeAPIReqFailed), // "api_req_failed"
|
||||
string(types.AskTypeMistakeLimitReached), // "mistake_limit_reached"
|
||||
string(types.AskTypeAPIReqFailed), // "api_req_failed"
|
||||
string(types.AskTypeMistakeLimitReached), // "mistake_limit_reached"
|
||||
}
|
||||
|
||||
isError := false
|
||||
@@ -753,7 +779,21 @@ func (m *Manager) FollowConversation(ctx context.Context, instanceAddress string
|
||||
}
|
||||
|
||||
// FollowConversationUntilCompletion streams conversation updates until task completion
|
||||
func (m *Manager) FollowConversationUntilCompletion(ctx context.Context) error {
|
||||
func (m *Manager) FollowConversationUntilCompletion(ctx context.Context, opts FollowOptions) error {
|
||||
// Check if there's an active task before entering follow mode
|
||||
// Skip this check if we just created a task (to avoid race condition where task isn't active yet)
|
||||
if !opts.SkipActiveTaskCheck {
|
||||
err := m.CheckSendEnabled(ctx)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNoActiveTask) {
|
||||
fmt.Println("No task is currently running.")
|
||||
return nil
|
||||
}
|
||||
// For other errors (like task busy), we can still enter follow mode
|
||||
// as the user may want to observe the task
|
||||
}
|
||||
}
|
||||
|
||||
// Enable streaming mode
|
||||
m.mu.Lock()
|
||||
m.isStreamingMode = true
|
||||
@@ -970,6 +1010,33 @@ func (m *Manager) processStateUpdate(stateUpdate *cline.State, coordinator *Stre
|
||||
coordinator.MarkProcessedInCurrentTurn(msgKey)
|
||||
}
|
||||
|
||||
case msg.Say == string(types.SayTypeMcpServerResponse):
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
|
||||
fmt.Println()
|
||||
m.displayMessage(msg, false, false, i)
|
||||
|
||||
coordinator.MarkProcessedInCurrentTurn(msgKey)
|
||||
}
|
||||
|
||||
case msg.Say == string(types.SayTypeMcpNotification):
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
|
||||
fmt.Println()
|
||||
m.displayMessage(msg, false, false, i)
|
||||
|
||||
coordinator.MarkProcessedInCurrentTurn(msgKey)
|
||||
}
|
||||
|
||||
case msg.Say == string(types.SayTypeUseMcpServer):
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
|
||||
fmt.Println()
|
||||
m.displayMessage(msg, false, false, i)
|
||||
|
||||
coordinator.MarkProcessedInCurrentTurn(msgKey)
|
||||
}
|
||||
|
||||
case msg.Say == string(types.SayTypeCheckpointCreated):
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
|
||||
@@ -979,6 +1046,26 @@ func (m *Manager) processStateUpdate(stateUpdate *cline.State, coordinator *Stre
|
||||
coordinator.MarkProcessedInCurrentTurn(msgKey)
|
||||
}
|
||||
|
||||
case msg.Say == string(types.SayTypeHookStatus):
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
|
||||
fmt.Println()
|
||||
m.displayMessage(msg, false, false, i)
|
||||
|
||||
coordinator.MarkProcessedInCurrentTurn(msgKey)
|
||||
}
|
||||
|
||||
case msg.Say == string(types.SayTypeHookOutputStream):
|
||||
// Hook stdout/stderr streaming arrives as hook_output_stream messages.
|
||||
// These are intentionally suppressed unless verbose (see SayHandler.handleHookOutputStream),
|
||||
// but we still need to route them through the normal handler pipeline in streaming/follow
|
||||
// mode so verbose users actually see `HOOK> ...` lines.
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
|
||||
m.displayMessage(msg, false, false, i)
|
||||
coordinator.MarkProcessedInCurrentTurn(msgKey)
|
||||
}
|
||||
|
||||
case msg.Say == string(types.SayTypeAPIReqStarted):
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
apiInfo := types.APIRequestInfo{Cost: -1}
|
||||
@@ -993,6 +1080,14 @@ func (m *Manager) processStateUpdate(stateUpdate *cline.State, coordinator *Stre
|
||||
}
|
||||
}
|
||||
|
||||
case msg.Say == string(types.SayTypeCompletionResult):
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
if !msg.Partial && !coordinator.IsProcessedInCurrentTurn(msgKey) {
|
||||
fmt.Println()
|
||||
m.displayMessage(msg, false, false, i)
|
||||
coordinator.MarkProcessedInCurrentTurn(msgKey)
|
||||
}
|
||||
|
||||
case msg.Ask == string(types.AskTypeCommandOutput):
|
||||
msgKey := fmt.Sprintf("%d", msg.Timestamp)
|
||||
if !coordinator.IsProcessedInCurrentTurn(msgKey) {
|
||||
@@ -1102,12 +1197,14 @@ func (m *Manager) displayMessage(msg *types.ClineMessage, isLast, isPartial bool
|
||||
m.mu.RUnlock()
|
||||
|
||||
dc := &handlers.DisplayContext{
|
||||
State: m.state,
|
||||
Renderer: m.renderer,
|
||||
ToolRenderer: m.toolRenderer,
|
||||
SystemRenderer: m.systemRenderer,
|
||||
State: m.state,
|
||||
Renderer: m.renderer,
|
||||
ToolRenderer: m.toolRenderer,
|
||||
HookRenderer: m.hookRenderer,
|
||||
SystemRenderer: m.systemRenderer,
|
||||
IsLast: isLast,
|
||||
IsPartial: isPartial,
|
||||
Verbose: global.Config.Verbose,
|
||||
MessageIndex: messageIndex,
|
||||
IsStreamingMode: isStreaming,
|
||||
IsInteractive: isInteractive,
|
||||
@@ -1214,6 +1311,11 @@ func (m *Manager) GetCurrentMode() string {
|
||||
return m.currentMode
|
||||
}
|
||||
|
||||
// GetSlashRegistry returns the slash command registry
|
||||
func (m *Manager) GetSlashRegistry() *slash.Registry {
|
||||
return m.slashRegistry
|
||||
}
|
||||
|
||||
// extractModeFromState extracts the current mode from state JSON
|
||||
func (m *Manager) extractModeFromState(stateJson string) string {
|
||||
var rawState map[string]interface{}
|
||||
@@ -1239,7 +1341,7 @@ func (m *Manager) updateMode(stateJson string) {
|
||||
// UpdateTaskAutoApprovalAction enables a specific auto-approval action for the current task
|
||||
func (m *Manager) UpdateTaskAutoApprovalAction(ctx context.Context, actionKey string) error {
|
||||
boolPtr := func(b bool) *bool { return &b }
|
||||
|
||||
|
||||
settings := &cline.Settings{
|
||||
AutoApprovalSettings: &cline.AutoApprovalSettings{
|
||||
Actions: &cline.AutoApprovalActions{},
|
||||
@@ -1248,7 +1350,7 @@ func (m *Manager) UpdateTaskAutoApprovalAction(ctx context.Context, actionKey st
|
||||
|
||||
// Set the specific action to true based on actionKey
|
||||
truePtr := boolPtr(true)
|
||||
|
||||
|
||||
switch actionKey {
|
||||
case "read_files":
|
||||
settings.AutoApprovalSettings.Actions.ReadFiles = truePtr
|
||||
|
||||
@@ -290,6 +290,18 @@ func setSimpleField(settings *cline.Settings, key, value string) error {
|
||||
return err
|
||||
}
|
||||
settings.ActModeAwsBedrockCustomSelected = boolPtr(val)
|
||||
case "hooks_enabled":
|
||||
val, err := parseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settings.HooksEnabled = boolPtr(val)
|
||||
case "azure_identity":
|
||||
val, err := parseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settings.AzureIdentity = boolPtr(val)
|
||||
|
||||
// Integer fields
|
||||
case "request_timeout_ms":
|
||||
|
||||
@@ -3,15 +3,16 @@ 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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
@@ -47,11 +47,11 @@ const (
|
||||
AskTypeResumeTask AskType = "resume_task"
|
||||
AskTypeResumeCompletedTask AskType = "resume_completed_task"
|
||||
AskTypeMistakeLimitReached AskType = "mistake_limit_reached"
|
||||
AskTypeBrowserActionLaunch AskType = "browser_action_launch"
|
||||
AskTypeUseMcpServer AskType = "use_mcp_server"
|
||||
AskTypeNewTask AskType = "new_task"
|
||||
AskTypeCondense AskType = "condense"
|
||||
AskTypeReportBug AskType = "report_bug"
|
||||
AskTypeBrowserActionLaunch AskType = "browser_action_launch"
|
||||
AskTypeUseMcpServer AskType = "use_mcp_server"
|
||||
AskTypeNewTask AskType = "new_task"
|
||||
AskTypeCondense AskType = "condense"
|
||||
AskTypeReportBug AskType = "report_bug"
|
||||
)
|
||||
|
||||
// SayType represents different types of SAY messages
|
||||
@@ -87,6 +87,10 @@ const (
|
||||
SayTypeLoadMcpDocumentation SayType = "load_mcp_documentation"
|
||||
SayTypeInfo SayType = "info"
|
||||
SayTypeTaskProgress SayType = "task_progress"
|
||||
// Hook status streaming from the backend.
|
||||
// These values must match the backend "say" strings emitted by the extension.
|
||||
SayTypeHookStatus SayType = "hook_status"
|
||||
SayTypeHookOutputStream SayType = "hook_output_stream"
|
||||
)
|
||||
|
||||
// ToolMessage represents a tool-related message
|
||||
@@ -113,6 +117,7 @@ const (
|
||||
ToolTypeListCodeDefinitionNames ToolType = "listCodeDefinitionNames"
|
||||
ToolTypeSearchFiles ToolType = "searchFiles"
|
||||
ToolTypeWebFetch ToolType = "webFetch"
|
||||
ToolTypeWebSearch ToolType = "webSearch"
|
||||
ToolTypeSummarizeTask ToolType = "summarizeTask"
|
||||
)
|
||||
|
||||
@@ -144,6 +149,42 @@ type APIRequestRetryStatus struct {
|
||||
ErrorSnippet string `json:"errorSnippet,omitempty"`
|
||||
}
|
||||
|
||||
// HookMessage represents hook execution metadata sent from the backend
|
||||
type HookMessage struct {
|
||||
HookName string `json:"hookName"` // Type of hook (TaskStart, PreToolUse, etc.)
|
||||
ToolName string `json:"toolName,omitempty"` // Optional tool name for tool-specific hooks
|
||||
Status string `json:"status"` // "running", "completed", "cancelled", or "failed"
|
||||
ScriptPaths []string `json:"scriptPaths,omitempty"` // Full paths to hook script(s)
|
||||
PendingToolInfo *ToolInfo `json:"pendingToolInfo,omitempty"` // Metadata about the pending tool execution (PreToolUse)
|
||||
ExitCode int `json:"exitCode,omitempty"` // Exit code for completed/failed hooks
|
||||
HasJsonResponse bool `json:"hasJsonResponse,omitempty"` // Whether hook returned JSON
|
||||
Error *HookError `json:"error,omitempty"` // Error details if hook failed
|
||||
}
|
||||
|
||||
// ToolInfo represents a compact subset of tool parameters for UI display.
|
||||
// This mirrors the extension's pendingToolInfo shape and is used by the CLI to
|
||||
// show what tool the PreToolUse hook is gating.
|
||||
type ToolInfo struct {
|
||||
Tool string `json:"tool"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Command string `json:"command,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
Diff string `json:"diff,omitempty"`
|
||||
Regex string `json:"regex,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
McpTool string `json:"mcpTool,omitempty"`
|
||||
McpServer string `json:"mcpServer,omitempty"`
|
||||
ResourceUri string `json:"resourceUri,omitempty"`
|
||||
}
|
||||
|
||||
// HookError represents structured error information from a failed hook
|
||||
type HookError struct {
|
||||
Type string `json:"type"` // Error type: "execution", "timeout", "validation", etc.
|
||||
Message string `json:"message"` // Human-readable error message
|
||||
Details string `json:"details,omitempty"` // Additional error details
|
||||
ScriptPath string `json:"scriptPath,omitempty"` // Path to script that failed
|
||||
}
|
||||
|
||||
// GetTimestamp returns a formatted timestamp string
|
||||
func (m *ClineMessage) GetTimestamp() string {
|
||||
return time.Unix(m.Timestamp/1000, 0).Format("15:04:05")
|
||||
@@ -323,6 +364,10 @@ func convertProtoSayType(sayType cline.ClineSay) string {
|
||||
return string(SayTypeInfo)
|
||||
case cline.ClineSay_TASK_PROGRESS:
|
||||
return string(SayTypeTaskProgress)
|
||||
case cline.ClineSay_HOOK_STATUS:
|
||||
return string(SayTypeHookStatus)
|
||||
case cline.ClineSay_HOOK_OUTPUT_STREAM:
|
||||
return string(SayTypeHookOutputStream)
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -183,3 +185,72 @@ 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
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ func (s *EnvService) GetHostVersion(ctx context.Context, req *cline.EmptyRequest
|
||||
|
||||
return &host.GetHostVersionResponse{
|
||||
Platform: proto.String("Cline CLI"),
|
||||
Version: proto.String(""),
|
||||
Version: proto.String(global.CliVersion),
|
||||
ClineType: proto.String("CLI"),
|
||||
ClineVersion: proto.String(global.CliVersion),
|
||||
}, nil
|
||||
|
||||
@@ -16,15 +16,17 @@ 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) *GrpcServer {
|
||||
func NewGrpcServer(port int, verbose bool, workspaces []string) *GrpcServer {
|
||||
return &GrpcServer{
|
||||
port: port,
|
||||
verbose: verbose,
|
||||
workspaces: workspaces,
|
||||
shutdownCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
@@ -50,7 +52,7 @@ func (s *GrpcServer) Start(ctx context.Context) error {
|
||||
grpc_health_v1.RegisterHealthServer(s.server, healthServer)
|
||||
|
||||
// Register services
|
||||
workspaceService := NewSimpleWorkspaceService(s.verbose)
|
||||
workspaceService := NewSimpleWorkspaceService(s.verbose, s.workspaces)
|
||||
host.RegisterWorkspaceServiceServer(s.server, workspaceService)
|
||||
|
||||
windowService := NewWindowService(s.verbose)
|
||||
|
||||
@@ -12,13 +12,15 @@ import (
|
||||
// SimpleWorkspaceService implements a basic workspace service without complex dependencies
|
||||
type SimpleWorkspaceService struct {
|
||||
host.UnimplementedWorkspaceServiceServer
|
||||
verbose bool
|
||||
verbose bool
|
||||
workspaces []string
|
||||
}
|
||||
|
||||
// NewSimpleWorkspaceService creates a new SimpleWorkspaceService
|
||||
func NewSimpleWorkspaceService(verbose bool) *SimpleWorkspaceService {
|
||||
func NewSimpleWorkspaceService(verbose bool, workspaces []string) *SimpleWorkspaceService {
|
||||
return &SimpleWorkspaceService{
|
||||
verbose: verbose,
|
||||
verbose: verbose,
|
||||
workspaces: workspaces,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +30,24 @@ func (s *SimpleWorkspaceService) GetWorkspacePaths(ctx context.Context, req *hos
|
||||
log.Printf("GetWorkspacePaths called")
|
||||
}
|
||||
|
||||
// Get current working directory as the workspace
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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)
|
||||
}
|
||||
|
||||
return &host.GetWorkspacePathsResponse{
|
||||
Paths: []string{cwd},
|
||||
Paths: paths,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 96 KiB |
@@ -95,6 +95,12 @@ 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:
|
||||
|
||||
@@ -254,6 +260,19 @@ COMMANDS
|
||||
cline c l
|
||||
List all configuration variables and their values.
|
||||
|
||||
Context Window Configuration
|
||||
For local model providers, you can configure the context window size:
|
||||
|
||||
Ollama
|
||||
cline config s ollama-api-options-ctx-num=32768
|
||||
|
||||
LM Studio
|
||||
cline config s lm-studio-max-tokens=32768
|
||||
|
||||
For other providers (Anthropic, OpenRouter, etc.), the context window
|
||||
is defined per model in the model metadata and is not user-settable.
|
||||
Cline uses each model's built-in context limits automatically.
|
||||
|
||||
TASK SETTINGS
|
||||
Task settings are persisted in the ~/.cline/x/tasks directory. When
|
||||
resuming a task with cline task open, task settings are automatically
|
||||
@@ -265,6 +284,29 @@ TASK SETTINGS
|
||||
|
||||
mode Starting mode (act/plan)
|
||||
|
||||
hooks_enabled
|
||||
Enable or disable hooks for the task (true/false)
|
||||
|
||||
HOOKS INTEGRATION
|
||||
Hooks let you inject custom logic into Cline's workflow at key moments.
|
||||
They can validate operations before they execute, monitor tool usage,
|
||||
and shape AI decisions. This allows you to integrate hooks into
|
||||
automated workflows, CI/CD pipelines, and headless task execution.
|
||||
|
||||
Enable hooks for a task:
|
||||
|
||||
cline "prompt" -s hooks_enabled=true
|
||||
|
||||
Configure hooks globally:
|
||||
|
||||
cline config set hooks-enabled=true
|
||||
cline config get hooks-enabled
|
||||
|
||||
Note: Hooks in the CLI are only supported on macOS and Linux.
|
||||
|
||||
For complete hooks documentation, see:
|
||||
<https://docs.cline.bot/features/hooks/index>
|
||||
|
||||
NOTES & EXAMPLES
|
||||
The cline task send and cline task new commands support reading from
|
||||
stdin, enabling powerful pipeline compositions:
|
||||
@@ -335,6 +377,43 @@ COPYRIGHT
|
||||
Copyright © 2025 Cline Bot Inc. Licensed under the Apache License 2.0.
|
||||
```
|
||||
|
||||
## JSON output (-F json)
|
||||
|
||||
When you run a command with `-F json` (or `--output-format json`), Cline prints each client message as JSON.
|
||||
|
||||
### ClineMessage schema
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
|-------|------|----------|-------|
|
||||
| `type` | `"ask" or "say"` | Yes | Top-level message category. |
|
||||
| `text` | `string` | Yes | Human-readable message content. |
|
||||
| `ts` | `number` | Yes | Unix epoch timestamp in milliseconds. |
|
||||
| `reasoning` | `string` | No | Omitted when empty. |
|
||||
| `say` | `string` | No | Omitted when empty. Present when `type` is `"say"`. |
|
||||
| `ask` | `string` | No | Omitted when empty. Present when `type` is `"ask"`. |
|
||||
| `partial` | `boolean` | No | Omitted when false. `true` for streaming updates. |
|
||||
| `images` | `string[]` | No | Omitted when empty. Image URIs when included with a message. |
|
||||
| `files` | `string[]` | No | Omitted when empty. File paths when attached to a message. |
|
||||
| `lastCheckpointHash` | `string` | No | Omitted when empty. Git checkpoint hash when available. |
|
||||
| `isCheckpointCheckedOut` | `boolean` | No | Omitted when false. `true` if Cline checked out a checkpoint. |
|
||||
| `isOperationOutsideWorkspace` | `boolean` | No | Omitted when false. `true` if an operation happened outside the workspace. |
|
||||
|
||||
<Note>
|
||||
Most fields are optional and omitted when empty. If you parse this output, treat missing fields as “not present”, not as empty strings.
|
||||
</Note>
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "say",
|
||||
"text": "Cline is about to run a command.",
|
||||
"ts": 1760501486669,
|
||||
"say": "command",
|
||||
"partial": false
|
||||
}
|
||||
```
|
||||
|
||||
### Shell Completion
|
||||
|
||||
Generate autocompletion scripts for various shells:
|
||||
|
||||
@@ -57,6 +57,20 @@ During installation, you'll authenticate and configure your preferred provider u
|
||||
- Create GitLab pipelines that generate migration scripts from schema changes
|
||||
- Build Jenkins jobs that use Cline to analyze test failures and suggest fixes
|
||||
|
||||
## Hooks integration
|
||||
|
||||
[Hooks](/features/hooks/index) let you inject custom logic into Cline's workflow to validate operations and enforce policies. You can enable hooks when running tasks from the command line:
|
||||
|
||||
```bash
|
||||
# Enable hooks for a task
|
||||
cline "What does this repo do?" -s hooks_enabled=true
|
||||
|
||||
# Configure hooks globally via CLI
|
||||
cline config set hooks-enabled=true
|
||||
```
|
||||
|
||||
This allows you to integrate hooks into automated workflows, CI/CD pipelines, and headless task execution for consistent enforcement across all environments.
|
||||
|
||||
## Learn more
|
||||
|
||||
<Columns cols={2}>
|
||||
|
||||
@@ -9,7 +9,7 @@ Automate GitHub issue analysis with AI. Mention `@cline` in any issue comment to
|
||||
|
||||
|
||||
<Note>
|
||||
**New to Cline CLI?** This sample assumes you understand Cline CLI basics and have completed the [Installation Guide](https://docs.cline.bot/cline-cli/installation). If you're new to Cline CLI, we recommend starting with the [GitHub RCA sample](../github-issue-rca) first, as it's simpler and will help you understand the fundamentals before setting up GitHub Actions.
|
||||
**New to Cline CLI?** This sample assumes you understand Cline CLI basics and have completed the [Installation Guide](https://docs.cline.bot/cline-cli/installation). If you're new to Cline CLI, we recommend starting with the [GitHub RCA sample](./github-issue-rca) first, as it's simpler and will help you understand the fundamentals before setting up GitHub Actions.
|
||||
</Note>
|
||||
|
||||
## The Workflow
|
||||
|
||||
@@ -113,6 +113,20 @@ cline instances kill -a
|
||||
Keep track of instance addresses returned by `cline instance new`. When scripting multiple agents, store these IDs and direct your tasks to the appropriate instance.
|
||||
</Tip>
|
||||
|
||||
## Configuring context window for local providers
|
||||
|
||||
For Ollama and LM Studio, you can configure the model context window via CLI:
|
||||
|
||||
```bash
|
||||
# For Ollama
|
||||
cline config s ollama-api-options-ctx-num=32768
|
||||
|
||||
# For LM Studio
|
||||
cline config s lm-studio-max-tokens=32768
|
||||
```
|
||||
|
||||
For other providers (Anthropic, OpenRouter, etc.), the context window is defined per model in the model metadata and is not user-configurable—Cline uses each model's built-in context limits automatically.
|
||||
|
||||
## Choosing the right flow
|
||||
|
||||
- **Interactive mode**: Best for exploring new problems, learning how Cline works, or when you want to review plans before execution
|
||||
@@ -138,7 +152,7 @@ For in-depth commands and flags, check out the [CLI reference](/cline-cli/cli-re
|
||||
Understand how YOLO mode works and when to use full automation versus manual approval.
|
||||
</Card>
|
||||
|
||||
<Card title="Task management" icon="clipboard-check" href="/getting-started/task-management">
|
||||
<Card title="Task management" icon="clipboard-check" href="/features/tasks/task-management">
|
||||
Learn how Cline tracks and manages tasks, including saving and restoring state from checkpoints.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
},
|
||||
"features/auto-approve",
|
||||
"features/auto-compact",
|
||||
"features/background-edit",
|
||||
"features/checkpoints",
|
||||
"features/cline-rules",
|
||||
{
|
||||
@@ -137,8 +138,16 @@
|
||||
"features/dictation",
|
||||
"features/drag-and-drop",
|
||||
"features/editing-messages",
|
||||
"features/explain-changes",
|
||||
"features/focus-chain",
|
||||
"features/hooks",
|
||||
{
|
||||
"group": "Hooks",
|
||||
"pages": [
|
||||
"features/hooks/index",
|
||||
"features/hooks/hook-reference",
|
||||
"features/hooks/samples"
|
||||
]
|
||||
},
|
||||
"features/multiroot-workspace",
|
||||
"features/plan-and-act",
|
||||
{
|
||||
@@ -146,12 +155,20 @@
|
||||
"pages": [
|
||||
"features/slash-commands/new-task",
|
||||
"features/slash-commands/new-rule",
|
||||
"features/slash-commands/explain-changes",
|
||||
"features/slash-commands/smol",
|
||||
"features/slash-commands/report-bug",
|
||||
"features/slash-commands/deep-planning"
|
||||
]
|
||||
},
|
||||
"features/slash-commands/workflows",
|
||||
{
|
||||
"group": "Workflows",
|
||||
"pages": [
|
||||
"features/slash-commands/workflows/index",
|
||||
"features/slash-commands/workflows/quickstart",
|
||||
"features/slash-commands/workflows/best-practices"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Task Management",
|
||||
"pages": [
|
||||
@@ -189,6 +206,7 @@
|
||||
"provider-config/fireworks",
|
||||
"provider-config/zai",
|
||||
"provider-config/gcp-vertex-ai",
|
||||
"provider-config/baseten",
|
||||
{
|
||||
"group": "AWS Bedrock",
|
||||
"pages": [
|
||||
@@ -215,8 +233,7 @@
|
||||
"provider-config/vscode-language-model-api",
|
||||
"provider-config/sap-aicore",
|
||||
"provider-config/vercel-ai-gateway",
|
||||
"provider-config/requesty",
|
||||
"provider-config/baseten"
|
||||
"provider-config/requesty"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -241,32 +258,79 @@
|
||||
"exploring-clines-tools/remote-browser-support"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Enterprise",
|
||||
"pages": [
|
||||
"enterprise-solutions/overview",
|
||||
"enterprise-solutions/security-concerns"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Reference",
|
||||
"pages": [
|
||||
"troubleshooting/networking-and-proxies",
|
||||
"troubleshooting/terminal-quick-fixes",
|
||||
"troubleshooting/terminal-integration-guide",
|
||||
"troubleshooting/task-history-recovery",
|
||||
"more-info/telemetry"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Enterprise",
|
||||
"icon": "building",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Enterprise Solutions",
|
||||
"pages": [
|
||||
"enterprise-solutions/overview",
|
||||
"enterprise-solutions/onboarding",
|
||||
"enterprise-solutions/sso-setup",
|
||||
"enterprise-solutions/team-management/managing-members",
|
||||
{
|
||||
"group": "SaaS Provider Configuration",
|
||||
"pages": [
|
||||
"enterprise-solutions/configuration/remote-configuration/overview",
|
||||
{
|
||||
"group": "AWS Bedrock",
|
||||
"pages": [
|
||||
"enterprise-solutions/configuration/remote-configuration/aws-bedrock/admin-configuration",
|
||||
"enterprise-solutions/configuration/remote-configuration/aws-bedrock/member-configuration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "LiteLLM",
|
||||
"pages": [
|
||||
"enterprise-solutions/configuration/remote-configuration/litellm/admin-configuration",
|
||||
"enterprise-solutions/configuration/remote-configuration/litellm/member-configuration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Google Vertex AI",
|
||||
"pages": [
|
||||
"enterprise-solutions/configuration/remote-configuration/google-vertex/admin-configuration",
|
||||
"enterprise-solutions/configuration/remote-configuration/google-vertex/member-configuration"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Control Other Cline Features",
|
||||
"pages": [
|
||||
"enterprise-solutions/configuration/infrastructure-configuration/control-other-cline-features/yolo-mode"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Monitoring",
|
||||
"pages": [
|
||||
"enterprise-solutions/monitoring/overview",
|
||||
"enterprise-solutions/monitoring/telemetry",
|
||||
"enterprise-solutions/monitoring/opentelemetry",
|
||||
"enterprise-solutions/monitoring/opentelemetry_override"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Learn",
|
||||
"icon": "graduation-cap",
|
||||
"href": "https://cline.bot/learn"
|
||||
},
|
||||
{
|
||||
"tab": "Blog",
|
||||
"icon": "newspaper",
|
||||
"href": "https://cline.bot/blog"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -328,6 +392,34 @@
|
||||
{
|
||||
"source": "/cline-cli/samples",
|
||||
"destination": "/cline-cli/samples/overview"
|
||||
},
|
||||
{
|
||||
"source": "/features/hooks/real-world-examples",
|
||||
"destination": "/features/hooks/samples"
|
||||
},
|
||||
{
|
||||
"source": "/enterprise-solutions/configure-AWS-Bedrock-Admin",
|
||||
"destination": "/enterprise-solutions/configuration/remote-configuration/aws-bedrock/admin-configuration"
|
||||
},
|
||||
{
|
||||
"source": "/enterprise-solutions/configure-AWS-Bedrock-Member",
|
||||
"destination": "/enterprise-solutions/configuration/remote-configuration/aws-bedrock/member-configuration"
|
||||
},
|
||||
{
|
||||
"source": "/enterprise-solutions/configure-workOS-authkit",
|
||||
"destination": "/enterprise-solutions/onboarding"
|
||||
},
|
||||
{
|
||||
"source": "/enterprise-solutions/Onboarding your Organization",
|
||||
"destination": "/enterprise-solutions/onboarding"
|
||||
},
|
||||
{
|
||||
"source": "/enterprise-solutions/team-management/overview",
|
||||
"destination": "/enterprise-solutions/team-management/managing-members"
|
||||
},
|
||||
{
|
||||
"source": "/enterprise-solutions/team-management/roles-and-permissions",
|
||||
"destination": "/enterprise-solutions/team-management/managing-members"
|
||||
}
|
||||
],
|
||||
"search": {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: "Choosing Your Configuration Path"
|
||||
sidebarTitle: "Deployment Guide"
|
||||
description: "Decide between SaaS and Self-Hosted configuration for your Cline Enterprise deployment"
|
||||
---
|
||||
|
||||
Choose the right configuration approach for your organization. Most teams start with SaaS for quick deployment, while enterprises with complex requirements opt for self-hosted infrastructure.
|
||||
|
||||
## Configuration Paths
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="SaaS Provider Configuration" icon="cloud" href="/enterprise-solutions/configuration/remote-configuration/overview">
|
||||
### Quick Setup via Web Console
|
||||
|
||||
✅ No infrastructure required
|
||||
✅ 5-10 minute configuration
|
||||
✅ Web-based admin console
|
||||
✅ Automatic updates
|
||||
✅ Simplified credential management
|
||||
|
||||
**Best for:**
|
||||
- Small to medium teams (5-50 developers)
|
||||
- Quick deployment needs
|
||||
- Limited DevOps resources
|
||||
- Standard security requirements
|
||||
- Single region deployments
|
||||
</Card>
|
||||
|
||||
<Card title="Self-Hosted Configuration" icon="server" href="/enterprise-solutions/configuration/infrastructure-configuration/overview">
|
||||
### Full Infrastructure Control
|
||||
|
||||
✅ Your own AWS/GCP/K8s
|
||||
✅ VPC endpoints & private connectivity
|
||||
✅ Multi-account setups
|
||||
✅ Advanced compliance & audit
|
||||
✅ GitOps workflows
|
||||
|
||||
**Best for:**
|
||||
- Large enterprises (50+ developers)
|
||||
- Complex security requirements
|
||||
- Existing cloud infrastructure
|
||||
- Multi-region deployments
|
||||
- Custom compliance needs
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Detailed Comparison
|
||||
|
||||
### Feature Comparison
|
||||
|
||||
| Feature | SaaS | Self-Hosted |
|
||||
|---------|------|-------------|
|
||||
| **Configuration** | Web UI | YAML + Helm/Kubernetes |
|
||||
| **Infrastructure** | None required | Full AWS/GCP/K8s |
|
||||
| **VPC Endpoints** | Basic | Full private connectivity |
|
||||
| **Multi-Account** | ❌ | ✅ |
|
||||
| **IAM** | Standard RBAC roles | Standard RBAC roles |
|
||||
| **Compliance** | Standard | Custom frameworks |
|
||||
| **GitOps** | ❌ | ✅ |
|
||||
| **Maintenance** | Managed by Cline | Self-managed |
|
||||
| **Updates** | Automatic (extension) | Automatic (extension) + Infrastructure control |
|
||||
|
||||
### Security & Compliance
|
||||
|
||||
| Capability | SaaS | Self-Hosted |
|
||||
|------------|------|-------------|
|
||||
| **Network Encryption** | HTTPS/TLS | HTTPS/TLS |
|
||||
| **Network** | Public internet | Private VPC endpoints |
|
||||
| **Access Control** | Standard RBAC | Standard RBAC |
|
||||
| **Audit Logs** | OpenTelemetry traces | OpenTelemetry traces + Infrastructure logs |
|
||||
| **Data Residency** | Cline-managed deployment | Customer-controlled deployment |
|
||||
|
||||
### Cost Structure
|
||||
|
||||
| Cost Category | SaaS | Self-Hosted |
|
||||
|---------------|------|-------------|
|
||||
| **Cline Subscription** | Fixed enterprise fee | Fixed enterprise fee |
|
||||
| **Inference Provider Costs** | Usage-based | Usage-based |
|
||||
| **Infrastructure** | ✅ None required | Kubernetes, networking, storage |
|
||||
| **Personnel** | ✅ None required | DevOps team needed |
|
||||
| **Total Cost Profile** | Predictable and simple | Variable based on scale |
|
||||
|
||||
## Migration Path
|
||||
|
||||
<Note>
|
||||
Most organizations start with SaaS configuration for quick deployment, then migrate to self-hosted later as requirements grow. This minimizes risk and ensures your infrastructure meets actual usage patterns.
|
||||
</Note>
|
||||
|
||||
## Getting Started
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Start with SaaS" icon="rocket" href="/enterprise-solutions/configuration/remote-configuration/overview">
|
||||
Begin with quick SaaS setup
|
||||
</Card>
|
||||
|
||||
<Card title="Deploy Self-Hosted" icon="server" href="/enterprise-solutions/configuration/infrastructure-configuration/overview">
|
||||
Plan your infrastructure deployment
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Need Help Deciding?
|
||||
|
||||
- [**Contact Cline Enterprise Sales**](https://cline.bot/contact-sales) for a consultation on your specific requirements
|
||||
- [**Start with SaaS**](/enterprise-solutions/configuration/remote-configuration/overview) if unsure - it's lower risk and you can always migrate later
|
||||
- [**Review Self-Hosted Requirements**](/enterprise-solutions/configuration/infrastructure-configuration/overview) if you have existing infrastructure that could benefit from self-hosted deployment
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "Overview"
|
||||
sidebarTitle: "Overview"
|
||||
description: "Configure Cline settings for your enterprise deployment"
|
||||
---
|
||||
|
||||
This section covers configuration options for controlling Cline's behavior in enterprise deployments.
|
||||
|
||||
## Available Settings
|
||||
|
||||
<Card title="YOLO Mode" icon="rocket" href="/enterprise-solutions/configuration/infrastructure-configuration/control-other-cline-features/yolo-mode">
|
||||
Control enterprise access to autonomous operation mode with complete auto-approval
|
||||
</Card>
|
||||
|
||||
## Configuration Methods
|
||||
|
||||
These settings can be configured through:
|
||||
|
||||
|
||||
### Individual Users
|
||||
- Users can toggle settings in their local Cline interface
|
||||
- Enterprise policies can restrict certain settings
|
||||
- Changes apply immediately to new tasks
|
||||
|
||||
## Enterprise Controls
|
||||
|
||||
Administrators can enforce policies through remote configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"yoloModeAllowed": false
|
||||
}
|
||||
```
|
||||
|
||||
When `yoloModeAllowed` is set to `false`, users cannot enable YOLO Mode in their local Cline interface.
|
||||
@@ -0,0 +1,233 @@
|
||||
---
|
||||
title: "YOLO Mode"
|
||||
sidebarTitle: "YOLO Mode"
|
||||
description: "Enterprise controls for YOLO Mode autonomous operation"
|
||||
---
|
||||
|
||||
YOLO Mode enables Cline to operate with complete autonomy, auto-approving all actions without user confirmation. For Enterprise administrators, this page covers how to control access to YOLO Mode across your organization.
|
||||
|
||||
<Note>
|
||||
For complete details about YOLO Mode functionality, risks, and best practices, see [YOLO Mode in Features](/features/yolo-mode).
|
||||
</Note>
|
||||
|
||||
## Overview
|
||||
|
||||
When YOLO Mode is enabled, Cline automatically approves all operations including file changes, terminal commands, browser actions, and mode transitions. This provides maximum automation speed but removes all safety guardrails.
|
||||
|
||||
<Warning>
|
||||
YOLO Mode is powerful but potentially dangerous. Administrators should carefully consider which teams or users should have access to this feature.
|
||||
</Warning>
|
||||
|
||||
## Enterprise Administrator Configuration
|
||||
|
||||
As an Enterprise administrator, you can control whether users in your organization can enable YOLO Mode through remote configuration.
|
||||
|
||||
### Disabling YOLO Mode for All Users
|
||||
|
||||
Add the following to your remote configuration JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"yoloModeAllowed": false
|
||||
}
|
||||
```
|
||||
|
||||
When `yoloModeAllowed` is set to `false`:
|
||||
- The YOLO Mode toggle is disabled in all user interfaces
|
||||
- Users cannot enable YOLO Mode even in their local settings
|
||||
- This policy applies immediately to all team members
|
||||
- Enterprise policy takes precedence over individual preferences
|
||||
|
||||
### Enabling YOLO Mode for All Users
|
||||
|
||||
```json
|
||||
{
|
||||
"yoloModeAllowed": true
|
||||
}
|
||||
```
|
||||
|
||||
When `yoloModeAllowed` is set to `true` or omitted:
|
||||
- Users can enable or disable YOLO Mode in their local Cline settings
|
||||
- Individual users make their own decisions about using YOLO Mode
|
||||
- No organizational restrictions apply
|
||||
|
||||
## Enterprise Policy Recommendations
|
||||
|
||||
### Recommended Approach
|
||||
|
||||
Most organizations should **disable YOLO Mode by default** for the following reasons:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Security & Compliance" icon="shield">
|
||||
YOLO Mode removes all approval gates, potentially allowing:
|
||||
- Unreviewed code changes to critical systems
|
||||
- Execution of commands without oversight
|
||||
- Automated actions that may violate compliance policies
|
||||
- Risk of data exposure through unmonitored operations
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Code Quality Control" icon="code">
|
||||
Without approval prompts:
|
||||
- Changes happen too quickly to review in real-time
|
||||
- Mistakes can compound before detection
|
||||
- Quality gates are bypassed
|
||||
- Rollback becomes more complex
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Audit Requirements" icon="clipboard-check">
|
||||
Many industries require:
|
||||
- Documented approval trails for code changes
|
||||
- Clear accountability for automated actions
|
||||
- Traceable decision-making processes
|
||||
- YOLO Mode may conflict with these requirements
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Exceptions: When to Allow YOLO Mode
|
||||
|
||||
Consider enabling YOLO Mode for:
|
||||
|
||||
**Sandbox/Development Environments**
|
||||
- Isolated testing environments
|
||||
- Personal development machines
|
||||
- Proof-of-concept projects
|
||||
- Temporary exploratory work
|
||||
|
||||
**Specialized Roles**
|
||||
- DevOps automation engineers (with proper monitoring)
|
||||
- Research & development teams in sandboxed environments
|
||||
- Teams with robust rollback and recovery procedures
|
||||
|
||||
**Controlled Use Cases**
|
||||
- Scripted CI/CD pipelines with comprehensive logging
|
||||
- Automated testing scenarios
|
||||
- Demonstration or training environments
|
||||
|
||||
## Enterprise Considerations
|
||||
|
||||
### Security Implications
|
||||
|
||||
When YOLO Mode is enabled in your organization:
|
||||
|
||||
**Risk Factors:**
|
||||
- All tool executions happen automatically without human review
|
||||
- Potential for rapid propagation of mistakes across multiple files
|
||||
- Reduced opportunity to catch security vulnerabilities before implementation
|
||||
- Automated operations may bypass existing security controls
|
||||
|
||||
**Mitigations:**
|
||||
- Implement comprehensive logging and monitoring
|
||||
- Restrict YOLO Mode to non-production environments
|
||||
- Require periodic security reviews for teams using YOLO Mode
|
||||
- Ensure version control and rollback procedures are in place
|
||||
|
||||
### Monitoring Requirements
|
||||
|
||||
When allowing YOLO Mode in your organization, implement:
|
||||
|
||||
**Mandatory Monitoring:**
|
||||
1. **Real-time Activity Tracking**
|
||||
- Monitor which users enable YOLO Mode
|
||||
- Track when YOLO Mode is active
|
||||
- Log all automated actions taken
|
||||
|
||||
2. **Audit Trail Maintenance**
|
||||
- Preserve complete history of YOLO Mode sessions
|
||||
- Document what was automated and when
|
||||
- Maintain records for compliance purposes
|
||||
|
||||
3. **Anomaly Detection**
|
||||
- Alert on unusual patterns of automated actions
|
||||
- Flag high-risk operations performed automatically
|
||||
- Monitor for potential security incidents
|
||||
|
||||
### Monitoring YOLO Mode Usage
|
||||
|
||||
When YOLO Mode is enabled (by policy), track usage through:
|
||||
|
||||
**Telemetry Events:**
|
||||
- Captures when users toggle YOLO Mode on/off
|
||||
- Records which tasks were executed with YOLO Mode enabled
|
||||
- Provides aggregate usage statistics across your organization
|
||||
|
||||
**Task History:**
|
||||
- Task metadata indicates whether YOLO Mode was active
|
||||
- Complete action logs show automated approvals
|
||||
- Enables post-action review and analysis
|
||||
|
||||
**Audit Logs:**
|
||||
- Standard logging captures all automated decisions
|
||||
- Tool executions are recorded with timestamps
|
||||
- Provides compliance trail for regulated environments
|
||||
|
||||
## Recommended Policies by Organization Size
|
||||
|
||||
### Small Teams (5-20 developers)
|
||||
- **Default:** Disabled
|
||||
- **Exceptions:** Allow for individual sandbox environments
|
||||
- **Monitoring:** Basic telemetry sufficient
|
||||
|
||||
### Medium Organizations (20-100 developers)
|
||||
- **Default:** Disabled
|
||||
- **Exceptions:** Permit for designated dev/test environments only
|
||||
- **Monitoring:** Required telemetry + regular audit reviews
|
||||
|
||||
### Large Enterprises (100+ developers)
|
||||
- **Default:** Strictly disabled
|
||||
- **Exceptions:** Require security approval for each use case
|
||||
- **Monitoring:** Comprehensive telemetry + real-time alerting + compliance reporting
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### Configuration Management
|
||||
|
||||
**Centralized Control through Remote Configuration:**
|
||||
|
||||
```json
|
||||
{
|
||||
"yoloModeAllowed": false,
|
||||
// Other policies...
|
||||
}
|
||||
```
|
||||
|
||||
This setting:
|
||||
- Applies instantly to all connected clients
|
||||
- Cannot be overridden by individual users
|
||||
- Persists across Cline restarts
|
||||
- Is synchronized across all team members
|
||||
|
||||
### Policy Enforcement
|
||||
|
||||
The enforcement mechanism:
|
||||
1. Users authenticate with your enterprise configuration server
|
||||
2. Remote configuration is downloaded and applied
|
||||
3. Local UI respects enterprise policy settings
|
||||
4. YOLO Mode toggle is disabled if policy forbids it
|
||||
5. Users see a message explaining the enterprise restriction
|
||||
|
||||
## Compliance Considerations
|
||||
|
||||
For organizations in regulated industries:
|
||||
|
||||
**SOC 2 Compliance:**
|
||||
- YOLO Mode may conflict with change management controls
|
||||
- Document decision to allow/disallow in security policies
|
||||
- Implement compensating controls if YOLO Mode is permitted
|
||||
|
||||
**GDPR/Data Protection:**
|
||||
- Automated operations must still respect data handling policies
|
||||
- Ensure YOLO Mode doesn't bypass data protection safeguards
|
||||
- Maintain audit trails of automated data processing
|
||||
|
||||
**Industry-Specific:**
|
||||
- Financial services: Generally incompatible with Reg requirements
|
||||
- Healthcare: May violate HIPAA audit trail requirements
|
||||
- Government: Often conflicts with approval workflow mandates
|
||||
|
||||
## Support & Questions
|
||||
|
||||
For help configuring YOLO Mode policies:
|
||||
- Review [Remote Configuration Overview](/enterprise-solutions/configuration/remote-configuration/overview)
|
||||
- See [Features: YOLO Mode](/features/yolo-mode) for detailed functionality
|
||||
- Contact your Enterprise support representative
|
||||
- Join our [Discord](https://discord.gg/cline) for community discussion
|
||||
@@ -0,0 +1,565 @@
|
||||
---
|
||||
title: "MCP Marketplace"
|
||||
sidebarTitle: "MCP Marketplace"
|
||||
description: "Deploy pre-built enterprise MCP servers from the Cline marketplace with one-click configuration"
|
||||
---
|
||||
|
||||
The MCP Marketplace provides curated, enterprise-ready integrations with popular development tools and services. All marketplace servers are built with enterprise security, compliance, and scalability in mind.
|
||||
|
||||
## Enterprise Marketplace Benefits
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="One-Click Deployment" icon="rocket">
|
||||
Deploy complex integrations instantly with pre-configured enterprise settings.
|
||||
</Card>
|
||||
|
||||
<Card title="Security Hardened" icon="shield-check">
|
||||
All servers include enterprise security features, audit logging, and compliance controls.
|
||||
</Card>
|
||||
|
||||
<Card title="Maintained & Updated" icon="sync">
|
||||
Regular security updates and feature enhancements managed by Cline Enterprise team.
|
||||
</Card>
|
||||
|
||||
<Card title="Enterprise Support" icon="headset">
|
||||
Dedicated support channels for marketplace integration issues and customization.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Available Integrations
|
||||
|
||||
### Development Tools
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="GitHub Enterprise" icon="github">
|
||||
Repository management, issue tracking, PR workflows, and code analysis
|
||||
</Card>
|
||||
|
||||
<Card title="GitLab Enterprise" icon="gitlab">
|
||||
Project management, CI/CD pipelines, merge requests, and security scanning
|
||||
</Card>
|
||||
|
||||
<Card title="Bitbucket Enterprise" icon="bitbucket">
|
||||
Source code management, build pipelines, and deployment automation
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Project Management
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Jira Enterprise" icon="jira">
|
||||
Issue tracking, sprint management, custom fields, and workflow automation
|
||||
</Card>
|
||||
|
||||
<Card title="Azure DevOps" icon="microsoft">
|
||||
Work items, boards, repos, pipelines, and test management
|
||||
</Card>
|
||||
|
||||
<Card title="Linear" icon="linear">
|
||||
Issue tracking, project planning, and development workflow integration
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Communication & Collaboration
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Slack Enterprise Grid" icon="slack">
|
||||
Notifications, bot interactions, file sharing, and workflow automation
|
||||
</Card>
|
||||
|
||||
<Card title="Microsoft Teams" icon="microsoft-teams">
|
||||
Chat notifications, meeting integration, and collaborative workflows
|
||||
</Card>
|
||||
|
||||
<Card title="Discord" icon="discord">
|
||||
Community management, bot interactions, and developer notifications
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Cloud Services
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="AWS Services" icon="aws">
|
||||
EC2, S3, Lambda, RDS, CloudWatch, and other AWS service integrations
|
||||
</Card>
|
||||
|
||||
<Card title="Google Cloud" icon="google-cloud">
|
||||
Compute Engine, Cloud Storage, BigQuery, and GCP service management
|
||||
</Card>
|
||||
|
||||
<Card title="Azure Services" icon="azure">
|
||||
Virtual Machines, Storage Accounts, Functions, and Azure resource management
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Installing Marketplace Servers
|
||||
|
||||
### Via Cline Enterprise Dashboard
|
||||
|
||||
1. **Access Marketplace**: Navigate to `Settings > Enterprise > MCP Marketplace`
|
||||
2. **Browse Integrations**: Filter by category, popularity, or search by name
|
||||
3. **Review Details**: Check compatibility, permissions, and configuration requirements
|
||||
4. **Install**: Click "Install" and configure required settings
|
||||
5. **Deploy**: Approve deployment to your selected environment
|
||||
|
||||
### Via Configuration File
|
||||
|
||||
Install marketplace servers through enterprise configuration:
|
||||
|
||||
```yaml
|
||||
# enterprise-mcp-config.yaml
|
||||
mcp:
|
||||
marketplace_servers:
|
||||
- name: "github-enterprise"
|
||||
package: "@cline/mcp-github-enterprise"
|
||||
version: "2.1.0"
|
||||
environment: "production"
|
||||
|
||||
config:
|
||||
github:
|
||||
base_url: "https://github.company.com/api/v3"
|
||||
token: "${GITHUB_ENTERPRISE_TOKEN}"
|
||||
organization: "company"
|
||||
|
||||
features:
|
||||
issue_management: true
|
||||
pull_request_automation: true
|
||||
code_analysis: true
|
||||
security_scanning: true
|
||||
|
||||
permissions:
|
||||
repositories: "read-write"
|
||||
issues: "write"
|
||||
pull_requests: "write"
|
||||
|
||||
compliance:
|
||||
audit_logging: true
|
||||
data_retention_days: 365
|
||||
encryption_at_rest: true
|
||||
|
||||
- name: "jira-enterprise"
|
||||
package: "@cline/mcp-jira-enterprise"
|
||||
version: "1.8.3"
|
||||
environment: "production"
|
||||
|
||||
config:
|
||||
jira:
|
||||
base_url: "https://company.atlassian.net"
|
||||
username: "${JIRA_USERNAME}"
|
||||
api_token: "${JIRA_API_TOKEN}"
|
||||
|
||||
projects:
|
||||
- key: "DEV"
|
||||
permissions: ["read", "write", "transition"]
|
||||
- key: "OPS"
|
||||
permissions: ["read", "comment"]
|
||||
|
||||
compliance:
|
||||
field_encryption: ["description", "comments"]
|
||||
audit_trail: true
|
||||
```
|
||||
|
||||
### Via CLI
|
||||
|
||||
Deploy using the Cline Enterprise CLI:
|
||||
|
||||
```bash
|
||||
# Install GitHub Enterprise integration
|
||||
cline-enterprise mcp install github-enterprise \
|
||||
--version 2.1.0 \
|
||||
--config-file github-config.yaml \
|
||||
--environment production
|
||||
|
||||
# Install Slack Enterprise Grid integration
|
||||
cline-enterprise mcp install slack-enterprise-grid \
|
||||
--version 1.5.2 \
|
||||
--config workspace_id=T1234567890 \
|
||||
--config bot_token=${SLACK_BOT_TOKEN} \
|
||||
--environment production
|
||||
|
||||
# List installed marketplace servers
|
||||
cline-enterprise mcp list --environment production
|
||||
|
||||
# Check server status
|
||||
cline-enterprise mcp status github-enterprise --environment production
|
||||
```
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### GitHub Enterprise Integration
|
||||
|
||||
```yaml
|
||||
# github-enterprise-config.yaml
|
||||
github:
|
||||
base_url: "https://github.company.com/api/v3"
|
||||
token: "${GITHUB_ENTERPRISE_TOKEN}"
|
||||
organization: "company"
|
||||
|
||||
# Repository access controls
|
||||
repositories:
|
||||
allowed_patterns:
|
||||
- "company/*"
|
||||
- "internal/*"
|
||||
blocked_patterns:
|
||||
- "*/secrets"
|
||||
- "*/private-keys"
|
||||
|
||||
# Feature configuration
|
||||
features:
|
||||
issue_management:
|
||||
enabled: true
|
||||
auto_assign: true
|
||||
labels:
|
||||
- "ai-generated"
|
||||
- "cline-task"
|
||||
|
||||
pull_requests:
|
||||
enabled: true
|
||||
auto_review_request: true
|
||||
required_approvals: 2
|
||||
enforce_branch_protection: true
|
||||
|
||||
code_analysis:
|
||||
enabled: true
|
||||
languages: ["typescript", "python", "go", "rust"]
|
||||
security_scan: true
|
||||
|
||||
# Security and compliance
|
||||
security:
|
||||
webhook_secret: "${GITHUB_WEBHOOK_SECRET}"
|
||||
rate_limiting:
|
||||
requests_per_hour: 5000
|
||||
burst_limit: 100
|
||||
ip_whitelist:
|
||||
- "10.0.0.0/8"
|
||||
- "192.168.0.0/16"
|
||||
|
||||
audit:
|
||||
log_level: "INFO"
|
||||
include_payloads: false
|
||||
retention_days: 365
|
||||
destinations: ["datadog", "splunk"]
|
||||
```
|
||||
|
||||
### Jira Enterprise Integration
|
||||
|
||||
```yaml
|
||||
# jira-enterprise-config.yaml
|
||||
jira:
|
||||
base_url: "https://company.atlassian.net"
|
||||
username: "${JIRA_USERNAME}"
|
||||
api_token: "${JIRA_API_TOKEN}"
|
||||
|
||||
# Project access configuration
|
||||
projects:
|
||||
- key: "DEV"
|
||||
name: "Development"
|
||||
permissions: ["read", "write", "transition", "assign"]
|
||||
issue_types: ["Story", "Bug", "Task", "Subtask"]
|
||||
|
||||
- key: "OPS"
|
||||
name: "Operations"
|
||||
permissions: ["read", "comment", "watch"]
|
||||
|
||||
# Custom field mappings
|
||||
custom_fields:
|
||||
story_points: "customfield_10002"
|
||||
epic_link: "customfield_10014"
|
||||
sprint: "customfield_10020"
|
||||
|
||||
# Workflow automation
|
||||
automation:
|
||||
auto_transition:
|
||||
enabled: true
|
||||
rules:
|
||||
- from_status: "To Do"
|
||||
to_status: "In Progress"
|
||||
condition: "assignee_changed"
|
||||
|
||||
auto_assign:
|
||||
enabled: true
|
||||
rules:
|
||||
- issue_type: "Bug"
|
||||
component: "Frontend"
|
||||
assignee: "frontend-team-lead"
|
||||
|
||||
# Security and compliance
|
||||
security:
|
||||
encrypt_fields: ["description", "comment"]
|
||||
mask_sensitive_data: true
|
||||
audit_changes: true
|
||||
|
||||
compliance:
|
||||
gdpr_compliant: true
|
||||
data_retention_policy: "365_days"
|
||||
audit_log_retention: "7_years"
|
||||
```
|
||||
|
||||
### Slack Enterprise Grid Integration
|
||||
|
||||
```yaml
|
||||
# slack-enterprise-config.yaml
|
||||
slack:
|
||||
workspace_id: "T1234567890"
|
||||
bot_token: "${SLACK_BOT_TOKEN}"
|
||||
signing_secret: "${SLACK_SIGNING_SECRET}"
|
||||
|
||||
# Channel management
|
||||
channels:
|
||||
notifications:
|
||||
- name: "#dev-alerts"
|
||||
types: ["deployments", "errors", "security"]
|
||||
- name: "#ai-activity"
|
||||
types: ["cline-tasks", "completions"]
|
||||
|
||||
private_channels:
|
||||
- name: "#security-incidents"
|
||||
members: ["security-team"]
|
||||
types: ["security-alerts", "compliance-issues"]
|
||||
|
||||
# Bot behavior
|
||||
bot:
|
||||
display_name: "Cline Enterprise"
|
||||
default_channel: "#general"
|
||||
response_delay_ms: 1000
|
||||
|
||||
commands:
|
||||
- command: "/cline-status"
|
||||
description: "Check Cline Enterprise status"
|
||||
permission: "all"
|
||||
|
||||
- command: "/cline-deploy"
|
||||
description: "Trigger deployment"
|
||||
permission: "admin"
|
||||
|
||||
# Enterprise features
|
||||
enterprise:
|
||||
app_approval_required: true
|
||||
data_residency: "US"
|
||||
compliance_export: true
|
||||
|
||||
dlp:
|
||||
enabled: true
|
||||
scan_messages: true
|
||||
block_sensitive_data: true
|
||||
|
||||
# Security settings
|
||||
security:
|
||||
require_app_approval: true
|
||||
audit_api_calls: true
|
||||
encrypt_messages: true
|
||||
retain_audit_logs_days: 2555 # 7 years
|
||||
```
|
||||
|
||||
## Enterprise Management
|
||||
|
||||
### Multi-Environment Deployment
|
||||
|
||||
Deploy marketplace servers across environments:
|
||||
|
||||
```yaml
|
||||
# environments-config.yaml
|
||||
environments:
|
||||
development:
|
||||
marketplace_servers:
|
||||
- github-enterprise:
|
||||
version: "2.1.0-beta"
|
||||
config_override:
|
||||
github:
|
||||
base_url: "https://github-dev.company.com/api/v3"
|
||||
organization: "company-dev"
|
||||
|
||||
staging:
|
||||
marketplace_servers:
|
||||
- github-enterprise:
|
||||
version: "2.1.0-rc1"
|
||||
config_override:
|
||||
github:
|
||||
base_url: "https://github-staging.company.com/api/v3"
|
||||
organization: "company-staging"
|
||||
|
||||
production:
|
||||
marketplace_servers:
|
||||
- github-enterprise:
|
||||
version: "2.1.0"
|
||||
config_override:
|
||||
github:
|
||||
base_url: "https://github.company.com/api/v3"
|
||||
organization: "company"
|
||||
```
|
||||
|
||||
### Version Management
|
||||
|
||||
Control marketplace server versions:
|
||||
|
||||
```bash
|
||||
# List available versions
|
||||
cline-enterprise mcp versions github-enterprise
|
||||
|
||||
# Upgrade to latest version
|
||||
cline-enterprise mcp upgrade github-enterprise --version 2.2.0 --environment staging
|
||||
|
||||
# Rollback to previous version
|
||||
cline-enterprise mcp rollback github-enterprise --version 2.1.0 --environment staging
|
||||
|
||||
# Pin to specific version (disable auto-updates)
|
||||
cline-enterprise mcp pin github-enterprise --version 2.1.0
|
||||
```
|
||||
|
||||
### Health Monitoring
|
||||
|
||||
Monitor marketplace server health:
|
||||
|
||||
```yaml
|
||||
# monitoring-config.yaml
|
||||
monitoring:
|
||||
marketplace_servers:
|
||||
health_checks:
|
||||
interval_seconds: 30
|
||||
timeout_seconds: 10
|
||||
|
||||
metrics:
|
||||
- server_status
|
||||
- request_latency
|
||||
- error_rate
|
||||
- resource_usage
|
||||
|
||||
alerts:
|
||||
- name: "marketplace-server-down"
|
||||
condition: "server_status != 1"
|
||||
severity: "critical"
|
||||
|
||||
- name: "high-error-rate"
|
||||
condition: "error_rate > 0.05"
|
||||
severity: "warning"
|
||||
|
||||
- name: "performance-degradation"
|
||||
condition: "request_latency > 5s"
|
||||
severity: "warning"
|
||||
```
|
||||
|
||||
## Security & Compliance
|
||||
|
||||
### Enterprise Security Features
|
||||
|
||||
All marketplace servers include:
|
||||
|
||||
- **Authentication Integration**: SSO, SAML, OAuth2 support
|
||||
- **Authorization Controls**: RBAC and fine-grained permissions
|
||||
- **Audit Logging**: Comprehensive activity tracking
|
||||
- **Data Encryption**: At-rest and in-transit encryption
|
||||
- **Network Security**: VPN, IP whitelisting, private endpoints
|
||||
- **Compliance**: SOC2, GDPR, HIPAA compliance frameworks
|
||||
|
||||
### Data Governance
|
||||
|
||||
Configure data handling policies:
|
||||
|
||||
```yaml
|
||||
# data-governance-config.yaml
|
||||
data_governance:
|
||||
classification:
|
||||
public:
|
||||
retention_days: 90
|
||||
backup_required: false
|
||||
|
||||
internal:
|
||||
retention_days: 365
|
||||
backup_required: true
|
||||
encryption_required: false
|
||||
|
||||
confidential:
|
||||
retention_days: 2555 # 7 years
|
||||
backup_required: true
|
||||
encryption_required: true
|
||||
audit_access: true
|
||||
|
||||
restricted:
|
||||
retention_days: 2555
|
||||
backup_required: true
|
||||
encryption_required: true
|
||||
audit_access: true
|
||||
approval_required: true
|
||||
|
||||
privacy:
|
||||
pii_detection: true
|
||||
pii_masking: true
|
||||
gdpr_compliance: true
|
||||
data_subject_requests: true
|
||||
|
||||
compliance:
|
||||
frameworks: ["SOC2", "GDPR", "CCPA", "HIPAA"]
|
||||
audit_frequency: "quarterly"
|
||||
certification_renewal: "annual"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Installation
|
||||
1. **Review Permissions**: Always review required permissions before installation
|
||||
2. **Test in Staging**: Deploy to staging environment first
|
||||
3. **Configuration Validation**: Validate configuration files before deployment
|
||||
4. **Backup Current State**: Create configuration backups before changes
|
||||
5. **Monitor Deployment**: Watch health metrics during rollout
|
||||
|
||||
### Configuration
|
||||
1. **Environment Separation**: Use different configurations per environment
|
||||
2. **Secret Management**: Store sensitive data in secure secret stores
|
||||
3. **Version Pinning**: Pin versions for production deployments
|
||||
4. **Access Controls**: Implement least-privilege access policies
|
||||
5. **Regular Updates**: Schedule regular security and feature updates
|
||||
|
||||
### Monitoring
|
||||
1. **Health Checks**: Monitor server health continuously
|
||||
2. **Performance Metrics**: Track latency and throughput
|
||||
3. **Error Tracking**: Alert on error rates and failure patterns
|
||||
4. **Resource Usage**: Monitor CPU, memory, and network usage
|
||||
5. **Audit Reviews**: Regular review of audit logs and access patterns
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Installation Failures**:
|
||||
```bash
|
||||
# Check marketplace connectivity
|
||||
cline-enterprise mcp marketplace-status
|
||||
|
||||
# Verify authentication
|
||||
cline-enterprise auth verify --service marketplace
|
||||
|
||||
# Check installation logs
|
||||
cline-enterprise logs mcp-installer --lines 100
|
||||
```
|
||||
|
||||
**Configuration Errors**:
|
||||
```bash
|
||||
# Validate configuration
|
||||
cline-enterprise mcp validate-config --file config.yaml
|
||||
|
||||
# Test connectivity
|
||||
cline-enterprise mcp test-connection github-enterprise --environment staging
|
||||
|
||||
# Check server status
|
||||
cline-enterprise mcp status --all
|
||||
```
|
||||
|
||||
**Performance Issues**:
|
||||
```bash
|
||||
# Check server metrics
|
||||
cline-enterprise mcp metrics github-enterprise --duration 1h
|
||||
|
||||
# View recent error logs
|
||||
cline-enterprise logs github-enterprise --level error --lines 50
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For marketplace server issues:
|
||||
|
||||
- **Documentation**: Check server-specific documentation in the dashboard
|
||||
- **Community**: Join the Cline Enterprise community forum
|
||||
- **Support Tickets**: Create support tickets for critical issues
|
||||
- **Professional Services**: Engage professional services for custom configurations
|
||||
|
||||
Enterprise customers have access to dedicated support channels with SLA guarantees.
|
||||
@@ -0,0 +1,571 @@
|
||||
---
|
||||
title: "MCP Integration"
|
||||
sidebarTitle: "Overview"
|
||||
description: "Configure Model Context Protocol (MCP) servers and marketplace integrations for enterprise Cline deployments"
|
||||
---
|
||||
|
||||
Model Context Protocol (MCP) provides standardized communication between AI models and external data sources, tools, and services. Enterprise MCP integration allows you to securely connect Cline to your organization's systems while maintaining governance and compliance.
|
||||
|
||||
## Enterprise MCP Benefits
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Extensible Architecture" icon="puzzle-piece">
|
||||
Connect to unlimited external tools, databases, APIs, and services through standardized MCP servers.
|
||||
</Card>
|
||||
|
||||
<Card title="Enterprise Security" icon="shield-alt">
|
||||
Secure authentication, authorization, and audit trails for all MCP server communications.
|
||||
</Card>
|
||||
|
||||
<Card title="Centralized Management" icon="network-wired">
|
||||
Manage and deploy MCP servers enterprise-wide with version control and configuration management.
|
||||
</Card>
|
||||
|
||||
<Card title="Compliance Ready" icon="clipboard-check">
|
||||
Built-in logging, monitoring, and data governance for regulatory compliance requirements.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## MCP Architecture Overview
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A[Cline Enterprise] --> B[MCP Hub]
|
||||
B --> C[MCP Marketplace]
|
||||
B --> D[Remote MCP Servers]
|
||||
B --> E[Internal MCP Servers]
|
||||
|
||||
C --> F[GitHub Integration]
|
||||
C --> G[Slack Integration]
|
||||
C --> H[Jira Integration]
|
||||
|
||||
D --> I[Custom APIs]
|
||||
D --> J[Databases]
|
||||
D --> K[Cloud Services]
|
||||
|
||||
E --> L[Internal Tools]
|
||||
E --> M[Legacy Systems]
|
||||
E --> N[Security Systems]
|
||||
|
||||
O[Enterprise Admin] --> B
|
||||
P[Audit Logging] --> B
|
||||
Q[Authentication] --> B
|
||||
```
|
||||
|
||||
## Core Components
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="MCP Marketplace" icon="store" href="/enterprise-solutions/configuration/infrastructure-configuration/mcp/mcp-marketplace">
|
||||
Pre-built, enterprise-ready MCP servers for popular tools and services with one-click deployment.
|
||||
</Card>
|
||||
|
||||
<Card title="Remote MCP Servers" icon="server" href="/enterprise-solutions/configuration/infrastructure-configuration/mcp/remote-mcp-servers">
|
||||
Deploy and manage custom MCP servers across your infrastructure with centralized configuration.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Enterprise Configuration
|
||||
|
||||
### Basic MCP Hub Setup
|
||||
|
||||
Configure the central MCP hub for your enterprise deployment:
|
||||
|
||||
```yaml
|
||||
# mcp-hub-config.yaml
|
||||
mcp:
|
||||
hub:
|
||||
enabled: true
|
||||
port: 8080
|
||||
authentication:
|
||||
method: "enterprise-sso"
|
||||
jwt_secret: "${MCP_JWT_SECRET}"
|
||||
|
||||
# Server discovery
|
||||
discovery:
|
||||
methods: ["marketplace", "remote", "local"]
|
||||
marketplace_url: "https://mcp.cline.bot/marketplace"
|
||||
|
||||
# Security settings
|
||||
security:
|
||||
enforce_tls: true
|
||||
allowed_origins: ["https://*.company.com"]
|
||||
rate_limiting:
|
||||
requests_per_minute: 1000
|
||||
burst_size: 100
|
||||
|
||||
# Audit and compliance
|
||||
audit:
|
||||
enabled: true
|
||||
log_level: "INFO"
|
||||
destinations: ["file", "syslog", "datadog"]
|
||||
retention_days: 90
|
||||
```
|
||||
|
||||
### Multi-Environment Configuration
|
||||
|
||||
Deploy MCP configurations across environments:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Development">
|
||||
```yaml
|
||||
# mcp-dev-config.yaml
|
||||
mcp:
|
||||
environment: "development"
|
||||
|
||||
servers:
|
||||
- name: "github-dev"
|
||||
type: "marketplace"
|
||||
package: "@cline/mcp-github"
|
||||
version: "latest"
|
||||
config:
|
||||
github_token: "${GITHUB_DEV_TOKEN}"
|
||||
org: "company-dev"
|
||||
|
||||
- name: "local-db"
|
||||
type: "remote"
|
||||
url: "http://localhost:3001"
|
||||
auth:
|
||||
type: "api-key"
|
||||
key: "${DEV_DB_API_KEY}"
|
||||
|
||||
policies:
|
||||
allow_experimental: true
|
||||
auto_update: true
|
||||
rate_limits:
|
||||
relaxed: true
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Production">
|
||||
```yaml
|
||||
# mcp-prod-config.yaml
|
||||
mcp:
|
||||
environment: "production"
|
||||
|
||||
servers:
|
||||
- name: "github-prod"
|
||||
type: "marketplace"
|
||||
package: "@cline/mcp-github"
|
||||
version: "1.2.3" # Pinned version
|
||||
config:
|
||||
github_token: "${GITHUB_PROD_TOKEN}"
|
||||
org: "company"
|
||||
|
||||
- name: "crm-integration"
|
||||
type: "remote"
|
||||
url: "https://mcp-crm.internal.company.com"
|
||||
auth:
|
||||
type: "mtls"
|
||||
cert_path: "/certs/mcp-client.pem"
|
||||
key_path: "/certs/mcp-client-key.pem"
|
||||
|
||||
- name: "security-scanner"
|
||||
type: "remote"
|
||||
url: "https://security-mcp.company.com"
|
||||
auth:
|
||||
type: "oauth2"
|
||||
client_id: "${SECURITY_CLIENT_ID}"
|
||||
client_secret: "${SECURITY_CLIENT_SECRET}"
|
||||
|
||||
policies:
|
||||
allow_experimental: false
|
||||
auto_update: false
|
||||
strict_versioning: true
|
||||
|
||||
monitoring:
|
||||
metrics: true
|
||||
health_checks: true
|
||||
alert_on_failure: true
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Server Management
|
||||
|
||||
### Lifecycle Management
|
||||
|
||||
Manage MCP server deployments with GitOps:
|
||||
|
||||
```yaml
|
||||
# mcp-server-manifest.yaml
|
||||
apiVersion: mcp.cline.bot/v1
|
||||
kind: MCPServer
|
||||
metadata:
|
||||
name: custom-api-server
|
||||
namespace: cline-enterprise
|
||||
spec:
|
||||
image: company/custom-mcp-server:v1.0.0
|
||||
replicas: 3
|
||||
|
||||
config:
|
||||
api_endpoint: "https://api.internal.company.com"
|
||||
timeout: 30s
|
||||
retry_attempts: 3
|
||||
|
||||
auth:
|
||||
type: service-account
|
||||
service_account: mcp-custom-api
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
|
||||
monitoring:
|
||||
enabled: true
|
||||
metrics_port: 9090
|
||||
health_endpoint: "/health"
|
||||
|
||||
security:
|
||||
network_policy: strict
|
||||
pod_security_standard: restricted
|
||||
```
|
||||
|
||||
### Configuration Management
|
||||
|
||||
Use Helm charts for enterprise MCP deployments:
|
||||
|
||||
```yaml
|
||||
# values-prod.yaml
|
||||
mcp:
|
||||
hub:
|
||||
replicaCount: 3
|
||||
image:
|
||||
repository: cline/mcp-hub-enterprise
|
||||
tag: "1.5.2"
|
||||
|
||||
servers:
|
||||
marketplace:
|
||||
enabled: true
|
||||
catalog_url: "https://enterprise-catalog.company.com"
|
||||
|
||||
custom:
|
||||
- name: "salesforce"
|
||||
enabled: true
|
||||
image: "company/mcp-salesforce:1.0.0"
|
||||
config:
|
||||
instance_url: "https://company.my.salesforce.com"
|
||||
|
||||
- name: "jira"
|
||||
enabled: true
|
||||
image: "company/mcp-jira:2.1.0"
|
||||
config:
|
||||
base_url: "https://company.atlassian.net"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
hosts:
|
||||
- host: mcp.company.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: mcp-tls
|
||||
hosts:
|
||||
- mcp.company.com
|
||||
```
|
||||
|
||||
## Security & Governance
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
Configure enterprise authentication for MCP servers:
|
||||
|
||||
```yaml
|
||||
# mcp-auth-config.yaml
|
||||
authentication:
|
||||
providers:
|
||||
- name: "enterprise-sso"
|
||||
type: "oidc"
|
||||
issuer: "https://sso.company.com"
|
||||
client_id: "${SSO_CLIENT_ID}"
|
||||
client_secret: "${SSO_CLIENT_SECRET}"
|
||||
|
||||
- name: "service-accounts"
|
||||
type: "jwt"
|
||||
signing_key: "${SERVICE_ACCOUNT_KEY}"
|
||||
|
||||
authorization:
|
||||
policies:
|
||||
- name: "developers"
|
||||
subjects: ["group:developers"]
|
||||
resources: ["mcp:servers:read", "mcp:servers:execute"]
|
||||
|
||||
- name: "admins"
|
||||
subjects: ["group:mcp-admins"]
|
||||
resources: ["mcp:*"]
|
||||
|
||||
- name: "security-team"
|
||||
subjects: ["group:security"]
|
||||
resources: ["mcp:audit:*", "mcp:servers:security-*"]
|
||||
|
||||
rbac:
|
||||
enabled: true
|
||||
default_role: "viewer"
|
||||
```
|
||||
|
||||
### Network Security
|
||||
|
||||
Implement network policies for MCP communications:
|
||||
|
||||
```yaml
|
||||
# mcp-network-policy.yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: mcp-server-policy
|
||||
namespace: cline-enterprise
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: mcp-server
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
name: cline-enterprise
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: cline-core
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
|
||||
egress:
|
||||
# Allow DNS
|
||||
- to: []
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
# Allow HTTPS to external APIs
|
||||
- to: []
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
```
|
||||
|
||||
## Monitoring & Observability
|
||||
|
||||
### Metrics Collection
|
||||
|
||||
Configure comprehensive MCP monitoring:
|
||||
|
||||
```yaml
|
||||
# mcp-monitoring.yaml
|
||||
monitoring:
|
||||
metrics:
|
||||
enabled: true
|
||||
interval: 30s
|
||||
|
||||
collectors:
|
||||
- name: "server-health"
|
||||
metrics:
|
||||
- mcp_server_status
|
||||
- mcp_server_response_time
|
||||
- mcp_server_error_rate
|
||||
|
||||
- name: "hub-performance"
|
||||
metrics:
|
||||
- mcp_hub_requests_total
|
||||
- mcp_hub_request_duration
|
||||
- mcp_hub_active_connections
|
||||
|
||||
- name: "resource-usage"
|
||||
metrics:
|
||||
- mcp_memory_usage
|
||||
- mcp_cpu_usage
|
||||
- mcp_network_io
|
||||
|
||||
alerts:
|
||||
- name: "server-down"
|
||||
condition: "mcp_server_status == 0"
|
||||
severity: "critical"
|
||||
notification_channels: ["pagerduty", "slack"]
|
||||
|
||||
- name: "high-error-rate"
|
||||
condition: "mcp_server_error_rate > 0.05"
|
||||
severity: "warning"
|
||||
notification_channels: ["slack"]
|
||||
|
||||
- name: "performance-degradation"
|
||||
condition: "mcp_server_response_time > 5s"
|
||||
severity: "warning"
|
||||
notification_channels: ["email"]
|
||||
```
|
||||
|
||||
### Audit Logging
|
||||
|
||||
Implement comprehensive audit trails:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2024-01-15T10:30:00Z",
|
||||
"event_type": "mcp_server_call",
|
||||
"user_id": "john.doe@company.com",
|
||||
"session_id": "sess_abc123",
|
||||
"server_name": "github-prod",
|
||||
"method": "github.create_issue",
|
||||
"request": {
|
||||
"repository": "company/project",
|
||||
"title": "Bug fix required",
|
||||
"sensitive_data_detected": false
|
||||
},
|
||||
"response": {
|
||||
"status": "success",
|
||||
"issue_id": "12345",
|
||||
"duration_ms": 234
|
||||
},
|
||||
"compliance": {
|
||||
"data_classification": "internal",
|
||||
"retention_required": true,
|
||||
"pii_detected": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Custom MCP Server Development
|
||||
|
||||
### Development Framework
|
||||
|
||||
Create custom MCP servers using the enterprise SDK:
|
||||
|
||||
```typescript
|
||||
// custom-mcp-server.ts
|
||||
import { MCPServer, Tool, Resource } from '@cline/mcp-enterprise-sdk';
|
||||
|
||||
class CustomAPIServer extends MCPServer {
|
||||
constructor() {
|
||||
super({
|
||||
name: 'custom-api-server',
|
||||
version: '1.0.0',
|
||||
description: 'Custom API integration server'
|
||||
});
|
||||
|
||||
this.addTool(new DatabaseQueryTool());
|
||||
this.addResource(new UserDataResource());
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseQueryTool implements Tool {
|
||||
name = 'query_database';
|
||||
description = 'Query the company database';
|
||||
|
||||
async execute(params: any) {
|
||||
// Implement database query logic
|
||||
const result = await this.database.query(params.sql);
|
||||
|
||||
// Audit log the query
|
||||
await this.auditLog({
|
||||
action: 'database_query',
|
||||
query: params.sql,
|
||||
user: params.user_id,
|
||||
results_count: result.length
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async validate(params: any): Promise<boolean> {
|
||||
// Implement query validation
|
||||
return params.sql && !this.containsMaliciousSQL(params.sql);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Deployment Pipeline
|
||||
|
||||
Automate MCP server deployments:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/deploy-mcp-server.yml
|
||||
name: Deploy MCP Server
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['mcp-servers/**']
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build MCP Server
|
||||
run: |
|
||||
docker build -t company/mcp-server:${{ github.sha }} .
|
||||
docker push company/mcp-server:${{ github.sha }}
|
||||
|
||||
- name: Deploy to Staging
|
||||
run: |
|
||||
helm upgrade mcp-server-staging ./helm-chart \
|
||||
--set image.tag=${{ github.sha }} \
|
||||
--namespace mcp-staging
|
||||
|
||||
- name: Run Integration Tests
|
||||
run: |
|
||||
kubectl wait --for=condition=ready pod -l app=mcp-server -n mcp-staging
|
||||
npm run test:integration
|
||||
|
||||
- name: Deploy to Production
|
||||
if: success()
|
||||
run: |
|
||||
helm upgrade mcp-server-prod ./helm-chart \
|
||||
--set image.tag=${{ github.sha }} \
|
||||
--namespace mcp-prod
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Security
|
||||
1. **Authentication**: Always require authentication for MCP servers
|
||||
2. **Encryption**: Use TLS for all MCP communications
|
||||
3. **Validation**: Validate all inputs and sanitize outputs
|
||||
4. **Least Privilege**: Grant minimal required permissions
|
||||
5. **Audit**: Log all MCP server interactions
|
||||
|
||||
### Performance
|
||||
1. **Caching**: Implement response caching where appropriate
|
||||
2. **Connection Pooling**: Reuse connections to external services
|
||||
3. **Async Operations**: Use non-blocking operations for I/O
|
||||
4. **Resource Limits**: Set appropriate CPU and memory limits
|
||||
5. **Load Balancing**: Scale MCP servers based on demand
|
||||
|
||||
### Reliability
|
||||
1. **Health Checks**: Implement comprehensive health endpoints
|
||||
2. **Circuit Breakers**: Fail fast when external services are down
|
||||
3. **Retry Logic**: Implement exponential backoff for failures
|
||||
4. **Graceful Degradation**: Provide fallback behavior
|
||||
5. **Monitoring**: Set up proactive alerting and monitoring
|
||||
|
||||
## Production Checklist
|
||||
|
||||
Before deploying MCP servers to production:
|
||||
|
||||
- [ ] Security review completed
|
||||
- [ ] Authentication and authorization configured
|
||||
- [ ] Network policies implemented
|
||||
- [ ] Monitoring and alerting set up
|
||||
- [ ] Audit logging enabled
|
||||
- [ ] Resource limits configured
|
||||
- [ ] Health checks implemented
|
||||
- [ ] Integration tests passing
|
||||
- [ ] Disaster recovery plan documented
|
||||
- [ ] Compliance requirements validated
|
||||
|
||||
## Getting Started
|
||||
|
||||
Ready to implement enterprise MCP integration? Start with:
|
||||
|
||||
1. [MCP Marketplace](/enterprise-solutions/configuration/infrastructure-configuration/mcp/mcp-marketplace) - Deploy pre-built integrations
|
||||
2. [Remote MCP Servers](/enterprise-solutions/configuration/infrastructure-configuration/mcp/remote-mcp-servers) - Configure custom servers
|
||||
3. Review our [MCP Development Guide](/mcp/mcp-overview) for building custom integrations
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "Self-Hosted Configuration"
|
||||
sidebarTitle: "Overview"
|
||||
description: "Deploy and configure Cline on your own infrastructure with enterprise-grade security and compliance"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**Self-Hosted Configuration Path**
|
||||
|
||||
This section is for enterprises deploying **self-hosted Cline infrastructure** with complex security, compliance, and multi-environment requirements. Configuration is done through YAML files, Kubernetes/Helm deployments, and infrastructure-as-code.
|
||||
|
||||
**Looking for simple setup?** See [SaaS Provider Configuration](/enterprise-solutions/configuration/remote-configuration/overview) for quick configuration through the app.cline.bot admin console - no infrastructure deployment required, just web-based settings.
|
||||
</Warning>
|
||||
|
||||
Self-Hosted Configuration provides centralized control over all aspects of your Cline deployment on your own infrastructure, from AI providers to custom workflows. This section covers how to configure, manage, and optimize your enterprise Cline installation with advanced security, compliance, and operational features.
|
||||
|
||||
## Configuration Categories
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Providers" icon="cloud" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/overview">
|
||||
Configure AI providers including AWS Bedrock, LiteLLM, and Google Vertex AI with enterprise-grade security and governance.
|
||||
</Card>
|
||||
|
||||
<Card title="MCP Integration" icon="plug" href="/enterprise-solutions/configuration/infrastructure-configuration/mcp/overview">
|
||||
Manage Model Context Protocol servers, marketplace integrations, and remote MCP server configurations.
|
||||
</Card>
|
||||
|
||||
<Card title="Rules Engine" icon="shield-check" href="/enterprise-solutions/configuration/infrastructure-configuration/rules">
|
||||
Define and enforce enterprise governance rules, security policies, and compliance requirements.
|
||||
</Card>
|
||||
|
||||
<Card title="Workflows" icon="workflow" href="/enterprise-solutions/configuration/infrastructure-configuration/workflows">
|
||||
Create automated workflows for development processes, approval chains, and integration pipelines.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Advanced Controls
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Control Other Cline Features" icon="toggles" href="/enterprise-solutions/configuration/infrastructure-configuration/control-other-cline-features/overview">
|
||||
Enable or disable specific Cline features across your organization with granular permission controls.
|
||||
</Card>
|
||||
|
||||
<Card title="Monitoring" icon="chart-line" href="/enterprise-solutions/monitoring/overview">
|
||||
Configure OpenTelemetry integration for comprehensive monitoring, logging, and analytics.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **Assessment**: Review your current infrastructure and integration requirements
|
||||
2. **Provider Setup**: Configure your preferred AI providers with enterprise credentials
|
||||
3. **Security Configuration**: Implement rules and access controls
|
||||
4. **Monitoring Setup**: Enable telemetry and monitoring for operational visibility
|
||||
5. **User Onboarding**: Deploy configurations to your development teams
|
||||
|
||||
## Enterprise Architecture Considerations
|
||||
|
||||
### Security & Compliance
|
||||
- **Zero Trust Architecture**: All configurations support zero-trust security models
|
||||
- **Audit Logging**: Complete audit trails for all configuration changes
|
||||
- **Role-Based Access**: Granular permissions for different administrative roles
|
||||
- **Data Sovereignty**: Keep sensitive data within your infrastructure boundaries
|
||||
|
||||
### Scalability & Performance
|
||||
- **Multi-Region Support**: Deploy configurations across multiple geographic regions
|
||||
- **Load Balancing**: Distribute AI provider requests across multiple endpoints
|
||||
- **Caching Strategies**: Optimize performance with intelligent caching
|
||||
- **Rate Limiting**: Prevent abuse with configurable rate limits
|
||||
|
||||
### Integration & Automation
|
||||
- **GitOps Integration**: Version control your configurations alongside code
|
||||
- **CI/CD Pipeline Integration**: Automate configuration deployment
|
||||
- **Webhook Support**: React to configuration changes with custom automation
|
||||
- **API-First Design**: Programmatically manage all configurations
|
||||
|
||||
## Configuration Management
|
||||
|
||||
All enterprise configurations support:
|
||||
|
||||
- **Version Control**: Track changes with full revision history
|
||||
- **Environment Promotion**: Deploy configurations from dev → staging → production
|
||||
- **Rollback Capabilities**: Quickly revert problematic configurations
|
||||
- **Configuration Validation**: Automated testing of configuration changes
|
||||
- **Drift Detection**: Monitor and alert on configuration drift
|
||||
|
||||
## Next Steps
|
||||
|
||||
Ready to configure your enterprise deployment? Start with:
|
||||
|
||||
1. [Provider Configuration](/enterprise-solutions/configuration/infrastructure-configuration/providers/overview) - Set up your AI providers
|
||||
2. [Security Rules](/enterprise-solutions/configuration/infrastructure-configuration/rules) - Implement governance policies
|
||||
3. [Monitoring Setup](/enterprise-solutions/monitoring/overview) - Enable operational visibility
|
||||
|
||||
For hands-on configuration assistance, contact your Cline Enterprise support team or refer to our implementation guides.
|
||||
@@ -0,0 +1,182 @@
|
||||
---
|
||||
title: "AWS Bedrock Configuration"
|
||||
sidebarTitle: "AWS Bedrock"
|
||||
description: "Configure AWS Bedrock for your Cline deployment"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Configuration Path: Self-Hosted**
|
||||
|
||||
This guide covers Bedrock configuration for self-hosted deployments. For simple web-based setup, see [AWS Bedrock SaaS Configuration](/enterprise-solutions/configuration/remote-configuration/aws-bedrock/admin-configuration).
|
||||
</Info>
|
||||
|
||||
Configure Cline to use AWS Bedrock for enterprise access to Claude and other foundation models through Amazon's managed service.
|
||||
|
||||
## Configuration Format
|
||||
|
||||
Configure Bedrock through your remote configuration JSON using the `providerSettings.AwsBedrock` section:
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"AwsBedrock": {
|
||||
"models": [
|
||||
{
|
||||
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
}
|
||||
],
|
||||
"awsRegion": "us-east-1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Fields
|
||||
|
||||
| Field | Type | Description | Required |
|
||||
|-------|------|-------------|----------|
|
||||
| `models` | Array | List of model configurations | Yes |
|
||||
| `awsRegion` | String | AWS region (e.g., `us-east-1`) | Yes |
|
||||
| `awsUseCrossRegionInference` | Boolean | Enable cross-region inference | No |
|
||||
| `awsUseGlobalInference` | Boolean | Enable global inference routing | No |
|
||||
| `awsBedrockUsePromptCache` | Boolean | Enable prompt caching | No |
|
||||
| `awsBedrockEndpoint` | String | Custom Bedrock endpoint URL | No |
|
||||
| `customModels` | Array | Custom model configurations | No |
|
||||
|
||||
### Model Configuration
|
||||
|
||||
Each model in the `models` array requires:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"name": "Claude 3.5 Sonnet",
|
||||
"info": {
|
||||
"maxTokens": 8192,
|
||||
"contextWindow": 200000,
|
||||
"supportsImages": true,
|
||||
"supportsPromptCache": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Model IDs
|
||||
|
||||
| Model ID | Description | Context Window |
|
||||
|----------|-------------|----------------|
|
||||
| `anthropic.claude-3-5-sonnet-20241022-v2:0` | Latest Claude Sonnet | 200K tokens |
|
||||
| `anthropic.claude-3-5-haiku-20241022-v1:0` | Latest Claude Haiku | 200K tokens |
|
||||
| `anthropic.claude-3-opus-20240229-v1:0` | Claude Opus | 200K tokens |
|
||||
|
||||
<Note>
|
||||
Model availability varies by region. See [AWS Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/models-regions.html) for region-specific model availability.
|
||||
</Note>
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"AwsBedrock": {
|
||||
"models": [
|
||||
{
|
||||
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
}
|
||||
],
|
||||
"awsRegion": "us-east-1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Prompt Caching
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"AwsBedrock": {
|
||||
"models": [
|
||||
{
|
||||
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
}
|
||||
],
|
||||
"awsRegion": "us-east-1",
|
||||
"awsBedrockUsePromptCache": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Models
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"AwsBedrock": {
|
||||
"models": [
|
||||
{
|
||||
"id": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
},
|
||||
{
|
||||
"id": "anthropic.claude-3-5-haiku-20241022-v1:0",
|
||||
"name": "Claude 3.5 Haiku"
|
||||
}
|
||||
],
|
||||
"awsRegion": "us-east-1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before configuring Cline to use Bedrock, you need:
|
||||
|
||||
1. **AWS Account** with Bedrock access enabled
|
||||
2. **IAM Permissions** for Bedrock API calls (`bedrock:InvokeModel`, `bedrock:InvokeModelWithResponseStream`)
|
||||
3. **Model Access** enabled for desired models in the Bedrock console
|
||||
4. **AWS Credentials** configured (IAM role, access keys, or AWS profile)
|
||||
|
||||
<Tip>
|
||||
For AWS account setup and IAM configuration, see the [AWS Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html).
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"Access Denied" Errors**
|
||||
|
||||
Ensure your AWS credentials have the required Bedrock permissions. See [AWS IAM documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html) for permission requirements.
|
||||
|
||||
**"Model Not Found" Errors**
|
||||
|
||||
Verify model access is enabled in the AWS Bedrock console and the model is available in your configured region.
|
||||
|
||||
**High Latency**
|
||||
|
||||
Consider using a region closer to your users or enabling cross-region inference for better performance.
|
||||
|
||||
## Related Resources
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="AWS Bedrock Docs" icon="book" href="https://docs.aws.amazon.com/bedrock/">
|
||||
Complete AWS Bedrock documentation
|
||||
</Card>
|
||||
|
||||
<Card title="Model Access" icon="key" href="https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html">
|
||||
How to enable model access
|
||||
</Card>
|
||||
|
||||
<Card title="IAM Permissions" icon="shield" href="https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html">
|
||||
Required IAM permissions
|
||||
</Card>
|
||||
|
||||
<Card title="Pricing" icon="dollar-sign" href="https://aws.amazon.com/bedrock/pricing/">
|
||||
AWS Bedrock pricing details
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,254 @@
|
||||
---
|
||||
title: "Custom Provider Configuration"
|
||||
sidebarTitle: "Custom Providers"
|
||||
description: "Configure custom OpenAI-compatible providers for your Cline deployment"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Configuration Path: Self-Hosted**
|
||||
|
||||
This guide covers custom provider configuration for self-hosted deployments.
|
||||
</Info>
|
||||
|
||||
Configure Cline to use any OpenAI-compatible API provider, including Azure OpenAI, self-hosted inference servers, and other third-party services.
|
||||
|
||||
## What are Custom Providers?
|
||||
|
||||
Custom providers include any API that implements the OpenAI API format:
|
||||
|
||||
- **Azure OpenAI Service**: Microsoft's managed OpenAI models
|
||||
- **vLLM**: Self-hosted inference server
|
||||
- **Ollama**: Local model runner
|
||||
- **Text Generation Inference (TGI)**: Hugging Face's inference server
|
||||
- **LocalAI**: Local OpenAI API replacement
|
||||
- **Other OpenAI-compatible APIs**: Any custom implementation
|
||||
|
||||
## Configuration Format
|
||||
|
||||
Configure custom providers through your remote configuration JSON using the `providerSettings.OpenAiCompatible` section:
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"name": "GPT-4 Turbo"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "https://your-api.company.com/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Fields
|
||||
|
||||
| Field | Type | Description | Required |
|
||||
|-------|------|-------------|----------|
|
||||
| `models` | Array | List of model configurations | Yes |
|
||||
| `openAiBaseUrl` | String | API endpoint base URL | Yes |
|
||||
| `openAiApiKey` | String | API key for authentication | No |
|
||||
| `openAiModelId` | String | Default model identifier | No |
|
||||
|
||||
### Azure OpenAI Specific Fields
|
||||
|
||||
For Azure OpenAI, additional fields are available:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `azureApiVersion` | String | Azure API version (e.g., `2024-02-15-preview`) |
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### Azure OpenAI
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"name": "GPT-4 Turbo"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "https://your-resource.openai.azure.com/openai/deployments/gpt-4-turbo",
|
||||
"openAiApiKey": "your-azure-api-key",
|
||||
"azureApiVersion": "2024-02-15-preview"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Self-Hosted vLLM
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "meta-llama/Llama-2-70b-chat-hf",
|
||||
"name": "Llama 2 70B"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "http://vllm.company.com:8000/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Local Ollama
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "codellama",
|
||||
"name": "Code Llama"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "http://localhost:11434/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Text Generation Inference (TGI)
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "mistralai/Mistral-7B-Instruct-v0.2",
|
||||
"name": "Mistral 7B Instruct"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "http://tgi.company.com:8080/v1",
|
||||
"openAiApiKey": "your-tgi-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### LocalAI
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-3.5-turbo",
|
||||
"name": "Local GPT-3.5"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "http://localhost:8080/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Internal Network (No Auth)
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "custom-model",
|
||||
"name": "Custom Model"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "http://internal.api:8000/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Model Configuration
|
||||
|
||||
Each model requires basic information:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "model-identifier",
|
||||
"name": "Display Name",
|
||||
"info": {
|
||||
"maxTokens": 4096,
|
||||
"contextWindow": 128000,
|
||||
"supportsImages": true,
|
||||
"supportsPromptCache": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before configuring a custom provider, you need:
|
||||
|
||||
1. **API Endpoint**: URL of your OpenAI-compatible API
|
||||
2. **API Key** (if required): Authentication credentials
|
||||
3. **Model IDs**: Names of available models
|
||||
4. **Network Access**: Connectivity from where Cline is being used
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Connection Errors**
|
||||
|
||||
Verify the endpoint is accessible:
|
||||
```bash
|
||||
curl https://your-api.company.com/v1/models
|
||||
```
|
||||
|
||||
**Authentication Errors**
|
||||
|
||||
Test authentication with your API key:
|
||||
```bash
|
||||
curl -H "Authorization: Bearer your-api-key" \
|
||||
https://your-api.company.com/v1/models
|
||||
```
|
||||
|
||||
**Model Not Found**
|
||||
|
||||
Ensure the model ID in your configuration matches what the API expects. Check available models:
|
||||
```bash
|
||||
curl -H "Authorization: Bearer your-api-key" \
|
||||
https://your-api.company.com/v1/models
|
||||
```
|
||||
|
||||
**Timeout Issues**
|
||||
|
||||
If responses are slow:
|
||||
- Check network latency
|
||||
- Verify server has adequate resources
|
||||
- Consider using faster models
|
||||
|
||||
## Provider Documentation
|
||||
|
||||
For setup and deployment of these services, see their official documentation:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Azure OpenAI" icon="microsoft" href="https://learn.microsoft.com/en-us/azure/ai-services/openai/">
|
||||
Microsoft's managed OpenAI service
|
||||
</Card>
|
||||
|
||||
<Card title="vLLM" icon="server" href="https://docs.vllm.ai/">
|
||||
High-performance inference engine
|
||||
</Card>
|
||||
|
||||
<Card title="Ollama" icon="download" href="https://ollama.ai/">
|
||||
Run models locally
|
||||
</Card>
|
||||
|
||||
<Card title="Text Generation Inference" icon="code" href="https://huggingface.co/docs/text-generation-inference/">
|
||||
Hugging Face inference server
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,185 @@
|
||||
---
|
||||
title: "Google Vertex AI Configuration"
|
||||
sidebarTitle: "Google Vertex"
|
||||
description: "Configure Google Vertex AI for your Cline deployment"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Configuration Path: Self-Hosted**
|
||||
|
||||
This guide covers Vertex AI configuration for self-hosted deployments. For simple web-based setup, see [Google Vertex SaaS Configuration](/enterprise-solutions/configuration/remote-configuration/google-vertex/admin-configuration).
|
||||
</Info>
|
||||
|
||||
Configure Cline to use Google Vertex AI for enterprise access to Gemini and other Google AI models through Google Cloud Platform.
|
||||
|
||||
## Configuration Format
|
||||
|
||||
Configure Vertex AI through your remote configuration JSON using the `providerSettings.Vertex` section:
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"Vertex": {
|
||||
"models": [
|
||||
{
|
||||
"id": "claude-3-5-sonnet-v2@20241022",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
}
|
||||
],
|
||||
"vertexProjectId": "my-project-id",
|
||||
"vertexRegion": "us-central1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Fields
|
||||
|
||||
| Field | Type | Description | Required |
|
||||
|-------|------|-------------|----------|
|
||||
| `models` | Array | List of model configurations | Yes |
|
||||
| `vertexProjectId` | String | Google Cloud project ID | Yes |
|
||||
| `vertexRegion` | String | GCP region (e.g., `us-central1`) | Yes |
|
||||
|
||||
### Model Configuration
|
||||
|
||||
Each model in the `models` array requires:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "claude-3-5-sonnet-v2@20241022",
|
||||
"name": "Claude 3.5 Sonnet",
|
||||
"info": {
|
||||
"maxTokens": 8192,
|
||||
"contextWindow": 200000,
|
||||
"supportsImages": true,
|
||||
"supportsPromptCache": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Model IDs
|
||||
|
||||
| Model ID | Description | Context Window |
|
||||
|----------|-------------|----------------|
|
||||
| `claude-3-5-sonnet-v2@20241022` | Claude 3.5 Sonnet | 200K tokens |
|
||||
| `claude-3-5-haiku@20241022` | Claude 3.5 Haiku | 200K tokens |
|
||||
| `claude-3-opus@20240229` | Claude 3 Opus | 200K tokens |
|
||||
| `gemini-2.0-flash-exp` | Gemini Flash (experimental) | 1M tokens |
|
||||
| `gemini-1.5-pro-002` | Gemini Pro | 2M tokens |
|
||||
| `gemini-1.5-flash-002` | Gemini Flash | 1M tokens |
|
||||
|
||||
<Note>
|
||||
Model availability varies by region. See [Vertex AI documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/learn/models) for region-specific model availability.
|
||||
</Note>
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"Vertex": {
|
||||
"models": [
|
||||
{
|
||||
"id": "claude-3-5-sonnet-v2@20241022",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
}
|
||||
],
|
||||
"vertexProjectId": "my-company-prod",
|
||||
"vertexRegion": "us-central1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Models
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"Vertex": {
|
||||
"models": [
|
||||
{
|
||||
"id": "claude-3-5-sonnet-v2@20241022",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
},
|
||||
{
|
||||
"id": "gemini-1.5-pro-002",
|
||||
"name": "Gemini Pro"
|
||||
}
|
||||
],
|
||||
"vertexProjectId": "my-company-prod",
|
||||
"vertexRegion": "us-central1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Extended Thinking
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"Vertex": {
|
||||
"models": [
|
||||
{
|
||||
"id": "claude-3-5-sonnet-v2@20241022",
|
||||
"name": "Claude 3.5 Sonnet",
|
||||
"thinkingBudgetTokens": 1600
|
||||
}
|
||||
],
|
||||
"vertexProjectId": "my-company-prod",
|
||||
"vertexRegion": "us-central1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before configuring Cline to use Vertex AI, you need:
|
||||
|
||||
1. **Google Cloud Project** with Vertex AI API enabled
|
||||
2. **Service Account** with Vertex AI User role (`roles/aiplatform.user`)
|
||||
3. **Service Account Credentials** configured for authentication
|
||||
4. **Model Access** verified in your project and region
|
||||
|
||||
<Tip>
|
||||
For Google Cloud setup and authentication configuration, see the [Vertex AI documentation](https://cloud.google.com/vertex-ai/docs/generative-ai/start/quickstarts/quickstart-multimodal).
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"Permission Denied" Errors**
|
||||
|
||||
Ensure your service account has the required Vertex AI permissions. See [Google Cloud IAM documentation](https://cloud.google.com/vertex-ai/docs/general/access-control) for permission requirements.
|
||||
|
||||
**"API Not Enabled" Errors**
|
||||
|
||||
Verify the Vertex AI API is enabled in your Google Cloud project.
|
||||
|
||||
**"Model Not Found" Errors**
|
||||
|
||||
Check that the model is available in your configured region and that your project has access to it.
|
||||
|
||||
## Related Resources
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Vertex AI Docs" icon="book" href="https://cloud.google.com/vertex-ai/docs">
|
||||
Complete Vertex AI documentation
|
||||
</Card>
|
||||
|
||||
<Card title="Service Accounts" icon="key" href="https://cloud.google.com/iam/docs/service-accounts">
|
||||
Service account best practices
|
||||
</Card>
|
||||
|
||||
<Card title="Model Guide" icon="brain" href="https://cloud.google.com/vertex-ai/docs/generative-ai/learn/models">
|
||||
Available models and features
|
||||
</Card>
|
||||
|
||||
<Card title="Pricing" icon="dollar-sign" href="https://cloud.google.com/vertex-ai/pricing">
|
||||
Vertex AI pricing details
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
title: "LiteLLM Configuration"
|
||||
sidebarTitle: "LiteLLM"
|
||||
description: "Configure LiteLLM proxy for your Cline deployment"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Configuration Path: Self-Hosted**
|
||||
|
||||
This guide covers LiteLLM configuration for self-hosted deployments. For web-based setup, see [LiteLLM SaaS Configuration](/enterprise-solutions/configuration/remote-configuration/litellm/admin-configuration).
|
||||
</Info>
|
||||
|
||||
Configure Cline to use an existing LiteLLM proxy for unified access to multiple AI models through a single API endpoint.
|
||||
|
||||
## What is LiteLLM?
|
||||
|
||||
[LiteLLM](https://github.com/BerriAI/litellm) is an open-source proxy that provides a unified OpenAI-compatible API for accessing 100+ AI models from different providers. Cline connects to your deployed LiteLLM instance.
|
||||
|
||||
<Note>
|
||||
LiteLLM is a separate service you deploy and manage. This guide covers how to configure Cline to connect to an existing LiteLLM deployment.
|
||||
</Note>
|
||||
|
||||
## Configuration Format
|
||||
|
||||
Configure LiteLLM through your remote configuration JSON using the `providerSettings.OpenAiCompatible` section:
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"name": "GPT-4 Turbo"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "https://litellm.yourcompany.com/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Fields
|
||||
|
||||
| Field | Type | Description | Required |
|
||||
|-------|------|-------------|----------|
|
||||
| `models` | Array | List of model configurations | Yes |
|
||||
| `openAiBaseUrl` | String | LiteLLM proxy endpoint URL | Yes |
|
||||
| `openAiApiKey` | String | API key for authentication | No |
|
||||
|
||||
### Model Configuration
|
||||
|
||||
Each model in the `models` array requires:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"name": "GPT-4 Turbo",
|
||||
"info": {
|
||||
"maxTokens": 4096,
|
||||
"contextWindow": 128000,
|
||||
"supportsImages": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
Model IDs must match the model names configured in your LiteLLM proxy deployment.
|
||||
</Note>
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"name": "GPT-4 Turbo"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "https://litellm.company.com/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Authentication
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"name": "GPT-4 Turbo"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "https://litellm.company.com/v1",
|
||||
"openAiApiKey": "sk-your-litellm-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Models
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"name": "GPT-4 Turbo"
|
||||
},
|
||||
{
|
||||
"id": "claude-3-5-sonnet",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
},
|
||||
{
|
||||
"id": "gemini-pro",
|
||||
"name": "Gemini Pro"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "https://litellm.company.com/v1",
|
||||
"openAiApiKey": "sk-your-litellm-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Internal Network (No Auth)
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"OpenAiCompatible": {
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4-turbo",
|
||||
"name": "GPT-4 Turbo"
|
||||
}
|
||||
],
|
||||
"openAiBaseUrl": "http://litellm.internal:4000/v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before configuring Cline to use LiteLLM, you need:
|
||||
|
||||
1. **LiteLLM Proxy** deployed and accessible
|
||||
2. **LiteLLM Configuration** with desired models enabled
|
||||
3. **API Key** (if authentication is enabled)
|
||||
4. **Network Access** from where Cline is being used
|
||||
|
||||
<Tip>
|
||||
For LiteLLM deployment and configuration, see the [LiteLLM documentation](https://docs.litellm.ai/docs/proxy/quick_start).
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Connection Errors**
|
||||
|
||||
Verify the LiteLLM proxy is running and accessible:
|
||||
```bash
|
||||
curl https://litellm.yourcompany.com/health
|
||||
```
|
||||
|
||||
**Authentication Errors**
|
||||
|
||||
Check your API key is valid:
|
||||
```bash
|
||||
curl -H "Authorization: Bearer sk-your-key" \
|
||||
https://litellm.yourcompany.com/v1/models
|
||||
```
|
||||
|
||||
**Model Not Found**
|
||||
|
||||
Verify the model is configured in your LiteLLM deployment. Model IDs in Cline's config must match the model names in LiteLLM's configuration.
|
||||
|
||||
## Benefits of Using LiteLLM
|
||||
|
||||
- **Multi-Provider Access**: Connect to multiple AI providers through one endpoint
|
||||
- **Load Balancing**: Distribute requests across providers automatically
|
||||
- **Fallback Support**: Automatic retry with different models on failure
|
||||
- **Cost Tracking**: Monitor usage and costs across all models
|
||||
- **Rate Limiting**: Control usage at the proxy level
|
||||
|
||||
## Related Resources
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="LiteLLM Docs" icon="book" href="https://docs.litellm.ai/">
|
||||
Complete LiteLLM documentation
|
||||
</Card>
|
||||
|
||||
<Card title="LiteLLM GitHub" icon="github" href="https://github.com/BerriAI/litellm">
|
||||
Source code and deployment examples
|
||||
</Card>
|
||||
|
||||
<Card title="Proxy Setup" icon="server" href="https://docs.litellm.ai/docs/proxy/quick_start">
|
||||
LiteLLM proxy deployment guide
|
||||
</Card>
|
||||
|
||||
<Card title="Supported Providers" icon="list" href="https://docs.litellm.ai/docs/providers">
|
||||
List of supported AI providers
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
title: "AI Provider Configuration"
|
||||
sidebarTitle: "Overview"
|
||||
description: "Configure AI provider settings for your Cline deployment"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Configuration Path: Self-Hosted**
|
||||
|
||||
This section covers provider configuration for self-hosted deployments. For web-based configuration through app.cline.bot, see [SaaS Provider Configuration](/enterprise-solutions/configuration/remote-configuration/overview).
|
||||
</Info>
|
||||
|
||||
Configure which AI providers your team can use and manage provider credentials centrally. Cline supports major AI providers with enterprise-grade authentication options.
|
||||
|
||||
## Supported Providers
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="AWS Bedrock" icon="aws" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/aws-bedrock">
|
||||
Amazon's managed service for Claude and other foundation models
|
||||
</Card>
|
||||
|
||||
<Card title="Google Vertex AI" icon="google" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/google-vertex">
|
||||
Google Cloud's AI platform with Gemini and PaLM models
|
||||
</Card>
|
||||
|
||||
<Card title="LiteLLM" icon="zap" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/litellm">
|
||||
Universal proxy for accessing 100+ AI models through a unified API
|
||||
</Card>
|
||||
|
||||
<Card title="Custom Providers" icon="plug" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/custom">
|
||||
OpenAI-compatible APIs and self-hosted models
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## What is Provider Configuration?
|
||||
|
||||
Provider configuration in Cline allows administrators to:
|
||||
|
||||
1. **Manage Credentials Centrally**: Store API keys and authentication details in one place
|
||||
2. **Control Model Access**: Specify which models teams can use
|
||||
3. **Enforce Provider Usage**: Direct all team members to approved providers
|
||||
|
||||
## How It Works
|
||||
|
||||
Provider settings are configured through your remote configuration JSON file:
|
||||
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"provider": "bedrock",
|
||||
"bedrockRegion": "us-east-1",
|
||||
"bedrockServiceRole": "arn:aws:iam::..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When configured, these settings:
|
||||
- Apply to all team members automatically
|
||||
- Override individual user settings
|
||||
- Ensure consistent provider usage across the team
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Provider Selection
|
||||
|
||||
Choose from supported providers:
|
||||
- **bedrock**: Use AWS Bedrock
|
||||
- **vertex**: Use Google Vertex AI
|
||||
- **openai**: Use OpenAI API
|
||||
- **azure**: Use Azure OpenAI
|
||||
- **litellm**: Use a LiteLLM proxy
|
||||
|
||||
### Authentication
|
||||
|
||||
Each provider supports different authentication methods:
|
||||
|
||||
**AWS Bedrock:**
|
||||
- IAM roles with cross-account access
|
||||
- Access keys (not recommended for production)
|
||||
|
||||
**Google Vertex AI:**
|
||||
- Service account JSON keys
|
||||
- Workload Identity (for GKE deployments)
|
||||
|
||||
**OpenAI/Azure:**
|
||||
- API keys
|
||||
|
||||
**LiteLLM:**
|
||||
- Endpoint URL + API key
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### AWS Bedrock with IAM Role
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"provider": "bedrock",
|
||||
"bedrockRegion": "us-east-1",
|
||||
"bedrockServiceRole": "arn:aws:iam::123456789012:role/ClineBedrockRole"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Google Vertex AI
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"provider": "vertex",
|
||||
"vertexProject": "my-project-id",
|
||||
"vertexRegion": "us-central1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### LiteLLM Proxy
|
||||
```json
|
||||
{
|
||||
"providerSettings": {
|
||||
"provider": "litellm",
|
||||
"litellmBaseUrl": "https://litellm.company.com",
|
||||
"litellmApiKey": "sk-..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Configure AWS Bedrock" icon="aws" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/aws-bedrock">
|
||||
Set up AWS Bedrock integration
|
||||
</Card>
|
||||
|
||||
<Card title="Configure Google Vertex" icon="google" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/google-vertex">
|
||||
Set up Google Vertex AI integration
|
||||
</Card>
|
||||
|
||||
<Card title="Configure LiteLLM" icon="zap" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/litellm">
|
||||
Set up LiteLLM proxy integration
|
||||
</Card>
|
||||
|
||||
<Card title="Configure Custom Provider" icon="plug" href="/enterprise-solutions/configuration/infrastructure-configuration/providers/custom">
|
||||
Set up custom OpenAI-compatible provider
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,239 @@
|
||||
---
|
||||
title: "Rules"
|
||||
sidebarTitle: "Rules"
|
||||
description: "Custom instruction files that guide Cline's behavior in your enterprise deployment"
|
||||
---
|
||||
|
||||
Rules are custom instruction files that provide Cline with guidelines about your coding preferences, standards, and best practices. These instructions get added to Cline's context when working on tasks.
|
||||
|
||||
## What are Rules?
|
||||
|
||||
Rules are simple markdown files stored in a `.clinerules/` directory that contain your team's conventions, preferences, and guidelines. They help Cline understand your:
|
||||
|
||||
- Coding style and conventions
|
||||
- Preferred libraries and frameworks
|
||||
- Architectural patterns
|
||||
- Testing strategies
|
||||
- Documentation standards
|
||||
- Communication preferences
|
||||
|
||||
<Tip>
|
||||
Rules are just `.md` files - no complex configuration needed!
|
||||
</Tip>
|
||||
|
||||
## Quick Example
|
||||
|
||||
Here's a simple rule file that guides TypeScript development:
|
||||
|
||||
```markdown
|
||||
# TypeScript Conventions
|
||||
|
||||
## Code Style
|
||||
- Use 2-space indentation
|
||||
- Prefer `const` over `let`
|
||||
- Always use explicit return types for functions
|
||||
- Use named exports instead of default exports
|
||||
|
||||
## Testing
|
||||
- Write unit tests for all utility functions
|
||||
- Use Vitest as the testing framework
|
||||
- Aim for 80%+ code coverage
|
||||
|
||||
## Dependencies
|
||||
- Prefer native TypeScript features over external libraries
|
||||
- Use Zod for runtime type validation
|
||||
- Use date-fns for date manipulation
|
||||
```
|
||||
|
||||
## Creating Rules
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Using /newrule Command">
|
||||
The easiest way to create a rule is with the `/newrule` command:
|
||||
|
||||
1. During a conversation with Cline, type `/newrule`
|
||||
2. Cline will analyze your conversation and preferences
|
||||
3. It creates an appropriately named `.md` file in `.clinerules/`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
/newrule
|
||||
|
||||
Based on our conversation, create a rule for React component structure
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Manual Creation">
|
||||
You can also create rule files manually:
|
||||
|
||||
1. Create a `.clinerules/` directory in your repository root
|
||||
2. Add markdown files with your guidelines
|
||||
3. Use descriptive names like `react-patterns.md` or `api-conventions.md`
|
||||
|
||||
**File structure:**
|
||||
```
|
||||
your-repo/
|
||||
├── .clinerules/
|
||||
│ ├── typescript-style.md
|
||||
│ ├── testing-standards.md
|
||||
│ └── code-review-checklist.md
|
||||
└── src/
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Global vs Workspace Rules
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Workspace Rules" icon="folder">
|
||||
**Location:** `.clinerules/` in your repository
|
||||
|
||||
**Scope:** Specific to that project
|
||||
|
||||
**Use for:** Project-specific conventions and patterns
|
||||
</Card>
|
||||
|
||||
<Card title="Global Rules" icon="globe">
|
||||
**Location:** `Documents/Cline/` directory
|
||||
|
||||
**Scope:** All your projects
|
||||
|
||||
**Use for:** Personal preferences that apply everywhere
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Managing Rules
|
||||
|
||||
### Toggling Rules
|
||||
|
||||
You can enable or disable individual rule files:
|
||||
|
||||
1. Click the rules icon in Cline's interface
|
||||
2. Toggle rules on/off as needed
|
||||
3. Changes apply immediately to new tasks
|
||||
|
||||
<Note>
|
||||
Disabling a rule removes it from Cline's context, but keeps the file intact. You can re-enable it anytime.
|
||||
</Note>
|
||||
|
||||
### Enterprise Remote Rules
|
||||
|
||||
<Info>
|
||||
Enterprise deployments can configure **remote global rules** that apply to all team members. These are managed through your infrastructure configuration and cannot be toggled off by individual developers.
|
||||
|
||||
See [Self-Hosted Configuration](/enterprise-solutions/configuration/infrastructure-configuration/overview) for details on remote rules.
|
||||
</Info>
|
||||
|
||||
## Compatible Formats
|
||||
|
||||
Cline also respects rules from other AI coding tools:
|
||||
|
||||
| File/Directory | Tool | Location |
|
||||
|----------------|------|----------|
|
||||
| `.cursorrules` | Cursor | Workspace root (single file) |
|
||||
| `.cursor/rules/` | Cursor | Workspace directory (`.mdc` files) |
|
||||
| `.windsurfrules` | Windsurf | Workspace root (single file) |
|
||||
| `AGENTS.md` | Various | Workspace root + recursive search |
|
||||
|
||||
<Note>
|
||||
**AGENTS.md behavior:** Cline only searches for nested `AGENTS.md` files recursively if a top-level `AGENTS.md` exists in your workspace root. If found, all `AGENTS.md` files are combined with their relative paths as headers.
|
||||
</Note>
|
||||
|
||||
These files work the same way as `.clinerules/` files and can be toggled on/off independently.
|
||||
|
||||
## Best Practices
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Keep Rules Focused" icon="bullseye">
|
||||
Each rule file should focus on one topic:
|
||||
- ✅ `typescript-conventions.md`
|
||||
- ✅ `react-component-structure.md`
|
||||
- ❌ `everything-about-our-codebase.md`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Be Specific, Not Generic" icon="crosshairs">
|
||||
Base rules on actual team preferences, not assumptions:
|
||||
- ✅ "We use React Query for server state management"
|
||||
- ❌ "Use best practices for state management"
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Update Rules as Projects Evolve" icon="rotate">
|
||||
Review and update rules periodically:
|
||||
- When adopting new technologies
|
||||
- After major architectural changes
|
||||
- When team conventions evolve
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Don't Overdo It" icon="gauge-simple-high">
|
||||
Too many rules can overwhelm Cline's context:
|
||||
- Start with 3-5 essential rules
|
||||
- Add more only when truly needed
|
||||
- Remove outdated rules promptly
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Example Rule Files
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="API Design Standards" icon="code">
|
||||
```markdown
|
||||
# API Design Standards
|
||||
|
||||
## REST Conventions
|
||||
- Use plural nouns for endpoints (`/users`, not `/user`)
|
||||
- Use HTTP methods semantically (GET, POST, PUT, DELETE)
|
||||
- Return appropriate status codes
|
||||
|
||||
## Response Format
|
||||
\`\`\`typescript
|
||||
{
|
||||
data: T,
|
||||
error?: string,
|
||||
metadata?: {
|
||||
page: number,
|
||||
total: number
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Error Handling
|
||||
- Always return error messages in `error` field
|
||||
- Use 4xx for client errors, 5xx for server errors
|
||||
- Include request ID in error responses
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Testing Requirements" icon="vial">
|
||||
```markdown
|
||||
# Testing Requirements
|
||||
|
||||
## Test Organization
|
||||
- Place tests next to source files (`Button.test.tsx`)
|
||||
- Use `describe` blocks to group related tests
|
||||
- Write descriptive test names
|
||||
|
||||
## Coverage Requirements
|
||||
- Unit tests for all utility functions
|
||||
- Integration tests for API endpoints
|
||||
- E2E tests for critical user flows
|
||||
- Minimum 80% coverage for new code
|
||||
|
||||
## Mocking Strategy
|
||||
- Mock external API calls
|
||||
- Use test fixtures for complex data
|
||||
- Prefer dependency injection for testability
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Workflows" icon="diagram-project" href="/enterprise-solutions/configuration/infrastructure-configuration/workflows">
|
||||
Combine rules with automated workflows
|
||||
</Card>
|
||||
|
||||
<Card title="Remote Configuration" icon="cloud" href="/enterprise-solutions/configuration/remote-configuration/overview">
|
||||
Deploy global rules for your team
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,324 @@
|
||||
---
|
||||
title: "Workflows"
|
||||
sidebarTitle: "Workflows"
|
||||
description: "Reusable instruction sets that can be invoked on-demand via slash commands"
|
||||
---
|
||||
|
||||
Workflows are markdown files containing reusable instructions that you can invoke on-demand using slash commands. Think of them as "rules you can call when needed" rather than always-active guidelines.
|
||||
|
||||
## What are Workflows?
|
||||
|
||||
Workflows are similar to [Rules](/enterprise-solutions/configuration/infrastructure-configuration/rules), but with one key difference:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Rules" icon="book">
|
||||
**Always Active**
|
||||
|
||||
Automatically applied to every task when toggled on
|
||||
|
||||
Example: Coding standards, style guides
|
||||
</Card>
|
||||
|
||||
<Card title="Workflows" icon="diagram-project">
|
||||
**On-Demand**
|
||||
|
||||
Invoked only when you use the slash command
|
||||
|
||||
Example: Deployment checklists, review processes
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Tip>
|
||||
Workflows are just markdown files, no complex configuration needed!
|
||||
</Tip>
|
||||
|
||||
## Quick Example
|
||||
|
||||
Here's a simple deployment workflow:
|
||||
|
||||
**File:** `.clinerules/workflows/deploy.md`
|
||||
|
||||
```markdown
|
||||
# Deployment Workflow
|
||||
|
||||
Before deploying to production, ensure:
|
||||
|
||||
## Pre-Deployment Checklist
|
||||
1. All tests passing (unit, integration, e2e)
|
||||
2. Code review approved by 2+ engineers
|
||||
3. Staging environment tested successfully
|
||||
4. Database migrations reviewed
|
||||
5. Rollback plan documented
|
||||
|
||||
## Deployment Steps
|
||||
1. Create deployment branch from main
|
||||
2. Run final test suite
|
||||
3. Deploy to production
|
||||
4. Monitor error rates for 30 minutes
|
||||
5. Verify key user flows
|
||||
|
||||
## Post-Deployment
|
||||
1. Update deployment log
|
||||
2. Notify team in #deployments channel
|
||||
3. Monitor metrics for 24 hours
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```
|
||||
/deploy
|
||||
|
||||
I'm ready to deploy the new authentication feature
|
||||
```
|
||||
|
||||
When invoked, Cline adds the workflow instructions to its context for that specific task.
|
||||
|
||||
## Creating Workflows
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Manual Creation">
|
||||
Create workflow files in the `.clinerules/workflows/` directory:
|
||||
|
||||
1. Create `.clinerules/workflows/` in your repository root
|
||||
2. Add markdown files with your workflow instructions
|
||||
3. Use descriptive names matching your slash command
|
||||
|
||||
**File structure:**
|
||||
```
|
||||
your-repo/
|
||||
├── .clinerules/
|
||||
│ └── workflows/
|
||||
│ ├── deploy.md
|
||||
│ ├── code-review.md
|
||||
│ └── bug-triage.md
|
||||
└── src/
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Slash Command">
|
||||
You can also create workflows during a conversation:
|
||||
|
||||
1. Have a conversation about a process you want to codify
|
||||
2. Type `/newrule` and specify it should be a workflow
|
||||
3. Cline creates the workflow file in `.clinerules/workflows/`
|
||||
|
||||
<Note>
|
||||
The `/newrule` command can create both rules and workflows - just specify your intent clearly.
|
||||
</Note>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Using Workflows
|
||||
|
||||
### Invoking Workflows
|
||||
|
||||
Simply type `/` followed by the workflow filename (without `.md`):
|
||||
|
||||
```
|
||||
/deploy
|
||||
/code-review
|
||||
/bug-triage
|
||||
```
|
||||
|
||||
The workflow instructions are added to Cline's context for the current task only.
|
||||
|
||||
### Workflow Naming
|
||||
|
||||
- Use lowercase with hyphens: `deploy.md`, `code-review.md`
|
||||
- Keep names short and memorable
|
||||
- Name should indicate the workflow's purpose
|
||||
|
||||
<Warning>
|
||||
Workflow filenames become slash commands, so choose names that are easy to type and remember.
|
||||
</Warning>
|
||||
|
||||
## Global vs Workspace Workflows
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Workspace Workflows" icon="folder">
|
||||
**Location:** `.clinerules/workflows/` in your repository
|
||||
|
||||
**Scope:** Specific to that project
|
||||
|
||||
**Use for:** Project-specific processes and checklists
|
||||
</Card>
|
||||
|
||||
<Card title="Global Workflows" icon="globe">
|
||||
**Location:** `Documents/Cline/Workflows/` directory
|
||||
|
||||
**Scope:** All your projects
|
||||
|
||||
**Use for:** Personal workflows that apply everywhere
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Info>
|
||||
**Precedence:** Local workflows override global workflows if they have the same name.
|
||||
</Info>
|
||||
|
||||
## Managing Workflows
|
||||
|
||||
### Toggling Workflows
|
||||
|
||||
You can enable or disable workflows:
|
||||
|
||||
1. Click the rules icon in Cline's interface
|
||||
2. Switch to the "Workflows" tab
|
||||
3. Toggle workflows on/off as needed
|
||||
|
||||
<Note>
|
||||
Disabling a workflow prevents it from being invoked, but keeps the file intact. The slash command won't work until you re-enable it.
|
||||
</Note>
|
||||
|
||||
### Enterprise Remote Workflows
|
||||
|
||||
<Info>
|
||||
Enterprise deployments can configure **remote global workflows** that are available to all team members. These are managed through your infrastructure configuration.
|
||||
|
||||
See [Self-Hosted Configuration](/enterprise-solutions/configuration/infrastructure-configuration/overview) for details on remote workflows.
|
||||
</Info>
|
||||
|
||||
## Example Workflows
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Code Review Workflow" icon="code-review">
|
||||
```markdown
|
||||
# Code Review Workflow
|
||||
|
||||
## Pre-Review Checklist
|
||||
- [ ] Code follows project style guide
|
||||
- [ ] All tests pass locally
|
||||
- [ ] No console.log or debugging code
|
||||
- [ ] Comments explain "why" not "what"
|
||||
- [ ] PR description is clear and complete
|
||||
|
||||
## Review Focus Areas
|
||||
1. **Architecture**: Does this fit our existing patterns?
|
||||
2. **Security**: Any potential vulnerabilities?
|
||||
3. **Performance**: Any obvious bottlenecks?
|
||||
4. **Testing**: Are edge cases covered?
|
||||
5. **Documentation**: Is it clear how to use new features?
|
||||
|
||||
## Review Response
|
||||
- Address all feedback within 24 hours
|
||||
- Mark conversations as resolved when addressed
|
||||
- Re-request review after major changes
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Bug Triage Workflow" icon="bug">
|
||||
```markdown
|
||||
# Bug Triage Workflow
|
||||
|
||||
## Information Gathering
|
||||
1. Reproduce the bug in local environment
|
||||
2. Identify affected versions/environments
|
||||
3. Check if similar issues exist
|
||||
4. Gather error logs and stack traces
|
||||
|
||||
## Priority Assessment
|
||||
**P0 (Critical)**: Production down, data loss, security breach
|
||||
**P1 (High)**: Major feature broken, significant user impact
|
||||
**P2 (Medium)**: Minor feature broken, workaround available
|
||||
**P3 (Low)**: Cosmetic issue, minimal impact
|
||||
|
||||
## Create Ticket
|
||||
- Use template: "Bug Report"
|
||||
- Add reproduction steps
|
||||
- Include screenshots/videos if applicable
|
||||
- Tag with affected component
|
||||
- Assign priority label
|
||||
|
||||
## Next Steps
|
||||
- P0/P1: Immediate fix required
|
||||
- P2: Schedule for current sprint
|
||||
- P3: Add to backlog
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Feature Planning Workflow" icon="lightbulb">
|
||||
```markdown
|
||||
# Feature Planning Workflow
|
||||
|
||||
## Requirements Gathering
|
||||
1. Define the user problem we're solving
|
||||
2. List success criteria (measurable)
|
||||
3. Identify edge cases and constraints
|
||||
4. Document technical dependencies
|
||||
|
||||
## Design Considerations
|
||||
1. How does this fit existing architecture?
|
||||
2. What data models are needed?
|
||||
3. What API changes are required?
|
||||
4. How will this impact performance?
|
||||
|
||||
## Implementation Plan
|
||||
1. Break into smaller, shippable pieces
|
||||
2. Identify which pieces can be done in parallel
|
||||
3. Note any feature flags needed
|
||||
4. Plan for backwards compatibility
|
||||
|
||||
## Testing Strategy
|
||||
1. What unit tests are needed?
|
||||
2. What integration tests are needed?
|
||||
3. How will we test edge cases?
|
||||
4. What manual testing is required?
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Best Practices
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Keep Workflows Action-Oriented" icon="list-check">
|
||||
Workflows should contain **actionable steps**, not general advice:
|
||||
- ✅ "Run `npm test` and verify all tests pass"
|
||||
- ❌ "Make sure testing is done properly"
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Use Checklists" icon="square-check">
|
||||
Format workflows as checklists when possible:
|
||||
- Easy to follow step-by-step
|
||||
- Clear progress tracking
|
||||
- Reduces missed steps
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Include Context" icon="circle-info">
|
||||
Add **why** behind each step:
|
||||
```markdown
|
||||
1. Check staging environment first
|
||||
(Catching issues in staging prevents production incidents)
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Version as Code" icon="code-branch">
|
||||
Workflows live in your repository:
|
||||
- Track changes in git
|
||||
- Review updates in PRs
|
||||
- Maintain history of process evolution
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Workflows vs Rules: When to Use Each
|
||||
|
||||
| Use Rules When | Use Workflows When |
|
||||
|----------------|-------------------|
|
||||
| Guidance should apply to every task | Process is invoked occasionally |
|
||||
| Standards that rarely change | Checklist for specific scenarios |
|
||||
| Always-on coding conventions | On-demand deployment processes |
|
||||
| General coding style | Specific review procedures |
|
||||
|
||||
**Example:**
|
||||
- **Rule**: "Use TypeScript strict mode and explicit return types"
|
||||
- **Workflow**: "Follow these 10 steps when deploying to production"
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Rules" icon="book" href="/enterprise-solutions/configuration/infrastructure-configuration/rules">
|
||||
Learn about always-active rules
|
||||
</Card>
|
||||
|
||||
<Card title="Remote Configuration" icon="cloud" href="/enterprise-solutions/configuration/remote-configuration/overview">
|
||||
Deploy global workflows for your team
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: "Configuration Overview"
|
||||
sidebarTitle: "Overview"
|
||||
description: "Understanding enterprise configuration options for inference providers and system settings"
|
||||
---
|
||||
|
||||
Cline offers two distinct approaches to configure inference providers and system settings for your organization. Understanding the difference between these approaches will help you choose the right configuration method for your needs.
|
||||
|
||||
## Configuration Types
|
||||
|
||||
<Info>
|
||||
**Need help choosing?** See the [Deployment Guide](/enterprise-solutions/configuration/choosing-your-deployment) for a detailed comparison and decision tree.
|
||||
</Info>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="SaaS Provider Configuration" icon="cloud" href="/enterprise-solutions/configuration/remote-configuration/overview">
|
||||
**Simple cloud-based setup**
|
||||
|
||||
Configure inference providers through the Cline [admin console](https://app.cline.bot/dashboard). Ideal for quick organizational deployment with minimal infrastructure requirements.
|
||||
</Card>
|
||||
|
||||
<Card title="Self-Hosted Configuration" icon="server" href="/enterprise-solutions/configuration/infrastructure-configuration/overview">
|
||||
**Advanced enterprise setup**
|
||||
|
||||
Deep infrastructure integration with VPC endpoints, multi-account support, compliance features, and custom workflows on your own infrastructure.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Choosing the Right Configuration
|
||||
|
||||
### Use SaaS Configuration When:
|
||||
- **Quick Setup**: You need to get your team up and running quickly
|
||||
- **Centralized Management**: You want simple, cloud-based provider management
|
||||
- **Standard Requirements**: Your organization has typical security and compliance needs
|
||||
- **Small to Medium Teams**: You're managing dozens to hundreds of users
|
||||
|
||||
### Use Self-Hosted Configuration When:
|
||||
- **Enterprise Security**: You need advanced security features and compliance controls
|
||||
- **Complex Infrastructure**: You have existing AWS/GCP infrastructure to integrate with
|
||||
- **Custom Workflows**: You need custom rules, workflows, and automation
|
||||
- **Large Organizations**: You're managing hundreds to thousands of users
|
||||
- **Air-Gapped Environments**: You need on-premises or restricted network deployment
|
||||
|
||||
## Configuration Comparison
|
||||
|
||||
| Feature | SaaS Configuration | Self-Hosted Configuration |
|
||||
|---------|-------------------|---------------------------|
|
||||
| **Setup Complexity** | Simple | Advanced |
|
||||
| **Deployment Time** | Minutes | Days to Weeks |
|
||||
| **Infrastructure Required** | None | AWS/GCP/Azure |
|
||||
| **Compliance Features** | Basic | Advanced |
|
||||
| **Custom Rules** | No | Yes |
|
||||
| **Multi-Account Support** | No | Yes |
|
||||
| **VPC Integration** | No | Yes |
|
||||
| **Cost** | Lower | Higher |
|
||||
|
||||
## Getting Started
|
||||
|
||||
<Steps>
|
||||
<Step title="Evaluate Your Requirements">
|
||||
Review your organization's security, compliance, and infrastructure requirements to determine which configuration approach fits your needs.
|
||||
</Step>
|
||||
|
||||
<Step title="Choose Your Path">
|
||||
Select either SaaS Configuration for simple setup or Self-Hosted Configuration for advanced enterprise features. Use the [Deployment Guide](/enterprise-solutions/configuration/choosing-your-deployment) if you need help deciding.
|
||||
</Step>
|
||||
|
||||
<Step title="Follow Configuration Guide">
|
||||
Complete the setup process using the detailed guides for your chosen configuration type.
|
||||
</Step>
|
||||
|
||||
<Step title="Onboard Team Members">
|
||||
Once configured, team members can connect using the provider-specific member guides.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Available Providers
|
||||
|
||||
Both configuration approaches support the same core inference providers:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="AWS Bedrock" icon="aws">
|
||||
Enterprise AI models with AWS infrastructure integration and security features.
|
||||
</Card>
|
||||
|
||||
<Card title="LiteLLM" icon="layer-group">
|
||||
Unified proxy for accessing 100+ AI models through a single interface.
|
||||
</Card>
|
||||
|
||||
<Card title="Google Vertex AI" icon="google">
|
||||
Google Cloud's AI platform with advanced ML capabilities and global infrastructure.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
The main difference lies in how these providers are configured and managed within your organization's infrastructure and security requirements.
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: "Configure AWS Bedrock Provider (Admin)"
|
||||
sidebarTitle: "Configure AWS Bedrock (Admin)"
|
||||
description: "This guide explains how administrators configure AWS Bedrock as the organization-wide LLM provider for Cline."
|
||||
---
|
||||
|
||||
|
||||
As an administrator, you can add AWS Bedrock as the organization-wide LLM provider for all Cline users through the hosted admin console. This centralized approach ensures consistent access to Amazon's AI models while maintaining your organization's security and compliance requirements through region controls and basic configuration options.
|
||||
|
||||
## Before You Begin
|
||||
|
||||
To get started with setting up AWS Bedrock as your organization's LLM provider, you'll need a few items in place.
|
||||
|
||||
**Administrator access to the Cline Admin console**
|
||||
You need admin privileges to enforce provider settings across your organization. If you can navigate to **Settings → Cline Settings** in the admin console at [app.cline.bot](https://app.cline.bot), you have the right access level.
|
||||
|
||||
|
||||
**AWS Bedrock account with the right permissions**
|
||||
Your AWS account needs specific Bedrock permissions to work with Cline.
|
||||
|
||||
<Note>
|
||||
If you don't have direct AWS access, coordinate with your cloud team to get these permissions set up before proceeding.
|
||||
</Note>
|
||||
|
||||
**Your preferred AWS region**
|
||||
Choose your primary AWS region carefully since this will be enforced for all users.
|
||||
|
||||
<Tip>
|
||||
Check which models are available in your region first. Some newer models might not be available in all regions yet.
|
||||
</Tip>
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="https://assets.int.cline.bot/assets/AWS%20Remote%20Config.gif"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## Configuration Steps
|
||||
|
||||
<Steps>
|
||||
<Step title="Access Cline Settings">
|
||||
Navigate to [app.cline.bot](https://app.cline.bot) and sign in with your administrator account. Go to **Settings → Cline Settings**.
|
||||
|
||||
<Info>
|
||||
You should see the provider configuration options if you have the correct admin access level.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Enable Remote Provider Configuration">
|
||||
Toggle on **Enable settings** to reveal the remote provider configuration options. This allows you to enforce provider settings across your organization.
|
||||
</Step>
|
||||
|
||||
<Step title="Select AWS Bedrock as the API Provider">
|
||||
Open the **API Provider** dropdown menu and select **Amazon Bedrock**. This will open the Bedrock configuration panel where you'll configure all your organization-wide settings.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Bedrock Settings">
|
||||
The configuration panel includes several settings that control how Bedrock works for your organization. Configure what you need:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Region (required)">
|
||||
Enter your preferred AWS region like `us-west-2` or `us-east-1`. This region will be enforced for all organization members.
|
||||
|
||||
[View AWS Global Infrastructure](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/)
|
||||
|
||||
<Tip>
|
||||
For most organizations, `us-east-1` or `us-west-2` are recommended as they have the best model availability.
|
||||
</Tip>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Custom VPC Endpoint (optional)">
|
||||
If your organization uses a private VPC endpoint for Bedrock, specify it here to ensure all API calls go through your network infrastructure.
|
||||
|
||||
[Learn more about AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-overview.html)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Cross-region Inference (optional)">
|
||||
Enable this to let Bedrock automatically route requests to other regions when your primary region has capacity constraints. Useful for maintaining availability during high-demand periods.
|
||||
|
||||
[Learn more about Inference Profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Global Inference Profile (optional)">
|
||||
Turn this on to use AWS's global inference routing, which automatically directs requests to the optimal region based on availability and latency.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Prompt Caching (optional)">
|
||||
Enable prompt caching to reduce costs and latency. Bedrock caches portions of prompts that remain consistent across requests, making repeated interactions faster and cheaper.
|
||||
|
||||
[Learn more about Prompt Caching](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</Step>
|
||||
|
||||
<Step title="Save Configuration">
|
||||
After configuring your settings, close the provider configuration panel and click **Save** on the settings page to persist your changes.
|
||||
|
||||
Once saved, all organization members signed into the Cline extension will automatically use AWS Bedrock with your configured settings. They won't be able to select other providers or switch to their personal Cline accounts.
|
||||
|
||||
<Warning>
|
||||
Members can't switch to personal Cline accounts or join other organizations once remote configuration is enabled. This ensures consistent provider usage across your team.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Verification
|
||||
|
||||
To verify the configuration:
|
||||
|
||||
1. Check that the provider shows as "Amazon Bedrock" in the Enabled provider field
|
||||
2. Confirm the settings persist after refreshing the page
|
||||
3. Test with a member account to ensure they see only Bedrock as a provider
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Members don't see the configured provider**
|
||||
Ensure you clicked Save after closing the configuration panel. Verify the member account belongs to the correct organization.
|
||||
|
||||
**Configuration changes don't persist**
|
||||
Make sure to click the Save button on the main settings page, not just close the configuration panel.
|
||||
|
||||
**Need to change regions later**
|
||||
You can update the region at any time. Members will need to ensure their local AWS credentials have access to the new region. For more information, refer to the [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html).
|
||||
|
||||
For further details, consult the [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) and coordinate with your internal cloud team.
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
title: "Configure AWS Bedrock in VS Code (Members)"
|
||||
sidebarTitle: "Configure AWS Bedrock (Member)"
|
||||
description: "Guide for engineers configuring AWS Bedrock credentials in VS Code after admin setup"
|
||||
---
|
||||
|
||||
As a team member, you can connect your local development environment to your organization's AWS Bedrock setup. This guide walks you through configuring your AWS credentials in VS Code so you can start using models through your organization's Bedrock infrastructure. Your administrator has already configured the provider settings—you just need to add your credentials to get started.
|
||||
|
||||
## Before You Begin
|
||||
|
||||
To successfully connect to your organization's AWS Bedrock setup, you'll need a few things ready.
|
||||
|
||||
**Cline extension installed and configured**
|
||||
The Cline extension must be installed in VS Code and you need to be signed into your organization account. If you haven't installed Cline yet, follow our [installation guide](/getting-started/installing-cline).
|
||||
|
||||
<Info>
|
||||
**Quick Check**: Open the Cline panel in VS Code. If you see your organization name in the bottom left, you're signed in correctly.
|
||||
</Info>
|
||||
|
||||
**AWS credentials with Bedrock access**
|
||||
You need AWS credentials that have permission to access Bedrock in your organization's configured region.
|
||||
|
||||
<Note>
|
||||
If you don't have AWS credentials yet, reach out to your IT or cloud team to get access keys or AWS CLI profiles configured with the necessary Bedrock permissions.
|
||||
</Note>
|
||||
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="https://assets.int.cline.bot/assets/VS%20Code%20Bedrock%20API%20Key.gif"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## Configuration Steps
|
||||
|
||||
<Steps>
|
||||
<Step title="Open Cline Settings">
|
||||
Open VS Code and access the Cline settings panel using either of these methods:
|
||||
|
||||
- Click the settings icon (⚙️) in the Cline panel
|
||||
- Click on the API Provider dropdown located directly below the chat area (it will display as `bedrock.anthropic.claude-sonnet-4-20250514-v1:0` or similar)
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Select Your Authentication Method">
|
||||
Choose one of the following credential methods to authenticate with AWS Bedrock:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="AWS Bedrock API Key">
|
||||
Use dedicated AWS access keys specifically for Bedrock access.
|
||||
|
||||
[Learn more about AWS Bedrock API Keys](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html)
|
||||
|
||||
1. Select the **API Key** radio button
|
||||
2. Enter your AWS Access Key ID and Secret Access Key
|
||||
3. These credentials are stored locally and used only by the VS Code extension
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="AWS Profile">
|
||||
Use an existing AWS CLI profile configured on your machine.
|
||||
|
||||
[Learn more about AWS CLI Profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html)
|
||||
|
||||
1. Select the **AWS Profile** radio button
|
||||
2. Choose or enter the profile name from your `~/.aws/credentials` file
|
||||
3. Cline will use the credentials associated with that profile
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="AWS Credentials">
|
||||
Use your default AWS credential chain (environment variables, EC2 instance roles, etc.).
|
||||
|
||||
1. Select the **AWS Credentials** radio button
|
||||
2. Cline will automatically detect credentials from your environment using the standard AWS credential provider chain
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Note>
|
||||
The AWS Region is preconfigured by your administrator and does not need to be set in the extension.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Verify Configuration">
|
||||
After selecting your authentication method, the extension will display checkmarks for enabled features:
|
||||
|
||||
- ✓ Supports images
|
||||
- ✓ Supports browser use
|
||||
- ✓ Supports prompt caching
|
||||
|
||||
Additional settings like cross-region inference and global inference profile will be locked (shown with a lock icon 🔒) as they're controlled by your administrator.
|
||||
</Step>
|
||||
|
||||
<Step title="Test the Connection">
|
||||
Send a test message in Cline to verify your credentials work correctly with the configured Bedrock region.
|
||||
|
||||
<Tip>
|
||||
**Testing Recommendation**
|
||||
|
||||
It is recommended to test the connection in plan mode to verify everything works correctly before using it for actual tasks.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Authentication errors ("Access Denied" or "Invalid Credentials")**
|
||||
Verify your chosen credential method has the necessary IAM permissions to call Bedrock in the configured region. Required permissions include `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream`. For more information, refer to [AWS Bedrock IAM Permissions](https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html).
|
||||
|
||||
**Region-related errors or "model not available"**
|
||||
Ask your administrator to confirm which region is configured for your organization. Ensure your AWS credentials have access to Bedrock in that specific region. [View AWS Global Infrastructure](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/)
|
||||
|
||||
**Don't see AWS Bedrock as an option**
|
||||
Confirm you're signed into the correct Cline organization. Verify your administrator has saved the Bedrock configuration. Try signing out and back into the extension.
|
||||
|
||||
**AWS Credentials option not finding credentials**
|
||||
Verify AWS CLI is installed and configured with `aws configure` ([AWS CLI Installation Guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)). Check that credentials are present in `~/.aws/credentials`. For EC2/ECS environments, ensure IAM roles are properly attached. If using environment variables, set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
|
||||
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
When configuring your AWS credentials, follow these security guidelines:
|
||||
|
||||
- Use IAM roles with minimum required permissions ([AWS IAM Best Practices](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html))
|
||||
- Rotate access keys regularly if using the API Key method
|
||||
- Never store credentials in code or version control
|
||||
- Prefer AWS Profile method for better credential management
|
||||
- Consider using AWS SSO/federated roles for enhanced security
|
||||
|
||||
Your organization administrator controls which models are available. The extension will automatically display available models based on your region's Bedrock configuration. For more information about available models, refer to the [AWS Bedrock Model Access documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html).
|
||||
|
||||
For further assistance, consult the [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) and coordinate with your organization's cloud administrator.
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: "Configure Google Vertex AI Provider (Admin)"
|
||||
sidebarTitle: "Configure Google Vertex (Admin)"
|
||||
description: "This guide explains how administrators configure Google Vertex AI as the organization-wide LLM provider for Cline."
|
||||
---
|
||||
|
||||
|
||||
As an administrator, you can add Google Vertex AI as the organization-wide LLM provider for all Cline users through the hosted admin console. This centralized approach ensures consistent access to Google's Gemini models while maintaining your organization's project boundaries and regional settings.
|
||||
|
||||
## Before You Begin
|
||||
|
||||
To get started with setting up Google Vertex AI as your organization's LLM provider, you'll need a few items in place.
|
||||
|
||||
**Administrator access to the Cline Admin console**
|
||||
You need admin privileges to enforce provider settings across your organization. If you can navigate to **Settings → Cline Settings** in the admin console at [app.cline.bot](https://app.cline.bot), you have the right access level.
|
||||
|
||||
|
||||
**Google Cloud Project with Vertex AI enabled**
|
||||
You need a Google Cloud project with the Vertex AI API enabled and appropriate models accessible.
|
||||
|
||||
<Note>
|
||||
If you haven't set up Google Cloud or Vertex AI yet, work with your cloud team to enable the Vertex AI API and ensure necessary quotas are configured.
|
||||
</Note>
|
||||
|
||||
**Project configuration details**
|
||||
You'll need your Google Cloud project ID and preferred region for Vertex AI model access.
|
||||
|
||||
<Tip>
|
||||
Service accounts should have the minimum IAM permissions needed for Vertex AI access to follow security best practices.
|
||||
</Tip>
|
||||
|
||||
## Configuration Steps
|
||||
|
||||
<Steps>
|
||||
<Step title="Access Cline Settings">
|
||||
Navigate to [app.cline.bot](https://app.cline.bot) and sign in with your administrator account. Go to **Settings → Cline Settings**.
|
||||
|
||||
<Info>
|
||||
You should see the provider configuration options if you have the correct admin access level.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Enable Remote Provider Configuration">
|
||||
Toggle on **Enable settings** to reveal the remote provider configuration options. This allows you to enforce provider settings across your organization.
|
||||
</Step>
|
||||
|
||||
<Step title="Select Google Vertex AI as the API Provider">
|
||||
Open the **API Provider** dropdown menu and select **Google Vertex AI**. This will open the Vertex AI configuration panel where you'll configure all your organization-wide settings.
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Vertex AI Settings">
|
||||
The configuration panel includes settings that control how Vertex AI works for your organization:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Project ID (required)">
|
||||
Enter your Google Cloud project ID where Vertex AI is enabled. This project will be used for all AI model requests from your organization members.
|
||||
|
||||
<Tip>
|
||||
Use a dedicated project for AI workloads to better track usage and costs. Ensure the project has sufficient quotas for your team's expected usage.
|
||||
</Tip>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Region (required)">
|
||||
Select the Google Cloud region where your Vertex AI models should be accessed. Common options include `us-central1`, `us-east4`, or `europe-west4`.
|
||||
|
||||
[View Google Cloud Regions](https://cloud.google.com/docs/geography-and-regions)
|
||||
|
||||
<Note>
|
||||
Choose a region close to your team's location for optimal performance. Some models may not be available in all regions.
|
||||
</Note>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
</Step>
|
||||
|
||||
<Step title="Save Configuration">
|
||||
After configuring your settings, close the provider configuration panel and click **Save** on the settings page to persist your changes.
|
||||
|
||||
Once saved, all organization members signed into the Cline extension will automatically use Google Vertex AI with your configured settings. They won't be able to select other providers or switch to their personal Cline accounts.
|
||||
|
||||
<Warning>
|
||||
Members can't switch to personal Cline accounts or join other organizations once remote configuration is enabled. This ensures consistent provider usage across your team.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Verification
|
||||
|
||||
To verify the configuration:
|
||||
|
||||
1. Check that the provider shows as "Google Vertex AI" in the Enabled provider field
|
||||
2. Confirm the settings persist after refreshing the page
|
||||
3. Test with a member account to ensure they see only Vertex AI as a provider
|
||||
4. Verify that Gemini models are available in the model dropdown
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Members don't see the configured provider**
|
||||
Ensure you clicked Save after closing the configuration panel. Verify the member account belongs to the correct organization and that your Google Cloud project has Vertex AI API enabled.
|
||||
|
||||
**Project access errors**
|
||||
Verify the project ID is correct and that Vertex AI API is enabled. Check that the project has appropriate billing configured and hasn't exceeded quotas.
|
||||
|
||||
**Regional availability issues**
|
||||
Confirm the selected region supports the Gemini models you want to use. Some newer models may only be available in specific regions.
|
||||
|
||||
**Configuration changes don't persist**
|
||||
Make sure to click the Save button on the main settings page, not just close the configuration panel.
|
||||
|
||||
**Need to change project or region later**
|
||||
You can update these settings at any time. Members will need to ensure their local Google Cloud credentials have access to the new project/region.
|
||||
|
||||
For further details, consult the [Google Cloud Vertex AI Documentation](https://cloud.google.com/vertex-ai/docs) and coordinate with your internal cloud team.
|
||||