mirror of
https://github.com/cline/cline.git
synced 2026-09-01 23:19:18 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b8b0279e04 | |||
| 69fd18c5e4 |
@@ -1,211 +0,0 @@
|
||||
---
|
||||
name: create-pull-request
|
||||
description: Create a GitHub pull request following project conventions. Use when the user asks to create a PR, submit changes for review, or open a pull request. Handles commit analysis, branch management, PR template usage, and PR creation using the gh CLI tool.
|
||||
---
|
||||
|
||||
# Create Pull Request
|
||||
|
||||
This skill guides you through creating a well-structured GitHub pull request that follows project conventions and best practices.
|
||||
|
||||
## Prerequisites Check
|
||||
|
||||
Before proceeding, verify the following:
|
||||
|
||||
### 1. Check if `gh` CLI is installed
|
||||
|
||||
```bash
|
||||
gh --version
|
||||
```
|
||||
|
||||
If not installed, inform the user:
|
||||
> The GitHub CLI (`gh`) is required but not installed. Please install it:
|
||||
> - macOS: `brew install gh`
|
||||
> - Other: https://cli.github.com/
|
||||
|
||||
### 2. Check if authenticated with GitHub
|
||||
|
||||
```bash
|
||||
gh auth status
|
||||
```
|
||||
|
||||
If not authenticated, guide the user to run `gh auth login`.
|
||||
|
||||
### 3. Verify clean working directory
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
If there are uncommitted changes, ask the user whether to:
|
||||
- Commit them as part of this PR
|
||||
- Stash them temporarily
|
||||
- Discard them (with caution)
|
||||
|
||||
## Gather Context
|
||||
|
||||
### 1. Identify the current branch
|
||||
|
||||
```bash
|
||||
git branch --show-current
|
||||
```
|
||||
|
||||
Ensure you're not on `main` or `master`. If so, ask the user to create or switch to a feature branch.
|
||||
|
||||
### 2. Find the base branch
|
||||
|
||||
```bash
|
||||
git remote show origin | grep "HEAD branch"
|
||||
```
|
||||
|
||||
This is typically `main` or `master`.
|
||||
|
||||
### 3. Analyze recent commits relevant to this PR
|
||||
|
||||
```bash
|
||||
git log origin/main..HEAD --oneline --no-decorate
|
||||
```
|
||||
|
||||
Review these commits to understand:
|
||||
- What changes are being introduced
|
||||
- The scope of the PR (single feature/fix or multiple changes)
|
||||
- Whether commits should be squashed or reorganized
|
||||
|
||||
### 4. Review the diff
|
||||
|
||||
```bash
|
||||
git diff origin/main..HEAD --stat
|
||||
```
|
||||
|
||||
This shows which files changed and helps identify the type of change.
|
||||
|
||||
## Information Gathering
|
||||
|
||||
Before creating the PR, you need the following information. Check if it can be inferred from:
|
||||
- Commit messages
|
||||
- Branch name (e.g., `fix/issue-123`, `feature/new-login`)
|
||||
- Changed files and their content
|
||||
|
||||
If any critical information is missing, use `ask_followup_question` to ask the user:
|
||||
|
||||
### Required Information
|
||||
|
||||
1. **Related Issue Number**: Look for patterns like `#123`, `fixes #123`, or `closes #123` in commit messages
|
||||
2. **Description**: What problem does this solve? Why were these changes made?
|
||||
3. **Type of Change**: Bug fix, new feature, breaking change, refactor, cosmetic, documentation, or workflow
|
||||
4. **Test Procedure**: How was this tested? What could break?
|
||||
|
||||
### Example clarifying question
|
||||
|
||||
If the issue number is not found:
|
||||
> I couldn't find a related issue number in the commit messages or branch name. What GitHub issue does this PR address? (Enter the issue number, e.g., "123" or "N/A" for small fixes)
|
||||
|
||||
## Git Best Practices
|
||||
|
||||
Before creating the PR, consider these best practices:
|
||||
|
||||
### Commit Hygiene
|
||||
|
||||
1. **Atomic commits**: Each commit should represent a single logical change
|
||||
2. **Clear commit messages**: Follow conventional commit format when possible
|
||||
3. **No merge commits**: Prefer rebasing over merging to keep history clean
|
||||
|
||||
### Branch Management
|
||||
|
||||
1. **Rebase on latest main** (if needed):
|
||||
```bash
|
||||
git fetch origin
|
||||
git rebase origin/main
|
||||
```
|
||||
|
||||
2. **Squash if appropriate**: If there are many small "WIP" commits, consider interactive rebase:
|
||||
```bash
|
||||
git rebase -i origin/main
|
||||
```
|
||||
Only suggest this if commits appear messy and the user is comfortable with rebasing.
|
||||
|
||||
### Push Changes
|
||||
|
||||
Ensure all commits are pushed:
|
||||
```bash
|
||||
git push origin HEAD
|
||||
```
|
||||
|
||||
If the branch was rebased, you may need:
|
||||
```bash
|
||||
git push origin HEAD --force-with-lease
|
||||
```
|
||||
|
||||
## Create the Pull Request
|
||||
|
||||
**IMPORTANT**: Read and use the PR template at `.github/pull_request_template.md`. The PR body format must **strictly match** the template structure. Do not deviate from the template format.
|
||||
|
||||
When filling out the template:
|
||||
- Replace `#XXXX` with the actual issue number, or keep as `#XXXX` if no issue exists (for small fixes)
|
||||
- Fill in all sections with relevant information gathered from commits and context
|
||||
- Mark the appropriate "Type of Change" checkbox(es)
|
||||
- Complete the "Pre-flight Checklist" items that apply
|
||||
|
||||
### Create PR with gh CLI
|
||||
|
||||
**Use a temporary file for the PR body** to avoid shell escaping issues, newline problems, and other command-line flakiness:
|
||||
|
||||
1. Write the PR body to a temporary file:
|
||||
```
|
||||
/tmp/pr-body.md
|
||||
```
|
||||
|
||||
2. Create the PR using the file:
|
||||
```bash
|
||||
gh pr create --title "PR_TITLE" --body-file /tmp/pr-body.md --base main
|
||||
```
|
||||
|
||||
3. Clean up the temporary file:
|
||||
```bash
|
||||
rm /tmp/pr-body.md
|
||||
```
|
||||
|
||||
For draft PRs:
|
||||
```bash
|
||||
gh pr create --title "PR_TITLE" --body-file /tmp/pr-body.md --base main --draft
|
||||
```
|
||||
|
||||
**Why use a file?** Passing complex markdown with newlines, special characters, and checkboxes directly via `--body` is error-prone. The `--body-file` flag handles all content reliably.
|
||||
|
||||
## Post-Creation
|
||||
|
||||
After creating the PR:
|
||||
|
||||
1. **Display the PR URL** so the user can review it
|
||||
2. **Remind about CI checks**: Tests and linting will run automatically
|
||||
3. **Suggest next steps**:
|
||||
- Add reviewers if needed: `gh pr edit --add-reviewer USERNAME`
|
||||
- Add labels if needed: `gh pr edit --add-label "bug"`
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **No commits ahead of main**: The branch has no changes to submit
|
||||
- Ask if the user meant to work on a different branch
|
||||
|
||||
2. **Branch not pushed**: Remote doesn't have the branch
|
||||
- Push the branch first: `git push -u origin HEAD`
|
||||
|
||||
3. **PR already exists**: A PR for this branch already exists
|
||||
- Show the existing PR: `gh pr view`
|
||||
- Ask if they want to update it instead
|
||||
|
||||
4. **Merge conflicts**: Branch conflicts with base
|
||||
- Guide user through resolving conflicts or rebasing
|
||||
|
||||
## Summary Checklist
|
||||
|
||||
Before finalizing, ensure:
|
||||
- [ ] `gh` CLI is installed and authenticated
|
||||
- [ ] Working directory is clean
|
||||
- [ ] All commits are pushed
|
||||
- [ ] Branch is up-to-date with base branch
|
||||
- [ ] Related issue number is identified, or placeholder is used
|
||||
- [ ] PR description follows the template exactly
|
||||
- [ ] Appropriate type of change is selected
|
||||
- [ ] Pre-flight checklist items are addressed
|
||||
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Add the o4-mini model in the isOminiModel
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Allow option to collect events to send them in a bundle to avoid sending too many events
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
add open ai cache to ui
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
disable autocapture
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Moved rule file conversions
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
"access": "restricted",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
suggested model exists again
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
getRelativePaths protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
getOllamaModels protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Add FeatureFlagProvider service for the Node.js extension side
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Add confirmation dialog to Delete All History button
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
breakpoint just in system prompt for gemini for OR and cline provider
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
fetch cache details from generation endpoint
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Bump ollama from 0.5.13 to 0.5.15
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Add npm script for issue creation
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate more info section to new docs
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Extend ReasoningEffort to non-o3-mini reasoning models for all providers
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Re-enable tests in workflow
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
updated gemini caching for OR and cline provider
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
searchFiles protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Add ability to generate commit message via cline
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
updated OR/cline provider to automate gemini models caching
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
getLmStudioModels protobus migration
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
adding quote reply support
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Add enterprise section to new docs
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Make Previous Updates in the Announcement a dropdown
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate prompting section to new docs
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate running models locally section to new docs
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
timestamp visualization
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Allow the user to scroll when Cline is editing a file by disabling auto-scroll when the user scrolls up
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate MCP section to new docs
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate custom model config section to new docs
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
add ui for windsurf and cursor rules
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Batch selection and deletion of tasks in history
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Migrate getting-started section to new docs
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": minor
|
||||
---
|
||||
|
||||
Task Timeline
|
||||
@@ -0,0 +1,26 @@
|
||||
changesDir: .changes
|
||||
unreleasedDir: unreleased
|
||||
headerPath: header.tpl.md
|
||||
changelogPath: CHANGELOG.md
|
||||
versionExt: md
|
||||
versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}'
|
||||
kindFormat: "### {{.Kind}}"
|
||||
changeFormat: "* {{.Body}}"
|
||||
kinds:
|
||||
- label: Added
|
||||
auto: minor
|
||||
- label: Changed
|
||||
auto: major
|
||||
- label: Deprecated
|
||||
auto: minor
|
||||
- label: Removed
|
||||
auto: major
|
||||
- label: Fixed
|
||||
auto: patch
|
||||
- label: Security
|
||||
auto: patch
|
||||
newlines:
|
||||
afterChangelogHeader: 1
|
||||
beforeChangelogVersion: 1
|
||||
endOfVersion: 1
|
||||
envPrefix: CHANGIE_
|
||||
@@ -1 +0,0 @@
|
||||
../../.clinerules/workflows/hotfix-release.md
|
||||
@@ -1 +0,0 @@
|
||||
../../.clinerules/workflows/release.md
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Only run in Claude Code remote environments
|
||||
if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR"
|
||||
|
||||
echo "=== Claude Code for Web Setup ==="
|
||||
echo ""
|
||||
|
||||
# Install latest gh CLI tool
|
||||
echo "Installing GitHub CLI..."
|
||||
GH_VERSION=$(curl -s https://api.github.com/repos/cli/cli/releases/latest | grep '"tag_name"' | cut -d'"' -f4 | sed 's/^v//')
|
||||
curl -sL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o /tmp/gh.tar.gz
|
||||
tar -xzf /tmp/gh.tar.gz -C /tmp
|
||||
sudo mv "/tmp/gh_${GH_VERSION}_linux_amd64/bin/gh" /usr/local/bin/gh
|
||||
rm -rf /tmp/gh.tar.gz /tmp/gh_${GH_VERSION}_linux_amd64
|
||||
echo "Installed gh version: $(gh --version | head -1)"
|
||||
echo ""
|
||||
|
||||
# Check if GITHUB_TOKEN is set and configure gh
|
||||
if [ -n "${GITHUB_TOKEN:-}" ]; then
|
||||
echo "GITHUB_TOKEN is configured - gh CLI is ready to use"
|
||||
echo ""
|
||||
echo "You can use gh commands directly, for example:"
|
||||
echo " gh issue list --repo cline/cline --limit 5"
|
||||
echo " gh pr list --repo cline/cline --state open"
|
||||
echo " gh issue view 123 --repo cline/cline"
|
||||
echo ""
|
||||
else
|
||||
echo "GITHUB_TOKEN is not set - gh CLI will have limited functionality"
|
||||
echo ""
|
||||
echo "To enable full GitHub API access:"
|
||||
echo "1. Create a Fine-grained Personal Access Token at https://github.com/settings/tokens?type=beta"
|
||||
echo "2. Add it as GITHUB_TOKEN in your Claude Code environment settings"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Install project dependencies
|
||||
echo "Installing dependencies..."
|
||||
npm run install:all
|
||||
|
||||
# Generate gRPC/protobuf types (required for TypeScript)
|
||||
echo "Generating proto types..."
|
||||
npm run protos
|
||||
|
||||
echo ""
|
||||
echo "Session setup complete!"
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/claude-code-for-web-setup.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
# CLI Development
|
||||
|
||||
The CLI lives in `cli/` and uses React Ink for terminal UI.
|
||||
|
||||
- If needed, look at `cli/src/constants/colors.ts` for re-used terminal colors, e.g. `COLORS.primaryBlue` highlight color (selections, spinners, success states).
|
||||
- Never use `dimColor` with gray (e.g. `<Text color="gray" dimColor>`) - it's too hard to read. Use `color="gray"` for secondary text and normal foreground (no color) for primary text.
|
||||
- When thinking about how to handle state or messages from core, look at webview for how it communicates with the vs code extension.
|
||||
- When updating the webview, consider and suggest to the user to update the CLI TUI since we want to provide a similar experience to our terminal users as we do our vs code extension users.
|
||||
|
||||
## Adding New API Providers
|
||||
|
||||
When adding a new API provider to the extension, you must also update the CLI:
|
||||
|
||||
1. **Update `cli/src/components/ModelPicker.tsx`**: Add the provider to the `providerModels` map so `getDefaultModelId()` returns the correct default model. Import the models and default ID from `@shared/api`:
|
||||
```typescript
|
||||
import { newProviderDefaultModelId, newProviderModels } from "@/shared/api"
|
||||
|
||||
export const providerModels = {
|
||||
// ...existing providers
|
||||
"new-provider": { models: newProviderModels, defaultId: newProviderDefaultModelId },
|
||||
}
|
||||
```
|
||||
|
||||
2. **Use `applyProviderConfig()` for auth flows**: When implementing OAuth or other auth flows for the provider, use the shared utility at `cli/src/utils/provider-config.ts`:
|
||||
```typescript
|
||||
import { applyProviderConfig } from "../utils/provider-config"
|
||||
|
||||
// After successful auth:
|
||||
await applyProviderConfig({ providerId: "new-provider", controller })
|
||||
```
|
||||
This handles setting provider, default model, API key mapping, state persistence, and rebuilding the API handler.
|
||||
|
||||
3. **Provider-specific auth**: If the provider uses OAuth (like `openai-codex`), add handling in `SettingsPanelContent.tsx`'s `handleProviderSelect` callback. See the existing Codex OAuth flow as a reference.
|
||||
@@ -164,7 +164,6 @@ Key providers include:
|
||||
- **OpenRouter**: Meta-provider supporting multiple model providers
|
||||
- **AWS Bedrock**: Integration with Amazon's AI services
|
||||
- **Gemini**: Google's AI models
|
||||
- **Cerebras**: High-performance inference with Llama, Qwen, and DeepSeek models
|
||||
- **Ollama**: Local model hosting
|
||||
- **LM Studio**: Local model hosting
|
||||
- **VSCode LM**: VSCode's built-in language models
|
||||
@@ -210,7 +209,7 @@ class Task {
|
||||
switch (chunk.type) {
|
||||
case "text":
|
||||
// Parse into content blocks
|
||||
this.assistantMessageContent = parseAssistantMessageV2(chunk.text)
|
||||
this.assistantMessageContent = parseAssistantMessage(chunk.text)
|
||||
// Present blocks to user
|
||||
await this.presentAssistantMessage()
|
||||
break
|
||||
@@ -716,7 +715,7 @@ The Controller class manages MCP servers through the McpHub service:
|
||||
class Controller {
|
||||
mcpHub?: McpHub
|
||||
|
||||
constructor(context: vscode.ExtensionContext, webviewProvider: WebviewProvider) {
|
||||
constructor(context: vscode.ExtensionContext, outputChannel: vscode.OutputChannel, webviewProvider: WebviewProvider) {
|
||||
this.mcpHub = new McpHub(this)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
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, contributors should not create changelog-entry files. Maintainers handle release versioning and changelog curation during the release process.
|
||||
- When adding new feature flags, see this PR as a reference https://github.com/cline/cline/pull/7566
|
||||
- Additional instructions about making requests: @.clinerules/network.md
|
||||
|
||||
## 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 a New API Provider
|
||||
When adding a new provider (e.g., "openai-codex"), you must update the proto conversion layer in THREE places or the provider will silently reset to Anthropic:
|
||||
|
||||
1. `proto/cline/models.proto` - Add to the `ApiProvider` enum (e.g., `OPENAI_CODEX = 40;`)
|
||||
2. `convertApiProviderToProto()` in `src/shared/proto-conversions/models/api-configuration-conversion.ts` - Add case mapping string to proto enum
|
||||
3. `convertProtoToApiProvider()` in the same file - Add case mapping proto enum back to string
|
||||
|
||||
**Why this matters:** Without these, the provider string hits the `default` case and returns `ANTHROPIC`. The webview, provider list, and handler all work fine, but the state silently resets when it round-trips through proto serialization. No error is thrown.
|
||||
|
||||
**Other files to update when adding a provider:**
|
||||
- `src/shared/api.ts` - Add to `ApiProvider` union type, define models
|
||||
- `src/shared/providers/providers.json` - Add to provider list for dropdown
|
||||
- `src/core/api/index.ts` - Register handler in `createHandlerForProvider()`
|
||||
- `webview-ui/src/components/settings/utils/providerUtils.ts` - Add cases in `getModelsForProvider()` and `normalizeApiConfiguration()`
|
||||
- `webview-ui/src/utils/validate.ts` - Add validation case
|
||||
- `webview-ui/src/components/settings/ApiOptions.tsx` - Render provider component
|
||||
|
||||
## Responses API Providers (OpenAI Codex, OpenAI Native)
|
||||
Providers using OpenAI's Responses API require native tool calling. XML tools don't work with the Responses API.
|
||||
|
||||
**Symptoms of broken native tool calling:**
|
||||
- Tools get called multiple times (e.g., `ask_followup_question` asks the same question twice)
|
||||
- Tool arguments get duplicated or malformed
|
||||
- The model responds but tools aren't recognized
|
||||
|
||||
**Root causes to check:**
|
||||
1. **Provider missing from `isNextGenModelProvider()`** in `src/utils/model-utils.ts`. The native variant matchers (e.g., `native-gpt-5/config.ts`) call this function. If your provider isn't in the list, the matcher returns false and falls back to XML tools.
|
||||
|
||||
2. **Model missing `apiFormat: ApiFormat.OPENAI_RESPONSES`** in its model info (`src/shared/api.ts`). This property signals that the model requires native tool calling. The task runner in `src/core/task/index.ts` checks this and forces `enableNativeToolCalls: true` regardless of user settings.
|
||||
|
||||
**When adding a new Responses API provider:**
|
||||
1. Add provider to `isNextGenModelProvider()` list in `src/utils/model-utils.ts`
|
||||
2. Set `apiFormat: ApiFormat.OPENAI_RESPONSES` on all models that use the Responses API
|
||||
3. The variant matcher and task runner will handle the rest automatically
|
||||
|
||||
## 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
|
||||
|
||||
## Adding New Global State Keys
|
||||
Adding a new key to global state requires updates in multiple places. Missing any step causes silent failures.
|
||||
|
||||
Required steps:
|
||||
1. Type definition in `src/shared/storage/state-keys.ts` - Add to `GlobalState` or `Settings` interface
|
||||
2. Read from globalState in `src/core/storage/utils/state-helpers.ts`:
|
||||
- Add `const myKey = context.globalState.get<GlobalStateAndSettings["myKey"]>("myKey")` in `readGlobalStateFromDisk()`
|
||||
- Add to the return object: `myKey: myKey ?? defaultValue,`
|
||||
3. StateManager handles read/write via `setGlobalState()`/`getGlobalStateKey()` after initialization
|
||||
|
||||
Common mistake: Adding only the return value without the `context.globalState.get()` call. This compiles but the value is always `undefined` on load.
|
||||
|
||||
Settings plumbing gotcha: if a key is user-toggleable from settings, wire both controller update paths:
|
||||
- `src/core/controller/state/updateSettings.ts` for webview `updateSetting(...)`
|
||||
- `src/core/controller/state/updateSettingsCli.ts` for CLI/ACP settings updates
|
||||
Missing one path causes a toggle to appear to change in one surface while the backend state stays unchanged.
|
||||
|
||||
Webview toggle gotcha: settings changes must also round-trip back in state payloads.
|
||||
- Add the field to `UpdateSettingsRequest` in `proto/cline/state.proto` (for webview update requests), then run `npm run protos`
|
||||
- Include the key in `Controller.getStateToPostToWebview()` (`src/core/controller/index.ts`)
|
||||
- Ensure `ExtensionState` and webview defaults include the key (`src/shared/ExtensionMessage.ts`, `webview-ui/src/context/ExtensionStateContext.tsx`)
|
||||
If this round-trip wiring is missing, the backend value can update but the toggle in webview appears stuck or reverts.
|
||||
|
||||
## StateManager Cache vs Direct globalState Access
|
||||
StateManager uses an in-memory cache populated during `StateManager.initialize(context)` in `common.ts`. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
|
||||
|
||||
Exception: State needed immediately at extension startup (before cache is ready)
|
||||
|
||||
When Window A sets state and immediately opens Window B, the new window's StateManager cache is populated from `context.globalState` during initialization. If you need to read state in Window B right at startup (e.g., in `common.ts` during `initialize()`), read directly from `context.globalState.get()` instead of StateManager's cache.
|
||||
|
||||
Example pattern (see `lastShownAnnouncementId` and `worktreeAutoOpenPath`):
|
||||
```typescript
|
||||
// Writing (normal pattern)
|
||||
controller.stateManager.setGlobalState("myKey", value)
|
||||
|
||||
// Reading at startup in common.ts (bypass cache)
|
||||
const value = context.globalState.get<string>("myKey")
|
||||
```
|
||||
|
||||
This is only needed for cross-window state read during the brief startup window before StateManager cache is fully usable. Normal state access after initialization should use StateManager.
|
||||
|
||||
## 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.
|
||||
@@ -1,423 +0,0 @@
|
||||
# Cline Hooks Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
Cline hooks allow you to execute custom scripts at specific points in the agentic workflow. Hooks can be placed in either:
|
||||
- **Global hooks directory**: `~/Documents/Cline/Hooks/` (applies to all workspaces)
|
||||
- **Workspace hooks directory**: `.clinerules/hooks/` (applies to the workspace the repo is part of)
|
||||
|
||||
Hooks run automatically when enabled.
|
||||
|
||||
## Enabling Hooks
|
||||
|
||||
1. Open Cline settings in VSCode
|
||||
2. Navigate to the Feature Settings section
|
||||
3. Check the "Enable Hooks" checkbox
|
||||
4. Hooks must be executable files (on Unix/Linux/macOS use `chmod +x hookname`)
|
||||
|
||||
## Available Hooks
|
||||
|
||||
### TaskStart Hook
|
||||
- **When**: Runs when a NEW task is started (not when resuming)
|
||||
- **Purpose**: Initialize task context, validate task requirements, set up environment
|
||||
- **Global Location**: `~/Documents/Cline/Hooks/TaskStart`
|
||||
- **Workspace Location**: `.clinerules/hooks/TaskStart`
|
||||
|
||||
### TaskResume Hook
|
||||
- **When**: Runs when an EXISTING task is resumed (after user clicks resume button)
|
||||
- **Purpose**: Validate resumed task state, restore context, check for changes since last run
|
||||
- **Global Location**: `~/Documents/Cline/Hooks/TaskResume`
|
||||
- **Workspace Location**: `.clinerules/hooks/TaskResume`
|
||||
|
||||
### TaskCancel Hook
|
||||
- **When**: Runs when a task is cancelled or a hook is aborted by the user (only if there's actual active work or work was started)
|
||||
- **Purpose**: Clean up resources, log cancellation, save state
|
||||
- **Global Location**: `~/Documents/Cline/Hooks/TaskCancel`
|
||||
- **Workspace Location**: `.clinerules/hooks/TaskCancel`
|
||||
- **Note**: This hook is NOT cancellable
|
||||
|
||||
### TaskComplete Hook (coming soon!)
|
||||
- **When**: Runs when a task is marked as complete
|
||||
- **Purpose**: Log completion status, perform final cleanup, generate reports
|
||||
- **Global Location**: `~/Documents/Cline/Hooks/TaskComplete`
|
||||
- **Workspace Location**: `.clinerules/hooks/TaskComplete`
|
||||
|
||||
### UserPromptSubmit Hook
|
||||
- **When**: Runs when the user submits a prompt/message (initial task, resume, or feedback)
|
||||
- **Purpose**: Validate user input, preprocess prompts, add context to user messages
|
||||
- **Global Location**: `~/Documents/Cline/Hooks/UserPromptSubmit`
|
||||
- **Workspace Location**: `.clinerules/hooks/UserPromptSubmit`
|
||||
|
||||
### PreToolUse Hook
|
||||
- **When**: Runs BEFORE a tool is executed
|
||||
- **Purpose**: Validate parameters, block execution, or add context
|
||||
- **Global Location**: `~/Documents/Cline/Hooks/PreToolUse`
|
||||
- **Workspace Location**: `.clinerules/hooks/PreToolUse`
|
||||
|
||||
### PostToolUse Hook
|
||||
- **When**: Runs AFTER a tool completes
|
||||
- **Purpose**: Observe results, track patterns, or add context
|
||||
- **Global Location**: `~/Documents/Cline/Hooks/PostToolUse`
|
||||
- **Workspace Location**: `.clinerules/hooks/PostToolUse`
|
||||
|
||||
### PreCompact Hook (coming soon!)
|
||||
- **When**: Runs BEFORE the conversation context is compacted/truncated
|
||||
- **Purpose**: Observe compaction events, log context management, track token usage
|
||||
- **Global Location**: `~/Documents/Cline/Hooks/PreCompact`
|
||||
- **Workspace Location**: `.clinerules/hooks/PreCompact`
|
||||
|
||||
## Cross-Platform Hook Format
|
||||
|
||||
Cline uses a git-style approach for hooks that works consistently across all platforms:
|
||||
|
||||
### Hook Files (All Platforms)
|
||||
- **No file extensions**: Hooks are named exactly `PreToolUse` or `PostToolUse` (no `.bat`, `.cmd`, `.sh` etc.)
|
||||
- **Shebang required**: First line must be a shebang (e.g., `#!/usr/bin/env bash` or `#!/usr/bin/env node`)
|
||||
- **Executable on Unix**: On Unix/Linux/macOS, hooks must be executable: `chmod +x PreToolUse`
|
||||
- **Windows**: Not currently supported.
|
||||
|
||||
### How It Works
|
||||
|
||||
Like git hooks, Cline executes hook files through a shell that interprets the shebang line:
|
||||
- On Unix/Linux/macOS: Native shell execution with shebang support
|
||||
|
||||
This means:
|
||||
- ✅ Same hook script works on all platforms
|
||||
- ✅ Write once, run anywhere
|
||||
- ✅ Use any scripting language (bash, node, python, etc.)
|
||||
|
||||
### Creating Hooks
|
||||
|
||||
**On Unix/Linux/macOS:**
|
||||
```bash
|
||||
# Create hook file
|
||||
nano ~/Documents/Cline/Hooks/PreToolUse
|
||||
|
||||
# Make executable
|
||||
chmod +x ~/Documents/Cline/Hooks/PreToolUse
|
||||
```
|
||||
|
||||
## Context Injection Timing
|
||||
|
||||
**IMPORTANT**: Context injected by hooks affects **FUTURE AI decisions**, not the current tool execution.
|
||||
|
||||
### Why This Matters
|
||||
|
||||
When a hook runs:
|
||||
1. The AI has already decided what tool to use and with what parameters
|
||||
2. The hook cannot modify those parameters
|
||||
3. Context from the hook is added to the conversation
|
||||
4. The AI sees this context in the **NEXT API request** and can adjust future decisions
|
||||
|
||||
### PreToolUse Hook Flow
|
||||
```
|
||||
1. AI decides: "I'll use write_to_file with these parameters"
|
||||
2. PreToolUse hook runs → can block or add context
|
||||
3. If allowed, tool executes with original parameters
|
||||
4. Context is added to conversation
|
||||
5. Next API request includes this context
|
||||
6. AI adjusts future decisions based on context
|
||||
```
|
||||
|
||||
### PostToolUse Hook Flow
|
||||
```
|
||||
1. Tool completes execution
|
||||
2. PostToolUse hook runs → observes results
|
||||
3. Hook adds context about the outcome
|
||||
4. Context is added to conversation
|
||||
5. Next API request includes this context
|
||||
6. AI can learn from the results
|
||||
```
|
||||
|
||||
## Hook Input/Output
|
||||
|
||||
### Input (via stdin as JSON)
|
||||
|
||||
All hooks receive:
|
||||
```json
|
||||
{
|
||||
"clineVersion": "string",
|
||||
"hookName": "TaskStart" | "TaskResume" | "TaskCancel" | "TaskComplete" | "UserPromptSubmit" | "PreToolUse" | "PostToolUse" | "PreCompact",
|
||||
"timestamp": "string",
|
||||
"taskId": "string",
|
||||
"workspaceRoots": ["string"],
|
||||
"userId": "string",
|
||||
"taskStart": { // Only for TaskStart
|
||||
"taskMetadata": {
|
||||
"taskId": "string",
|
||||
"ulid": "string",
|
||||
"initialTask": "string"
|
||||
}
|
||||
},
|
||||
"taskResume": { // Only for TaskResume
|
||||
"taskMetadata": {
|
||||
"taskId": "string",
|
||||
"ulid": "string"
|
||||
},
|
||||
"previousState": {
|
||||
"lastMessageTs": "string",
|
||||
"messageCount": "string",
|
||||
"conversationHistoryDeleted": "string"
|
||||
}
|
||||
},
|
||||
"taskCancel": { // Only for TaskCancel
|
||||
"taskMetadata": {
|
||||
"taskId": "string",
|
||||
"ulid": "string",
|
||||
"completionStatus": "string"
|
||||
}
|
||||
},
|
||||
"taskComplete": { // Only for TaskComplete
|
||||
"taskMetadata": {
|
||||
"taskId": "string",
|
||||
"ulid": "string"
|
||||
}
|
||||
},
|
||||
"userPromptSubmit": { // Only for UserPromptSubmit
|
||||
"prompt": "string",
|
||||
"attachments": ["string"]
|
||||
},
|
||||
"preToolUse": { // Only for PreToolUse
|
||||
"toolName": "string",
|
||||
"parameters": {}
|
||||
},
|
||||
"postToolUse": { // Only for PostToolUse
|
||||
"toolName": "string",
|
||||
"parameters": {},
|
||||
"result": "string",
|
||||
"success": boolean,
|
||||
"executionTimeMs": number
|
||||
},
|
||||
"preCompact": { // Only for PreCompact
|
||||
"contextSize": number,
|
||||
"messagesToCompact": number,
|
||||
"compactionStrategy": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Output (via stdout as JSON)
|
||||
|
||||
All hooks must return:
|
||||
```json
|
||||
{
|
||||
"cancel": boolean, // Required: false to continue, true to block execution
|
||||
"contextModification": "string", // Optional: Context for future AI decisions
|
||||
"errorMessage": "string" // Optional: Error details if blocking
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: The `cancel` field works as follows:
|
||||
- `false` (or omitted): Allow execution to continue
|
||||
- `true`: Block execution and show error message to user
|
||||
|
||||
## Hook Execution Limits
|
||||
|
||||
- **Timeout**: Hooks must complete within 30 seconds (configurable via `HOOK_EXECUTION_TIMEOUT_MS`)
|
||||
- **Context Size**: Context modifications are limited to 50KB (configurable via `MAX_CONTEXT_MODIFICATION_SIZE`)
|
||||
- **Error Handling**: Expected errors (file not found, permission denied, not a directory) are handled silently; unexpected file system errors are propagated
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### 1. Validation - Block Invalid Operations
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
input=$(cat)
|
||||
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
|
||||
path=$(echo "$input" | jq -r '.preToolUse.parameters.path // ""')
|
||||
|
||||
if [[ "$tool_name" == "write_to_file" && "$path" == *.js ]]; then
|
||||
cat <<EOF
|
||||
{
|
||||
"cancel": true,
|
||||
"errorMessage": "Cannot create .js files in TypeScript project",
|
||||
"contextModification": "Use .ts/.tsx extensions only"
|
||||
}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo '{"cancel": false}'
|
||||
```
|
||||
|
||||
### 2. Context Building - Learn from Operations
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
input=$(cat)
|
||||
tool_name=$(echo "$input" | jq -r '.postToolUse.toolName')
|
||||
success=$(echo "$input" | jq -r '.postToolUse.success')
|
||||
path=$(echo "$input" | jq -r '.postToolUse.parameters.path // ""')
|
||||
|
||||
if [[ "$tool_name" == "write_to_file" && "$success" == "true" ]]; then
|
||||
cat <<EOF
|
||||
{
|
||||
"cancel": false,
|
||||
"contextModification": "Created '$path'. Maintain consistency with this file's patterns in future operations."
|
||||
}
|
||||
EOF
|
||||
else
|
||||
echo '{"cancel": false}'
|
||||
fi
|
||||
```
|
||||
|
||||
### 3. Performance Monitoring
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
input=$(cat)
|
||||
execution_time=$(echo "$input" | jq -r '.postToolUse.executionTimeMs')
|
||||
tool_name=$(echo "$input" | jq -r '.postToolUse.toolName')
|
||||
|
||||
if [[ "$execution_time" -gt 5000 ]]; then
|
||||
cat <<EOF
|
||||
{
|
||||
"cancel": false,
|
||||
"contextModification": "Tool '$tool_name' took ${execution_time}ms. Consider optimizing future similar operations."
|
||||
}
|
||||
EOF
|
||||
else
|
||||
echo '{"cancel": false}'
|
||||
fi
|
||||
```
|
||||
|
||||
### 4. Logging and Telemetry
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
input=$(cat)
|
||||
|
||||
# Log to file
|
||||
echo "$input" >> ~/.cline/hook-logs/tool-usage.jsonl
|
||||
|
||||
# Allow execution
|
||||
echo '{"cancel": false}'
|
||||
```
|
||||
|
||||
## Global vs Workspace Hooks
|
||||
|
||||
Cline supports two levels of hooks:
|
||||
|
||||
### Global Hooks
|
||||
- **Location**: `~/Documents/Cline/Hooks/` (macOS/Linux)
|
||||
- **Scope**: Apply to ALL workspaces and projects
|
||||
- **Use Case**: Organization-wide policies, personal preferences, universal validations
|
||||
- **Priority**: Order not guaranteed when combined with workspace hooks
|
||||
|
||||
### Workspace Hooks
|
||||
- **Location**: `.clinerules/hooks/` in each workspace root
|
||||
- **Scope**: Apply only to the specific workspace
|
||||
- **Use Case**: Project-specific rules, team conventions, repository requirements
|
||||
- **Priority**: Order not guaranteed when combined with global hooks
|
||||
|
||||
### Hook Execution
|
||||
|
||||
When multiple hooks exist (global and/or workspace):
|
||||
- All hooks for a given step are executed **concurrently** using `Promise.all`
|
||||
- **Execution order is not guaranteed** - hooks run in parallel
|
||||
- If ALL hooks allow execution (`cancel: false`), the tool proceeds
|
||||
- If ANY hook blocks (`cancel: true`), execution is blocked
|
||||
|
||||
**Result Combination:**
|
||||
- `cancel`: If ANY hook returns `true`, execution is blocked
|
||||
- `contextModification`: All context strings are concatenated with double newlines (`\n\n`)
|
||||
- `errorMessage`: All error messages are concatenated with single newlines (`\n`)
|
||||
|
||||
### Setting Up Global Hooks
|
||||
|
||||
1. The global hooks directory is automatically created at:
|
||||
- macOS/Linux: `~/Documents/Cline/Hooks/`
|
||||
|
||||
2. Add your hook script:
|
||||
```bash
|
||||
# Unix/Linux/macOS
|
||||
nano ~/Documents/Cline/Hooks/PreToolUse
|
||||
chmod +x ~/Documents/Cline/Hooks/PreToolUse
|
||||
```
|
||||
|
||||
3. Enable hooks in Cline settings
|
||||
|
||||
### Example: Global + Workspace Hooks
|
||||
|
||||
**Global Hook** (applies to all projects):
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# ~/Documents/Cline/Hooks/PreToolUse
|
||||
# Universal rule: Never delete package.json
|
||||
input=$(cat)
|
||||
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
|
||||
path=$(echo "$input" | jq -r '.preToolUse.parameters.path // ""')
|
||||
|
||||
if [[ "$tool_name" == "write_to_file" && "$path" == *"package.json"* ]]; then
|
||||
echo '{"cancel": true, "errorMessage": "Global policy: Cannot modify package.json"}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo '{"cancel": false}'
|
||||
```
|
||||
|
||||
**Workspace Hook** (applies to specific project):
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# .clinerules/hooks/PreToolUse
|
||||
# Project rule: Only TypeScript files
|
||||
input=$(cat)
|
||||
tool_name=$(echo "$input" | jq -r '.preToolUse.toolName')
|
||||
path=$(echo "$input" | jq -r '.preToolUse.parameters.path // ""')
|
||||
|
||||
if [[ "$tool_name" == "write_to_file" && "$path" == *.js ]]; then
|
||||
echo '{"cancel": true, "errorMessage": "Project rule: Use .ts files only"}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo '{"cancel": false}'
|
||||
```
|
||||
|
||||
**All hooks must allow execution for the tool to proceed.** Hooks may execute concurrently.
|
||||
|
||||
## Multi-Root Workspaces
|
||||
|
||||
If you have multiple workspace roots, you can place hooks in each root's `.clinerules/hooks/` directory. All hooks (global and workspace) may execute concurrently. Their results will be combined:
|
||||
|
||||
- **cancel**: If ANY hook returns `true`, execution is blocked
|
||||
- **contextModification**: All context modifications are concatenated
|
||||
- **errorMessage**: All error messages are concatenated
|
||||
|
||||
**Note:** No execution order is guaranteed between hooks from different directories.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Hook Not Running
|
||||
- Ensure the "Enable Hooks" setting is checked
|
||||
- Verify the hook file is executable (`chmod +x hookname`)
|
||||
- Check the hook file has no syntax errors
|
||||
- Look for errors in VSCode's Output panel (Cline channel)
|
||||
|
||||
### Hook Timing Out
|
||||
- Reduce complexity of the hook script
|
||||
- Avoid expensive operations (network calls, heavy computations)
|
||||
- Consider moving complex logic to a background process
|
||||
|
||||
### Context Not Affecting Behavior
|
||||
- Remember: context affects FUTURE decisions, not the current tool
|
||||
- Ensure context modifications are clear and actionable
|
||||
- Check that context isn't being truncated (50KB limit)
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Hooks run with the same permissions as VSCode
|
||||
- Be cautious with hooks from untrusted sources
|
||||
- Review hook scripts before enabling them
|
||||
- Consider using `.gitignore` to avoid committing sensitive hook logic
|
||||
- Hooks can access all workspace files and environment variables
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Keep hooks fast** - Aim for <100ms execution time
|
||||
2. **Make context actionable** - Be specific about what the AI should do
|
||||
3. **Use structured prefixes** - Help the AI categorize context
|
||||
4. **Handle errors gracefully** - Always return valid JSON
|
||||
5. **Log for debugging** - Keep logs of hook executions for troubleshooting
|
||||
6. **Test incrementally** - Start with simple hooks and add complexity
|
||||
7. **Document your hooks** - Add comments explaining the purpose and logic
|
||||
@@ -1,90 +0,0 @@
|
||||
# Networking & Proxy Support
|
||||
|
||||
To ensure Cline works correctly in all environments (VSCode, JetBrains, CLI) and with various network configurations (especially corporate proxies), strictly follow these guidelines for all network activity.
|
||||
|
||||
In extension code, do NOT use the global `fetch` or a default `axios` instance. (Note, `shared/net.ts` is exempt from these rules because it sets up the fetch wrappers.) In Webview code, you SHOULD use global `fetch`.
|
||||
|
||||
Global `fetch` and default `axios` do not automatically pick up proxy configurations in all environments (specifically JetBrains and CLI). You MUST use the provided utilities in `@/shared/net` which handle proxy agent configuration. In the webview, the browser/embedder handles proxies.
|
||||
|
||||
## Guidelines
|
||||
|
||||
### 1. Using `fetch`
|
||||
|
||||
Instead of `fetch(...)`, import the proxy-aware wrapper:
|
||||
|
||||
```typescript
|
||||
import { fetch } from '@/shared/net'
|
||||
|
||||
// Usage is identical to global fetch
|
||||
const response = await fetch('https://api.example.com/data')
|
||||
```
|
||||
|
||||
### 2. Using `axios`
|
||||
|
||||
When using `axios`, you must apply the settings from `getAxiosSettings()`:
|
||||
|
||||
```typescript
|
||||
import axios from 'axios'
|
||||
import { getAxiosSettings } from '@/shared/net'
|
||||
|
||||
const response = await axios.get('https://api.example.com/data', {
|
||||
headers: { 'Authorization': '...' },
|
||||
...getAxiosSettings() // <--- CRITICAL: Injects the proxy agent if needed
|
||||
})
|
||||
```
|
||||
|
||||
### 3. Third-Party Clients (OpenAI, Ollama, etc.)
|
||||
|
||||
Most API client libraries allow you to customize the `fetch` implementation. You **MUST** pass the proxy-aware `fetch` to these clients.
|
||||
|
||||
**Example (OpenAI):**
|
||||
```typescript
|
||||
import OpenAI from "openai"
|
||||
import { fetch } from "@/shared/net"
|
||||
|
||||
this.client = new OpenAI({
|
||||
apiKey: '...',
|
||||
fetch, // <--- CRITICAL: Pass our fetch wrapper
|
||||
})
|
||||
```
|
||||
|
||||
### 4. Tests
|
||||
|
||||
Use `mockFetchForTesting` to mock the underlying fetch implementation.
|
||||
|
||||
**Example (callback):**
|
||||
|
||||
```
|
||||
import { mockFetchForTesting } from "@/shared/net"
|
||||
|
||||
...
|
||||
let mockFetch = ...
|
||||
mockFetchForTesting(mockFetch, () => {
|
||||
// This calls mockFetch
|
||||
fetch('https://foo.example').then(...)
|
||||
})
|
||||
// Original fetch is restored immediately when the call returns.
|
||||
```
|
||||
|
||||
**Example (Promise):**
|
||||
|
||||
```
|
||||
import { mockFetchForTesting } from "@/shared/net"
|
||||
|
||||
...
|
||||
let mockFetch = ...
|
||||
await mockFetchForTesting(mockFetch, async () => {
|
||||
await ...
|
||||
// This calls mockFetch
|
||||
await fetch('https://foo.example')
|
||||
...
|
||||
})
|
||||
// Original fetch is restored when the Promise from the callback settles
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
If you are adding a new network call or integration:
|
||||
1. Check `@/shared/net.ts` is imported.
|
||||
2. Ensure `fetch` or `getAxiosSettings` is being used.
|
||||
3. Verify that third-party clients are configured to use the custom fetch.
|
||||
@@ -1,89 +0,0 @@
|
||||
# Cline Protobuf Development Guide
|
||||
|
||||
This guide outlines how to add new gRPC endpoints for communication between the webview (frontend) and the extension host (backend).
|
||||
|
||||
## Overview
|
||||
|
||||
Cline uses [Protobuf](https://protobuf.dev/) to define a strongly-typed API, ensuring efficient and type-safe communication. All definitions are in the `/proto` directory. The compiler and plugins are included as project dependencies, so no manual installation is needed.
|
||||
|
||||
## Key Concepts & Best Practices
|
||||
|
||||
- **File Structure**: Each feature domain should have its own `.proto` file (e.g., `account.proto`, `task.proto`).
|
||||
- **Message Design**:
|
||||
- For simple, single-value data, use the shared types in `proto/common.proto` (e.g., `StringRequest`, `Empty`, `Int64Request`). This promotes consistency.
|
||||
- For complex data structures, define custom messages within the feature's `.proto` file (see `task.proto` for examples like `NewTaskRequest`).
|
||||
- **Naming Conventions**:
|
||||
- Services: `PascalCaseService` (e.g., `AccountService`).
|
||||
- RPCs: `camelCase` (e.g., `accountEmailIdentified`).
|
||||
- Messages: `PascalCase` (e.g., `StringRequest`).
|
||||
- **Streaming**: For server-to-client streaming, use the `stream` keyword on the response type. See `subscribeToAuthCallback` in `account.proto` for an example.
|
||||
|
||||
---
|
||||
|
||||
## 4-Step Development Workflow
|
||||
|
||||
Here’s how to add a new RPC, using `scrollToSettings` as an example.
|
||||
|
||||
### 1. Define the RPC in a `.proto` File
|
||||
|
||||
Add your service method to the appropriate file in the `proto/` directory.
|
||||
|
||||
**File: `proto/ui.proto`**
|
||||
```proto
|
||||
service UiService {
|
||||
// ... other RPCs
|
||||
// Scrolls to a specific settings section in the settings view
|
||||
rpc scrollToSettings(StringRequest) returns (KeyValuePair);
|
||||
}
|
||||
```
|
||||
Here, we use the common `StringRequest` and `KeyValuePair` types.
|
||||
|
||||
### 2. Compile Definitions
|
||||
|
||||
After editing a `.proto` file, regenerate the TypeScript code. From the project root, run:
|
||||
```bash
|
||||
npm run protos
|
||||
```
|
||||
This command compiles all `.proto` files and outputs the generated code to `src/generated/` and `src/shared/`. Do not edit these generated files manually.
|
||||
|
||||
### 3. Implement the Backend Handler
|
||||
|
||||
Create the RPC implementation in the backend. Handlers are located in `src/core/controller/[service-name]/`.
|
||||
|
||||
**File: `src/core/controller/ui/scrollToSettings.ts`**
|
||||
```typescript
|
||||
import { Controller } from ".."
|
||||
import { StringRequest, KeyValuePair } from "../../../shared/proto/common"
|
||||
|
||||
/**
|
||||
* Executes a scroll to settings action
|
||||
* @param controller The controller instance
|
||||
* @param request The request containing the ID of the settings section to scroll to
|
||||
* @returns KeyValuePair with action and value fields for the UI to process
|
||||
*/
|
||||
export async function scrollToSettings(controller: Controller, request: StringRequest): Promise<KeyValuePair> {
|
||||
return KeyValuePair.create({
|
||||
key: "scrollToSettings",
|
||||
value: request.value || "",
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Call the RPC from the Webview
|
||||
|
||||
Call the new RPC from a React component in `webview-ui/`. The generated client makes this simple.
|
||||
|
||||
**File: `webview-ui/src/components/browser/BrowserSettingsMenu.tsx`** (Example)
|
||||
```tsx
|
||||
import { UiServiceClient } from "../../../services/grpc"
|
||||
import { StringRequest } from "../../../../shared/proto/common"
|
||||
|
||||
// ... inside a React component
|
||||
const handleMenuClick = async () => {
|
||||
try {
|
||||
await UiServiceClient.scrollToSettings(StringRequest.create({ value: "browser" }))
|
||||
} catch (error) {
|
||||
console.error("Error scrolling to browser settings:", error)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,64 +0,0 @@
|
||||
# Storage Architecture
|
||||
|
||||
Global settings, secrets and workspace state are stored in **file-backed JSON stores** under `~/.cline/data/`. This is the shared storage layer used by VSCode, CLI, and JetBrains.
|
||||
|
||||
## Key Abstractions
|
||||
|
||||
### `StorageContext` (src/shared/storage/storage-context.ts)
|
||||
The entry point. Created via `createStorageContext()` and passed to `StateManager.initialize()`. Contains three `ClineFileStorage` instances:
|
||||
- `globalState` → `~/.cline/data/globalState.json`
|
||||
- `secrets` → `~/.cline/data/secrets.json` (mode 0o600)
|
||||
- `workspaceState` → `~/.cline/data/workspaces/<hash>/workspaceState.json`
|
||||
|
||||
### `ClineFileStorage` (src/shared/storage/ClineFileStorage.ts)
|
||||
Synchronous JSON key-value store backed by a single file. Supports `get()`, `set()`, `setBatch()`, `delete()`. Writes are atomic (write-then-rename).
|
||||
|
||||
### `StateManager` (src/core/storage/StateManager.ts)
|
||||
In-memory cache on top of `StorageContext`. All runtime reads hit the cache; writes update cache immediately and debounce-flush to disk.
|
||||
|
||||
## ⚠️ Do NOT Use VSCode's ExtensionContext for Storage
|
||||
|
||||
**Do not** read from or write to `context.globalState`, `context.workspaceState`, or `context.secrets` for persistent data. These are VSCode-specific and not available on CLI or JetBrains.
|
||||
|
||||
Instead, use:
|
||||
```typescript
|
||||
// Reading state
|
||||
StateManager.get().getGlobalStateKey("myKey")
|
||||
StateManager.get().getSecretKey("mySecretKey")
|
||||
StateManager.get().getWorkspaceStateKey("myWsKey")
|
||||
|
||||
// Writing state
|
||||
StateManager.get().setGlobalState("myKey", value)
|
||||
StateManager.get().setSecret("mySecretKey", value)
|
||||
StateManager.get().setWorkspaceState("myWsKey", value)
|
||||
```
|
||||
|
||||
Remember that your data may be read by a different client than the one that wrote it. For example, a value written by Cline in JetBrains may be read by Cline CLI.
|
||||
|
||||
## VSCode Migration (src/hosts/vscode/vscode-to-file-migration.ts)
|
||||
|
||||
On VSCode startup, a migration copies data from VSCode's `ExtensionContext` storage into the file-backed stores. This runs in `src/common.ts` before `StateManager.initialize()`.
|
||||
|
||||
- **Sentinel**: `__vscodeMigrationVersion` key in global state and workspace state — prevents re-migration.
|
||||
- **Merge strategy**: File store wins. Existing values are never overwritten.
|
||||
- **Safe downgrade**: VSCode storage is NOT cleared, so older extension versions still work.
|
||||
|
||||
## Adding New Storage Keys
|
||||
|
||||
1. Add to `src/shared/storage/state-keys.ts` (see existing patterns)
|
||||
2. Read/write via `StateManager` (NOT via `context.globalState`)
|
||||
3. If adding a secret, add to `SecretKeys` array in `state-keys.ts`
|
||||
|
||||
## File Layout
|
||||
|
||||
```
|
||||
~/.cline/
|
||||
data/
|
||||
globalState.json # Global settings & state
|
||||
secrets.json # API keys (mode 0o600)
|
||||
tasks/
|
||||
taskHistory.json # Task history (separate file)
|
||||
workspaces/
|
||||
<hash>/
|
||||
workspaceState.json # Per-workspace toggles
|
||||
```
|
||||
@@ -1,29 +0,0 @@
|
||||
# Address PR Comments
|
||||
|
||||
Review and address all comments on the current branch's PR.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Get the current branch name and find the associated PR:
|
||||
```bash
|
||||
gh pr view --json number,title,body
|
||||
```
|
||||
|
||||
2. Understand the PR context:
|
||||
- Get the full diff: `git diff origin/main...HEAD`
|
||||
- Read the changed files to understand what the PR is doing
|
||||
- Read related files if needed to understand the broader context
|
||||
- Understand the intent and spirit of the changes, not just the code
|
||||
|
||||
3. Fetch all PR comments:
|
||||
- Inline comments: `gh api repos/{owner}/{repo}/pulls/{pr_number}/comments`
|
||||
- General comments: `gh pr view {pr_number} --json comments,reviews`
|
||||
|
||||
4. Present a summary of all comments with your recommendation for each (apply, skip, or respond). Ignore bot noise (release automation, CI status, etc.).
|
||||
|
||||
5. **Wait for my approval** before proceeding.
|
||||
|
||||
6. After approval:
|
||||
- Apply code changes and commit
|
||||
- Reply to comments that were addressed or intentionally skipped
|
||||
- Push commits
|
||||
@@ -1,49 +0,0 @@
|
||||
# Find Best Reviewers for Current Branch
|
||||
|
||||
Analyze my current branch to find the best people to review my PR based on **domain expertise** and git history.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Get the current branch name and verify it's not `main`
|
||||
2. Get the diff between the current branch and `origin/main`:
|
||||
- Use `git diff origin/main...HEAD --name-only` to get changed files
|
||||
- Use `git diff origin/main...HEAD` to understand the nature/spirit of the changes
|
||||
3. **Identify the domain/feature area** being changed:
|
||||
- Read the diff carefully to understand WHAT is being changed conceptually (e.g., "slash commands", "authentication", "API client", "UI components")
|
||||
- This semantic understanding is crucial for finding the right reviewers
|
||||
4. Find domain experts by searching for related files and their contributors:
|
||||
- Identify all files related to the feature/domain (not just the ones changed)
|
||||
- Example: if changing slash commands, find ALL slash-command related files across the codebase
|
||||
- Use `git log --format="%an <%ae>" -- <related-files-pattern>` to find who has expertise in that domain
|
||||
5. For additional context, also gather:
|
||||
- `git blame -L <start>,<end> origin/main -- <file-path>` for exact lines changed
|
||||
- Recent commit activity on related files
|
||||
6. Score and rank contributors by:
|
||||
- **Highest weight: Domain expertise** - who has the most commits to files in this feature area (even files not touched by this PR)
|
||||
- **Medium weight: Direct file expertise** - commits to the specific files being changed
|
||||
- **Lower weight: Line-level ownership** - authored the exact lines being modified
|
||||
7. Exclude myself (check against my git config user.email)
|
||||
8. Present the top 5 reviewers as an ordered list
|
||||
|
||||
## Output Format
|
||||
|
||||
Output an ordered list:
|
||||
|
||||
1. **Name** - Domain expert: 15 commits to slash-command related files, authored core parsing logic
|
||||
2. **Name** - 8 commits to affected files, recently added the feature being modified
|
||||
3. ...
|
||||
|
||||
## Commands Reference
|
||||
```bash
|
||||
git config user.email
|
||||
git diff origin/main...HEAD --name-only
|
||||
git diff origin/main...HEAD
|
||||
# Find related files for a domain (adjust pattern based on what you learn from the diff)
|
||||
find . -type f \( -name "*slash-command*" -o -name "*SlashCommand*" \) | head -20
|
||||
# Get contributors for related files
|
||||
find . -type f \( -name "*slash-command*" -o -name "*SlashCommand*" \) -print0 | xargs -0 git log --format="%an <%ae>" -- | sort | uniq -c | sort -rn
|
||||
git log --format="%an <%ae>" -- <file> | sort | uniq -c | sort -rn
|
||||
git blame -L 10,20 origin/main -- <file>
|
||||
```
|
||||
|
||||
Do NOT ask questions - analyze the changes, identify the domain, and output the reviewer list.
|
||||
@@ -1,61 +0,0 @@
|
||||
# Git Diff Analysis Workflow
|
||||
|
||||
## Objective
|
||||
Analyze the current branch's changes against main to provide informed insights and context for development decisions.
|
||||
|
||||
## Step 1: Gather Git Information
|
||||
<important>Do not return any text or conversation other than what is necessary to run these commands</important>
|
||||
|
||||
**Run the following command to get the latest changes (bash):**
|
||||
```bash
|
||||
B=$(for c in main master origin/main origin/master; do git rev-parse --verify -q "$c" >/dev/null && echo "$c" && break; done); B=${B:-HEAD}; r(){ git branch --show-current; printf "=== STATUS ===\n"; git status --porcelain | cat; printf "=== COMMIT MESSAGES ===\n"; git log "$B"..HEAD --oneline | cat; printf "=== CHANGED FILES ===\n"; git diff "$B" --name-only | cat; printf "=== FULL DIFF ===\n"; git diff "$B" | cat; }; L=$(r | wc -l); if [ "$L" -gt 500 ]; then r > cline-git-analysis.temp && echo "::OUTPUT_FILE=cline-git-analysis.temp"; else r; fi
|
||||
```
|
||||
|
||||
```powershell
|
||||
$B=$null;foreach($c in 'main','master','origin/main','origin/master'){git rev-parse --verify -q $c *> $null;if($LASTEXITCODE -eq 0){$B=$c;break}};if(-not $B){$B='HEAD'};function r([string]$b){git rev-parse --abbrev-ref HEAD; '=== STATUS ==='; git status --porcelain | cat; '=== COMMIT MESSAGES ==='; git log "$b"..HEAD --oneline | cat; '=== CHANGED FILES ==='; git diff "$b" --name-only | cat; '=== FULL DIFF ==='; git diff "$b" | cat};$out=r $B|Out-String;$lines=($out -split "`r?`n").Count;if($lines -gt 500){$out|Set-Content -NoNewline cline-git-analysis.temp; '::OUTPUT_FILE=cline-git-analysis.temp'}else{$out}
|
||||
```
|
||||
|
||||
## Step 2: Silent, Structured Analysis Phase
|
||||
- Analyze all git output without providing commentary or narration
|
||||
- Read the full diff to understand the scope and nature of changes
|
||||
- Identify patterns, architectural modifications, or potential impacts
|
||||
- Use `read_file` to examine any related files providing additional context on the changes you have observed
|
||||
|
||||
## Step 3: Context Gathering
|
||||
- Analyze related code without providing commentary or narration
|
||||
- Read relevant related source files if needed for complete understanding
|
||||
- Check dependencies, imports, or cross-references spanning the changes
|
||||
- Understand the broader codebase context around modifications
|
||||
- This additional context gathering should include related backend code, as well as related ui/frontend code
|
||||
- You will typically need to analyze at least several files, potentially many, in order to fully complete this step
|
||||
- You should not continue reading additional context if you have exhausted more than 60% of your available context window
|
||||
- If you have exhausted less than 40% of your context window, you should continue reviewing additional context
|
||||
|
||||
## Step 4: Ready for User Interaction
|
||||
**Only after completing the full analysis:**
|
||||
- Engage with the user based on comprehensive understanding
|
||||
- Provide insights about specific modifications and their impacts
|
||||
- If you are certain they exist, note potential breaking changes or compatibility issues
|
||||
- Answer questions with informed context from the complete change set and context gathering
|
||||
- If the user has not provided a question, or the question is insufficient to provide a quality response, ask brief (one sentence) clarifying questions.
|
||||
- Only offer recommendations if they are applicable to the user's request and relevant to the changes that you have observed
|
||||
|
||||
## Key Rules
|
||||
- **No prose or conversation during git research phase**
|
||||
- **No prose or conversation during context gathering phase**
|
||||
- **Complete all analysis before any user interaction**
|
||||
- **Use gathered information for all subsequent questions and insights**
|
||||
- **Focus on understanding the complete picture before discussing**
|
||||
|
||||
## Optional: Additional Analysis Commands
|
||||
For deeper investigation when needed:
|
||||
|
||||
```shell
|
||||
# Detailed commit history with author info
|
||||
git log main..HEAD --format="%h %s (%an)" | cat
|
||||
|
||||
# Change statistics
|
||||
git diff main --stat | cat
|
||||
|
||||
# Specific file type changes
|
||||
git diff main --name-only | grep -E '\.(ts|js|tsx|jsx|py|md)$' | cat
|
||||
@@ -1,187 +0,0 @@
|
||||
# Hotfix Release
|
||||
|
||||
Create a hotfix release by cherry-picking specific commits from main onto the latest release tag.
|
||||
|
||||
## Overview
|
||||
|
||||
This workflow helps you:
|
||||
1. Select specific commits from main to include in a hotfix
|
||||
2. Create a release notes commit on main (changelog + version bump)
|
||||
3. Cherry-pick everything onto the latest release tag
|
||||
4. Tag and push the new release
|
||||
|
||||
## Step 1: Setup and Gather Information
|
||||
|
||||
First, ensure we're on main and up to date:
|
||||
|
||||
```bash
|
||||
git checkout main && git pull origin main
|
||||
```
|
||||
|
||||
Get the latest release tag:
|
||||
|
||||
```bash
|
||||
git tag --sort=-v:refname | head -1
|
||||
```
|
||||
|
||||
## Step 2: Present Commits Since Last Release
|
||||
|
||||
Show all commits on main since the last release tag:
|
||||
|
||||
```bash
|
||||
LAST_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
git log ${LAST_TAG}..HEAD --oneline --format="%h %s (%an)"
|
||||
```
|
||||
|
||||
Also get the commit messages already on the tag (to identify previously cherry-picked commits). Note: Run these as separate commands to avoid shell parsing issues with parentheses in author names:
|
||||
|
||||
```bash
|
||||
LAST_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
PREV_TAG=$(git tag --sort=-v:refname | head -2 | tail -1)
|
||||
```
|
||||
|
||||
```bash
|
||||
git log $PREV_TAG..$LAST_TAG --oneline --format="%s"
|
||||
```
|
||||
|
||||
**Present the list** to the user in a numbered format with commit hash, subject, and author. For any commits whose subject line already appears in the tag's history (previously cherry-picked in an earlier hotfix) or are "Release Notes" commits, add `(already in previous hotfix)` or `(release notes - skip)` after them so the user knows to skip those.
|
||||
|
||||
Ask which commits to include in the hotfix.
|
||||
|
||||
Use the ask_followup_question tool to let the user specify which commits they want (by number or hash).
|
||||
|
||||
## Step 3: Analyze Selected Commits
|
||||
|
||||
For each selected commit:
|
||||
1. Get the full commit message: `git show --no-patch --format="%B" <hash>`
|
||||
2. Get the diff to understand the change: `git show <hash> --stat`
|
||||
3. Find the associated PR if any: `gh pr list --search "<hash>" --state merged --json number,title --jq '.[0]'`
|
||||
|
||||
Build a mental model of what these changes do for the changelog.
|
||||
|
||||
## Step 4: Determine New Version Number
|
||||
|
||||
Parse the current version from package.json and the last tag:
|
||||
|
||||
```bash
|
||||
LAST_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
echo "Last release: $LAST_TAG"
|
||||
cat package.json | grep '"version"'
|
||||
```
|
||||
|
||||
Hotfixes always increment the patch version (e.g., 3.40.0 -> 3.40.1, or 3.40.1 -> 3.40.2).
|
||||
|
||||
**Ask the user to confirm the new version number.**
|
||||
|
||||
## Step 5: Create Release Notes Commit on Main
|
||||
|
||||
On the main branch, create a commit that updates:
|
||||
|
||||
1. **CHANGELOG.md** - Add a new section for the hotfix version at the top:
|
||||
```markdown
|
||||
## [3.40.1]
|
||||
|
||||
- Description of fix 1
|
||||
- Description of fix 2
|
||||
```
|
||||
|
||||
Write clear, user-friendly descriptions based on your analysis of the commits.
|
||||
|
||||
2. **package.json** - Update the version field to the new version
|
||||
|
||||
3. No changelog-entry file cleanup is needed. Contributors do not create changelog-entry files in this repo.
|
||||
|
||||
**Skip running `npm run install:all`** - release automation handles lockfile consistency as needed.
|
||||
|
||||
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
|
||||
git commit -m "v3.40.1 Release Notes (hotfix)
|
||||
|
||||
Hotfix release including:
|
||||
- <commit1-hash>: <description>
|
||||
- <commit2-hash>: <description>
|
||||
"
|
||||
```
|
||||
|
||||
Push to main:
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## Step 6: Build the Hotfix on the Tag
|
||||
|
||||
Checkout the last release tag (detached HEAD):
|
||||
|
||||
```bash
|
||||
LAST_TAG=$(git tag --sort=-v:refname | head -1)
|
||||
git checkout $LAST_TAG
|
||||
```
|
||||
|
||||
Cherry-pick the selected commits in order:
|
||||
|
||||
```bash
|
||||
git cherry-pick <commit1-hash>
|
||||
git cherry-pick <commit2-hash>
|
||||
# ... etc
|
||||
```
|
||||
|
||||
Finally, cherry-pick the release notes commit you just pushed to main:
|
||||
|
||||
```bash
|
||||
# Get the hash of the release notes commit (should be HEAD of main)
|
||||
RELEASE_NOTES_COMMIT=$(git rev-parse main)
|
||||
git cherry-pick $RELEASE_NOTES_COMMIT
|
||||
```
|
||||
|
||||
## Step 7: Tag and Push
|
||||
|
||||
After all cherry-picks are applied successfully:
|
||||
|
||||
```bash
|
||||
# Tag the new release
|
||||
git tag v{VERSION}
|
||||
|
||||
# Push the tag to remote
|
||||
git push origin v{VERSION}
|
||||
```
|
||||
|
||||
## Step 8: Return to Main and Summary
|
||||
|
||||
Return to main branch:
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
```
|
||||
|
||||
**Copy a Slack announcement message to clipboard** with the version and PR links for each included fix:
|
||||
|
||||
```
|
||||
VS Code Hotfix v{VERSION} Published
|
||||
|
||||
- Description of fix 1 https://github.com/cline/cline/pull/{PR_NUMBER}
|
||||
- Description of fix 2 https://github.com/cline/cline/pull/{PR_NUMBER}
|
||||
```
|
||||
|
||||
Present a final summary:
|
||||
- New version: v{VERSION}
|
||||
- Tag pushed: yes
|
||||
- Commits included: (list them)
|
||||
- Slack message copied to clipboard: yes
|
||||
|
||||
Remind the user to:
|
||||
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/publish.yml (paste `v{VERSION}` as the tag)
|
||||
2. Post the Slack message to announce the hotfix
|
||||
|
||||
## Important Notes
|
||||
|
||||
- This workflow does NOT create a release branch - only tags
|
||||
- The release notes commit goes to main first, then gets cherry-picked to the tag
|
||||
- This keeps main's history accurate while allowing hotfix releases from tags
|
||||
- If cherry-pick conflicts occur, resolve them before continuing
|
||||
@@ -1,352 +0,0 @@
|
||||
You have access to the `gh` terminal command. I already authenticated it for you. Please review it to use the PR that I asked you to review. You're already in the `cline` repo.
|
||||
|
||||
<detailed_sequence_of_steps>
|
||||
# GitHub PR Review Process - Detailed Sequence of Steps
|
||||
|
||||
## 1. Gather PR Information
|
||||
1. Get the PR title, description, and comments:
|
||||
```bash
|
||||
gh pr view <PR-number> --json title,body,comments
|
||||
```
|
||||
|
||||
2. Get the full diff of the PR:
|
||||
```bash
|
||||
gh pr diff <PR-number>
|
||||
```
|
||||
|
||||
## 2. Understand the Context
|
||||
1. Identify which files were modified in the PR:
|
||||
```bash
|
||||
gh pr view <PR-number> --json files
|
||||
```
|
||||
|
||||
2. Examine the original files in the main branch to understand the context:
|
||||
```xml
|
||||
<read_file>
|
||||
<path>path/to/file</path>
|
||||
</read_file>
|
||||
```
|
||||
|
||||
3. For specific sections of a file, you can use search_files:
|
||||
```xml
|
||||
<search_files>
|
||||
<path>path/to/directory</path>
|
||||
<regex>search term</regex>
|
||||
<file_pattern>*.ts</file_pattern>
|
||||
</search_files>
|
||||
```
|
||||
|
||||
## 3. Analyze the Changes
|
||||
1. For each modified file, understand:
|
||||
- What was changed
|
||||
- Why it was changed (based on PR description)
|
||||
- How it affects the codebase
|
||||
- Potential side effects
|
||||
|
||||
2. Look for:
|
||||
- Code quality issues
|
||||
- Potential bugs
|
||||
- Performance implications
|
||||
- Security concerns
|
||||
- Test coverage
|
||||
|
||||
## 4. Ask for User Confirmation
|
||||
1. Before making a decision, ask the user if you should approve the PR, providing your assessment and justification:
|
||||
```xml
|
||||
<ask_followup_question>
|
||||
<question>Based on my review of PR #<PR-number>, I recommend [approving/requesting changes]. Here's my justification:
|
||||
|
||||
[Detailed justification with key points about the PR quality, implementation, and any concerns]
|
||||
|
||||
Would you like me to proceed with this recommendation?</question>
|
||||
<options>["Yes, approve the PR", "Yes, request changes", "No, I'd like to discuss further"]</options>
|
||||
</ask_followup_question>
|
||||
```
|
||||
|
||||
## 5. Ask if User Wants a Comment Drafted
|
||||
1. After the user decides on approval/rejection, ask if they would like a comment drafted:
|
||||
```xml
|
||||
<ask_followup_question>
|
||||
<question>Would you like me to draft a comment for this PR that you can copy and paste?</question>
|
||||
<options>["Yes, please draft a comment", "No, I'll handle the comment myself"]</options>
|
||||
</ask_followup_question>
|
||||
```
|
||||
|
||||
2. If the user wants a comment drafted, provide a well-structured comment they can copy:
|
||||
```
|
||||
Thank you for this PR! Here's my assessment:
|
||||
|
||||
[Detailed assessment with key points about the PR quality, implementation, and any suggestions]
|
||||
|
||||
[Include specific feedback on code quality, functionality, and testing]
|
||||
```
|
||||
|
||||
## 6. Make a Decision
|
||||
1. Approve the PR if it meets quality standards:
|
||||
```bash
|
||||
# For single-line comments:
|
||||
gh pr review <PR-number> --approve --body "Your approval message"
|
||||
|
||||
# For multi-line comments with proper whitespace formatting:
|
||||
cat << EOF | gh pr review <PR-number> --approve --body-file -
|
||||
Thanks @username for this PR! The implementation looks good.
|
||||
|
||||
I particularly like how you've handled X and Y.
|
||||
|
||||
Great work!
|
||||
EOF
|
||||
```
|
||||
|
||||
2. Request changes if improvements are needed:
|
||||
```bash
|
||||
# For single-line comments:
|
||||
gh pr review <PR-number> --request-changes --body "Your feedback message"
|
||||
|
||||
# For multi-line comments with proper whitespace formatting:
|
||||
cat << EOF | gh pr review <PR-number> --request-changes --body-file -
|
||||
Thanks @username for this PR!
|
||||
|
||||
The implementation looks promising, but there are a few things to address:
|
||||
|
||||
1. Issue one
|
||||
2. Issue two
|
||||
|
||||
Please make these changes and we can merge this.
|
||||
EOF
|
||||
```
|
||||
|
||||
Note: The `cat << EOF | ... --body-file -` approach preserves all whitespace and formatting without requiring temporary files. The `-` parameter tells the command to read from standard input.
|
||||
</detailed_sequence_of_steps>
|
||||
|
||||
<example_review_process>
|
||||
# Example PR Review Process
|
||||
|
||||
Let's walk through a real example of reviewing PR #3627 which fixes the thinking mode calculation for Claude 3.7 models.
|
||||
|
||||
## Step 1: Gather PR Information
|
||||
|
||||
```bash
|
||||
# Get PR details
|
||||
gh pr view 3627 --json title,body,comments
|
||||
|
||||
# Get the full diff
|
||||
gh pr diff 3627
|
||||
```
|
||||
|
||||
## Step 2: Understand the Context
|
||||
|
||||
```xml
|
||||
# Examine the original files to understand what's being changed
|
||||
<read_file>
|
||||
<path>src/shared/api.ts</path>
|
||||
</read_file>
|
||||
|
||||
# Look at the ThinkingBudgetSlider component implementation
|
||||
<read_file>
|
||||
<path>webview-ui/src/components/settings/ThinkingBudgetSlider.tsx</path>
|
||||
</read_file>
|
||||
|
||||
# Check how thinking mode is currently handled in API providers
|
||||
<search_files>
|
||||
<path>src/api/providers</path>
|
||||
<regex>reasoningOn</regex>
|
||||
<file_pattern>*.ts</file_pattern>
|
||||
</search_files>
|
||||
```
|
||||
|
||||
## Step 3: Analyze the Changes
|
||||
|
||||
From the PR information and code examination, we can see that:
|
||||
|
||||
1. The PR fixes an issue with the thinking mode budget calculation for Claude 3.7 models
|
||||
2. Currently, the thinking budget is incorrectly calculated as 80% of maxTokens (8192), resulting in 6553 tokens
|
||||
3. Claude 3.7 actually supports a much larger thinking budget (up to 64000 tokens)
|
||||
4. The PR adds a `thinkingConfig` property with `maxBudget: 64000` to model definitions
|
||||
5. It updates API handlers to use this value when reasoning mode is enabled
|
||||
6. It enhances the slider component to calculate max values based on model-specific percentages
|
||||
7. Comprehensive tests were added to verify the calculations
|
||||
|
||||
## Step 4: Ask for User Confirmation
|
||||
|
||||
```xml
|
||||
<ask_followup_question>
|
||||
<question>Based on my review of PR #3627, I recommend approving this PR. Here's my justification:
|
||||
|
||||
1. The PR correctly fixes the thinking mode budget calculation for Claude 3.7 models
|
||||
2. It properly implements the thinkingConfig.maxBudget property (64000) for Claude 3.7 models
|
||||
3. It applies the appropriate percentage (50%) for the slider calculation
|
||||
4. The changes are well-tested with comprehensive unit tests
|
||||
5. The implementation is clean and follows the project's coding standards
|
||||
6. The PR includes helpful screenshots demonstrating the fix
|
||||
|
||||
Would you like me to proceed with approving this PR?</question>
|
||||
<options>["Yes, approve the PR", "No, I'd like to discuss further", "Let me review it myself first"]</options>
|
||||
</ask_followup_question>
|
||||
```
|
||||
|
||||
## Step 5: Ask if User Wants a Comment Drafted
|
||||
|
||||
```xml
|
||||
<ask_followup_question>
|
||||
<question>Would you like me to draft a comment for this PR that you can copy and paste?</question>
|
||||
<options>["Yes, please draft a comment", "No, I'll handle the comment myself"]</options>
|
||||
</ask_followup_question>
|
||||
```
|
||||
|
||||
## Step 6: Make a Decision
|
||||
|
||||
```bash
|
||||
# Option 1: Simple one-line comment
|
||||
gh pr review 3627 --approve --body "This PR looks good! It correctly fixes the thinking mode budget calculation for Claude 3.7 models."
|
||||
|
||||
# Option 2: Multi-line comment with proper whitespace formatting
|
||||
cat << EOF | gh pr review 3627 --approve --body-file -
|
||||
This PR looks good! It correctly fixes the thinking mode budget calculation for Claude 3.7 models.
|
||||
|
||||
I particularly like:
|
||||
1. The proper implementation of thinkingConfig.maxBudget property (64000)
|
||||
2. The appropriate percentage (50%) for the slider calculation
|
||||
3. The comprehensive unit tests
|
||||
4. The clean implementation that follows project coding standards
|
||||
|
||||
Great work!
|
||||
EOF
|
||||
```
|
||||
</example_review_process>
|
||||
|
||||
<common_gh_commands>
|
||||
# Common GitHub CLI Commands for PR Review
|
||||
|
||||
## Basic PR Commands
|
||||
```bash
|
||||
# Get current PR number
|
||||
gh pr view --json number -q .number
|
||||
|
||||
# List open PRs
|
||||
gh pr list
|
||||
|
||||
# View a specific PR
|
||||
gh pr view <PR-number>
|
||||
|
||||
# View PR with specific fields
|
||||
gh pr view <PR-number> --json title,body,comments,files,commits
|
||||
|
||||
# Check PR status
|
||||
gh pr status
|
||||
```
|
||||
|
||||
## Diff and File Commands
|
||||
```bash
|
||||
# Get the full diff of a PR
|
||||
gh pr diff <PR-number>
|
||||
|
||||
# List files changed in a PR
|
||||
gh pr view <PR-number> --json files
|
||||
|
||||
# Check out a PR locally
|
||||
gh pr checkout <PR-number>
|
||||
```
|
||||
|
||||
## Review Commands
|
||||
```bash
|
||||
# Approve a PR (single-line comment)
|
||||
gh pr review <PR-number> --approve --body "Your approval message"
|
||||
|
||||
# Approve a PR (multi-line comment with proper whitespace)
|
||||
cat << EOF | gh pr review <PR-number> --approve --body-file -
|
||||
Your multi-line
|
||||
approval message with
|
||||
|
||||
proper whitespace formatting
|
||||
EOF
|
||||
|
||||
# Request changes on a PR (single-line comment)
|
||||
gh pr review <PR-number> --request-changes --body "Your feedback message"
|
||||
|
||||
# Request changes on a PR (multi-line comment with proper whitespace)
|
||||
cat << EOF | gh pr review <PR-number> --request-changes --body-file -
|
||||
Your multi-line
|
||||
change request with
|
||||
|
||||
proper whitespace formatting
|
||||
EOF
|
||||
|
||||
# Add a comment review (without approval/rejection)
|
||||
gh pr review <PR-number> --comment --body "Your comment message"
|
||||
|
||||
# Add a comment review with proper whitespace
|
||||
cat << EOF | gh pr review <PR-number> --comment --body-file -
|
||||
Your multi-line
|
||||
comment with
|
||||
|
||||
proper whitespace formatting
|
||||
EOF
|
||||
```
|
||||
|
||||
## Additional Commands
|
||||
```bash
|
||||
# View PR checks status
|
||||
gh pr checks <PR-number>
|
||||
|
||||
# View PR commits
|
||||
gh pr view <PR-number> --json commits
|
||||
|
||||
# Merge a PR (if you have permission)
|
||||
gh pr merge <PR-number> --merge
|
||||
```
|
||||
</common_gh_commands>
|
||||
|
||||
<general_guidelines_for_commenting>
|
||||
When reviewing a PR, please talk normally and like a friendly reviwer. You should keep it short, and start out by thanking the author of the pr and @ mentioning them.
|
||||
|
||||
Whether or not you approve the PR, you should then give a quick summary of the changes without being too verbose or definitive, staying humble like that this is your understanding of the changes. Kind of how I'm talking to you right now.
|
||||
|
||||
If you have any suggestions, or things that need to be changed, request changes instead of approving the PR.
|
||||
|
||||
Leaving inline comments in code is good, but only do so if you have something specific to say about the code. And make sure you leave those comments first, and then request changes in the PR with a short comment explaining the overall theme of what you're asking them to change.
|
||||
</general_guidelines_for_commenting>
|
||||
|
||||
<example_comments_that_i_have_written_before>
|
||||
<brief_approve_comment>
|
||||
Looks good, though we should make this generic for all providers & models at some point
|
||||
</brief_approve_comment>
|
||||
<brief_approve_comment>
|
||||
Will this work for models that may not match across OR/Gemini? Like the thinking models?
|
||||
</brief_approve_comment>
|
||||
<approve_comment>
|
||||
This looks great! I like how you've handled the global endpoint support - adding it to the ModelInfo interface makes total sense since it's just another capability flag, similar to how we handle other model features.
|
||||
|
||||
The filtered model list approach is clean and will be easier to maintain than hardcoding which models work with global endpoints. And bumping the genai library was obviously needed for this to work.
|
||||
|
||||
Thanks for adding the docs about the limitations too - good for users to know they can't use context caches with global endpoints but might get fewer 429 errors.
|
||||
</approve_comment>
|
||||
<requesst_changes_comment>
|
||||
This is awesome. Thanks @scottsus.
|
||||
|
||||
My main concern though - does this work for all the possible VS Code themes? We struggled with this initially which is why it's not super styled currently. Please test and share screenshots with the different themes to make sure before we can merge
|
||||
</request_changes_comment>
|
||||
<request_changes_comment>
|
||||
Hey, the PR looks good overall but I'm concerned about removing those timeouts. Those were probably there for a reason - VSCode's UI can be finicky with timing.
|
||||
|
||||
Could you add back the timeouts after focusing the sidebar? Something like:
|
||||
|
||||
```typescript
|
||||
await vscode.commands.executeCommand("claude-dev.SidebarProvider.focus")
|
||||
await setTimeoutPromise(100) // Give UI time to update
|
||||
visibleWebview = WebviewProvider.getSidebarInstance()
|
||||
```
|
||||
</request_changes_comment>
|
||||
<request_changes_comment>
|
||||
Heya @alejandropta thanks for working on this!
|
||||
|
||||
A few notes:
|
||||
1 - Adding additional info to the environment variables is fairly problematic because env variables get appended to **every single message**. I don't think this is justifiable for a somewhat niche use case.
|
||||
2 - Adding this option to settings to include that could be an option, but we want our options to be simple and straightforward for new users
|
||||
3 - We're working on revisualizing the way our settings page is displayed/organized, and this could potentially be reconciled once that is in and our settings page is more clearly delineated.
|
||||
|
||||
So until the settings page is update, and this is added to settings in a way that's clean and doesn't confuse new users, I don't think we can merge this. Please bear with us.
|
||||
</request_changes_comment>
|
||||
<request_changes_comment>
|
||||
The architectural change is solid - moving the focus logic to the command handlers makes sense. Just don't want to introduce subtle timing issues by removing those timeouts.
|
||||
</request_changes_comment>
|
||||
</example_comments_that_i_have_written_before>
|
||||
@@ -1,64 +0,0 @@
|
||||
# Release
|
||||
|
||||
Prepare and publish a release directly from `main`.
|
||||
|
||||
## Overview
|
||||
|
||||
This workflow helps you:
|
||||
1. Select/confirm the target version
|
||||
2. Curate `CHANGELOG.md` entries manually for end users
|
||||
3. Ensure `package.json` version matches the changelog
|
||||
4. Create and push a release commit + tag
|
||||
5. Trigger publish workflow
|
||||
6. Update GitHub release notes and share a summary
|
||||
|
||||
## Process
|
||||
|
||||
### 1) Sync and determine version
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull origin main
|
||||
cat package.json | grep '"version"'
|
||||
```
|
||||
|
||||
Confirm the release version with the maintainer (patch/minor/major).
|
||||
|
||||
### 2) Curate changelog and version
|
||||
|
||||
- Edit `CHANGELOG.md` for the target version using human-friendly release notes.
|
||||
- Ensure version headers use bracket format, e.g. `## [3.66.1]`.
|
||||
- Update `package.json` version to the same value.
|
||||
|
||||
### 3) Commit and tag
|
||||
|
||||
```bash
|
||||
git add CHANGELOG.md package.json package-lock.json
|
||||
git commit -m "v<version> Release Notes"
|
||||
git push origin main
|
||||
git tag v<version>
|
||||
git push origin v<version>
|
||||
```
|
||||
|
||||
### 4) Trigger publish workflow
|
||||
|
||||
Tell the maintainer to run:
|
||||
https://github.com/cline/cline/actions/workflows/publish.yml
|
||||
|
||||
Use `v<version>` as the release tag.
|
||||
|
||||
### 5) Update GitHub release notes
|
||||
|
||||
After publish completes:
|
||||
|
||||
```bash
|
||||
gh release view v<version> --json body --jq '.body'
|
||||
gh release edit v<version> --notes "<final curated release notes>"
|
||||
```
|
||||
|
||||
### 6) Final summary
|
||||
|
||||
Provide:
|
||||
- Released version/tag
|
||||
- Link to release page
|
||||
- Summary of top end-user changes
|
||||
@@ -1,392 +0,0 @@
|
||||
# General writing guide
|
||||
|
||||
# How I want you to write
|
||||
|
||||
I'm gonna write something technical.
|
||||
|
||||
It's often less about the nitty-gritty details of the tech stuff and more about learning something new or getting a solution handed to me on a silver platter.
|
||||
|
||||
Look, when I read, I want something out of it. So when I write, I gotta remember that my readers want something too. This whole piece? It's about cluing in anyone who writes for me, or wants me to write for them, on how I see this whole writing product thing.
|
||||
|
||||
I'm gonna lay out a checklist of stuff I'd like to have. It'll make the whole writing gig a bit smoother, you know?
|
||||
|
||||
## Crafting Compelling Titles
|
||||
|
||||
I often come across titles like "How to do X with Y,Z technology." These don't excite me because X or Y are usually unfamiliar unless they're already well-known. Its rarely the dream to use X unless X is the dream.
|
||||
|
||||
My dream isn’t to use instructor, its to do something valueble with the data it extracts
|
||||
|
||||
An effective title should:
|
||||
|
||||
- Evoke an emotional response
|
||||
- Highlight someone's goal
|
||||
- Offer a dream or aspiration
|
||||
- Challenge or comment on a belief
|
||||
- Address someone's problems
|
||||
|
||||
I believe it's more impactful to write about specific problems. If this approach works, you can replicate it across various scenarios rather than staying too general.
|
||||
|
||||
- Time management for everyone can be a 15$ ebook
|
||||
- Time management for executives is a 2000$ workshop
|
||||
|
||||
Aim for titles that answer questions you think everyone is asking, or address thoughts people have but can't quite articulate.
|
||||
|
||||
Instead of "How I do something" or "How to do something," frame it from the reader's perspective with "How you can do something." This makes the title more engaging. Just make sure the difference is advisory if the content is subjective. “How I made a million dollars” might be more reasonable than “How to make a million dollars” since you are the subject and the goal might be to share your story in hopes of helping others.
|
||||
|
||||
This approach ultimately trains the reader to have a stronger emotional connection to your content.
|
||||
|
||||
- "How I do X"
|
||||
- "How You Can do X"
|
||||
|
||||
Between these two titles, it's obvious which one resonates more emotionally.
|
||||
|
||||
You can take it further by adding specific conditions. For instance, you could target a particular audience or set a timeframe:
|
||||
|
||||
- How to set up Braintrust
|
||||
- How to set up Braintrust in 5 minutes
|
||||
|
||||
## NO adjectiives
|
||||
|
||||
I want you to almost always avoid adjectives and try to use evidence instead. Instead of saying "production ready," you can write something like "scaling this to 100 servers or 1 million documents per second." Numbers like that will tell you exactly what the specificity of your product is. If you have to use adjectives rather than evidence, you are probably making something up.
|
||||
|
||||
There's no reason to say something like "blazingly fast" unless those things are already known phrases.
|
||||
|
||||
Instead, say "200 times faster" or "30% faster." A 30% improvement in recommendation system speed is insane.
|
||||
|
||||
There's a 200 times performance improvement because we went from one programming language to another. It's just something that's a little bit more expected and understandable.
|
||||
|
||||
Another test that I really like using recently is tracking whether or not the statements you make can be:
|
||||
|
||||
- Visualized
|
||||
- Proven false
|
||||
- Said only by you
|
||||
|
||||
If you can nail all three, the claim you make will be more likely to resonate with an audience because only you can say it.
|
||||
|
||||
Earlier this year, I had an example where I embedded all of Wikipedia in 17 minutes with 20 bucks, and it got half a million views. All we posted was a video of me kicking off the job, and then you can see all the log lines go through. You see the number of containers go from 1 out of 50 to 50 out of 50.
|
||||
|
||||
It was easy to visualize and could have been proven false by being unreproducible. Lastly, Modal is the only company that could do that in such an effortless way, which made it unique.
|
||||
|
||||
## Keep It Digestible
|
||||
- Aim for 5-minute reads
|
||||
- Write at a Grade 10 reading level
|
||||
- Break up long paragraphs
|
||||
- Use headers and bullet points
|
||||
|
||||
## Make It Scannable
|
||||
- Bold key points
|
||||
- Use subheadings every 3-4 paragraphs
|
||||
- Include plenty of white space
|
||||
- Add relevant examples
|
||||
|
||||
This structure works whether you're writing a tweet thread or a full blog post. The key is making complex ideas accessible.
|
||||
|
||||
# Guide to Writing Cline Documentation
|
||||
|
||||
## Some general principles for explaining features
|
||||
|
||||
If you're talking about a feature, it's helpful to start with a human-readable explanations that cover what the feature is in simple terms. Skip jargon and explain it like you're talking to someone who's never seen it before. This sets the foundation for everything that follows.
|
||||
|
||||
Combine location and usage into one flowing section. Tell users exactly where to find the feature and how to use it, but weave the instructions into natural prose with a good balance of bullet points, numbered lists, code examples (if applicable), mintlify components, and headers/subheaders. Users shouldn't have to jump between separate "where is it" and "how do I use it" sections.
|
||||
|
||||
Show the feature in action with real examples like actual files, workflows, or code. Users need to see concrete implementations, not just abstract descriptions. This is where understanding turns into practical knowledge.
|
||||
|
||||
When talking about a feature, include an inspiration section that sparks imagination. This section pushes people from understanding to action by showing them what becomes possible when they use this feature creatively. It's what separates good documentation from great documentation.
|
||||
|
||||
## Writing Principles That Actually Work
|
||||
|
||||
### Write for Action, Not Just Understanding
|
||||
|
||||
Documentation should motivate users to try things. Instead of just explaining how something works, focus on what users can accomplish with it. The inspiration section is crucial - it's what transforms passive readers into active users.
|
||||
|
||||
### Create a Natural Story Flow
|
||||
|
||||
It should feel like a conversation that naturally progresses from "what is this?" to "how do I use it?" to "here's a real example" to "imagine what you could do with this."
|
||||
|
||||
### Show Real Examples, Not Toy Demos
|
||||
|
||||
Provide actual workflow files, real code snippets, and concrete implementations that users can copy and adapt. Abstract examples don't help anyone - users want to see exactly what they'll be working with.
|
||||
|
||||
### Keep It Scannable But Not Fragmented
|
||||
|
||||
Write in prose that flows naturally when read completely, but structure it so users can quickly find specific information when they're troubleshooting. Avoid dense walls of text, but also avoid over-formatting with excessive bullet points and bold headers. There should be a nice visual heirarchy of balance between all elements, so you can quickly scan the page and find what you're looking for.
|
||||
|
||||
## Language and Tone Guidelines
|
||||
|
||||
Write clearly without dumbing things down. Use simple language when possible, but don't avoid technical terms that users need to know. Explain concepts in terms of what users can achieve rather than how the software works internally.
|
||||
|
||||
Make your writing conversational and encouraging. Phrases like "you can also try" or "when that works" feel more natural than rigid instructional language. Help users feel confident about trying new things.
|
||||
|
||||
Keep content concise and purposeful. Every sentence should either help users understand something or help them do something. If it doesn't serve one of those purposes, cut it.
|
||||
|
||||
Build in context and reasoning. Users want to understand why they're doing something, not just what to do. This builds confidence and helps them troubleshoot when things don't work exactly as expected.
|
||||
|
||||
## Practical Implementation
|
||||
|
||||
Structure each feature page consistently with the four-section approach, but let the content flow naturally within that structure. Use visual assets like videos and screenshots to complement the written content - they often communicate more effectively than paragraphs of description.
|
||||
|
||||
Link generously to related resources, examples, and deeper documentation. Users should never feel stuck or wonder where to go next. Maintain a repository of real examples that users can reference and adapt to their own needs.
|
||||
|
||||
The goal is documentation that feels more like helpful guidance from an experienced colleague than a technical manual. Users should finish reading feeling excited about what they can accomplish, not just informed about what the feature does.
|
||||
|
||||
## Balance Structure with Flexibility
|
||||
|
||||
While they discuss having consistent documentation structure, there's also mention of making content feel less rigid and more natural. The writing should follow guidelines while still feeling conversational and engaging.
|
||||
|
||||
## Bad examples
|
||||
|
||||
I personally hate this pattern of bullet point **Bold Text** colon and then more text:
|
||||
<bad_example_of_writing>
|
||||
#### macOS
|
||||
|
||||
1. **Switch to bash**: Go to Cline Settings → Terminal → Default Terminal Profile → Select "bash"
|
||||
2. **Disable Oh-My-Zsh temporarily**: If using zsh, try `mv ~/.zshrc ~/.zshrc.backup` and restart VSCode
|
||||
3. **Set environment**: Add to your shell config: `export TERM=xterm-256color`
|
||||
|
||||
#### Windows
|
||||
|
||||
1. **Use PowerShell 7**: Install from Microsoft Store, then select it in Cline settings
|
||||
2. **Disable Windows ConPTY**: VSCode Settings → Terminal › Integrated: Windows Enable Conpty → Uncheck
|
||||
3. **Try Command Prompt**: Sometimes simpler is better - switch to cmd.exe
|
||||
|
||||
#### Linux
|
||||
|
||||
1. **Use bash**: Most reliable option - select in Cline settings
|
||||
2. **Check permissions**: Ensure VSCode has terminal access permissions
|
||||
3. **Disable custom prompts**: Comment out prompt customizations in `.bashrc`
|
||||
|
||||
</bad_example_of_writing>
|
||||
|
||||
We should instead strive to write beautiful docs that read well. We can use bullet points and numbered lists but it should read naturally and be delightful to look at hierachally when scanning through the doc. There should be a good balance between blocks of text, code snippets, paragraphs, numbered lists, and bullet points. When scanning the documentation visually, you should feel like you're adminiring a tasteful art piece.
|
||||
|
||||
<good_example_of_writing>
|
||||
#### macOS
|
||||
|
||||
The most common fix is switching to bash. Navigate to Cline Settings → Terminal → Default Terminal Profile and select "bash" from the dropdown.
|
||||
|
||||
If you're still having issues, Oh-My-Zsh might be interfering with terminal integration. Try temporarily disabling it:
|
||||
- Run `mv ~/.zshrc ~/.zshrc.backup`
|
||||
- Restart VSCode
|
||||
|
||||
You can also add `export TERM=xterm-256color` to your shell configuration file to improve compatibility.
|
||||
|
||||
#### Windows
|
||||
|
||||
PowerShell 7 provides the most reliable experience. Install it from the Microsoft Store, then select it in your Cline settings.
|
||||
|
||||
Still seeing problems? Try these solutions:
|
||||
- Disable Windows ConPTY: VSCode Settings → Terminal › Integrated: Windows Enable Conpty → uncheck
|
||||
- Switch to Command Prompt (cmd.exe) - sometimes simpler shells work better
|
||||
|
||||
#### Linux
|
||||
|
||||
Bash is your most dependable option. Select it in Cline settings if you haven't already.
|
||||
|
||||
Check these common issues:
|
||||
- Ensure VSCode has terminal access permissions
|
||||
- Temporarily comment out custom prompt configurations in your `.bashrc`
|
||||
</good_example_of_writing>
|
||||
|
||||
This is much more natural to read. Writing this way creates a conversational flow, and bullet points are used idiomatically.
|
||||
|
||||
# Using Mintlify Components Idiomatically
|
||||
|
||||
Mintlify's custom components can transform basic documentation into engaging, scannable content that users actually want to read. Here's how to use them effectively.
|
||||
|
||||
## Visual Content with Frames
|
||||
|
||||
Videos and images should be wrapped in `<Frame>` components rather than using raw HTML or markdown. This creates consistent styling and proper responsive behavior.
|
||||
|
||||
For videos, embed them directly rather than linking externally. Users are much more likely to watch a 30-second demonstration than click through to another platform:
|
||||
|
||||
```jsx
|
||||
<Frame>
|
||||
<iframe
|
||||
style={{ width: "100%", aspectRatio: "16/9" }}
|
||||
src="https://www.youtube.com/embed/your-video-id"
|
||||
title="Feature demonstration"
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowFullScreen
|
||||
/>
|
||||
</Frame>
|
||||
```
|
||||
|
||||
Screenshots work similarly - the frame provides visual polish and consistency:
|
||||
|
||||
```jsx
|
||||
<Frame>
|
||||
<img src="/path/to/screenshot.png" alt="Descriptive alt text" />
|
||||
</Frame>
|
||||
```
|
||||
|
||||
## Cards for Navigation and Overview
|
||||
|
||||
Cards excel at creating scannable overviews that link to detailed documentation. They're perfect for feature listings, getting started guides, or any section where users need to choose their path.
|
||||
|
||||
Use the two-column layout for related features:
|
||||
|
||||
```jsx
|
||||
<Columns cols={2}>
|
||||
<Card title="Feature Name" icon="relevant-icon" href="/link/to/docs">
|
||||
Brief description that explains what this feature does and why someone would use it.
|
||||
</Card>
|
||||
|
||||
<Card title="Related Feature" icon="another-icon" href="/another/link">
|
||||
Another concise explanation that helps users understand the value proposition.
|
||||
</Card>
|
||||
</Columns>
|
||||
```
|
||||
|
||||
The key is writing card descriptions that are informative enough to help users decide whether to click through, but concise enough to scan quickly. Each card should answer "what does this do?" and "why would I need this?"
|
||||
|
||||
## Tips and Notes for Context
|
||||
|
||||
Use `<Tip>` components for helpful information that enhances the main content without cluttering it:
|
||||
|
||||
```jsx
|
||||
<Tip>
|
||||
Pro tip: You can combine multiple @ mentions in a single message to give Cline
|
||||
comprehensive context about your issue.
|
||||
</Tip>
|
||||
```
|
||||
|
||||
`<Note>` components work well for important caveats or technical limitations:
|
||||
|
||||
```jsx
|
||||
<Note>
|
||||
Due to VS Code limitations, some features require specific settings to work properly.
|
||||
</Note>
|
||||
```
|
||||
|
||||
`<Info>` is also cool:
|
||||
|
||||
<Info>
|
||||
**Quick Fix**: If you're experiencing terminal issues, try switching to a simpler shell like `bash` in the Cline settings.
|
||||
This resolves 90% of terminal integration problems.
|
||||
</Info>
|
||||
|
||||
**Never** fall into that awful **Bold Text** - description pattern that we specifically identified as bad writing. The content should flow naturally as connected thoughts rather than feeling like a templated AI response with forced formatting.
|
||||
|
||||
|
||||
## When to Use Bullet Points and Numbered Lists Strategically
|
||||
|
||||
Bullet points serve functional purposes - use them for:
|
||||
|
||||
**Sequential actions or troubleshooting steps** where users need to follow a specific order:
|
||||
1. Install the extension
|
||||
2. Restart VSCode
|
||||
3. Check the settings panel
|
||||
|
||||
**Lists of related options** where users need to choose one approach:
|
||||
- Try PowerShell 7 for the most reliable experience
|
||||
- Switch to Command Prompt if you're still having issues
|
||||
- Use WSL Bash for Linux compatibility
|
||||
|
||||
**Quick reference items** that users might need to scan quickly when problem-solving.
|
||||
|
||||
**Improving Visual Hierarchy** when there's a wall of text - that's a good time to introduce bullet points or numbered lists.
|
||||
|
||||
Each bulleted item or numbered list should be a discrete action or piece of information that benefits from being visually separated. This is a key weapon you can employ when going for that artwork experience I mentioned earlier.
|
||||
|
||||
<good_example_of_bullet_points>
|
||||
## Finding and Configuring Terminal Settings
|
||||
|
||||
You can access Cline's terminal settings by clicking the settings icon in the Cline sidebar, then navigating to the Terminal section. These settings control how Cline interacts with your system's terminal.
|
||||
|
||||
- The **Default Terminal Profile** setting determines which shell Cline uses for executing commands. If you're experiencing issues, this is usually the first thing to change. I personally keep this set to `bash` on all my systems because it's the most reliable option, even though I use `zsh` for my regular terminal work.
|
||||
|
||||
- **Shell Integration Timeout** controls how long Cline waits for the terminal to become ready. The default is 4 seconds, but if you have a heavy shell configuration (lots of plugins, slow startup scripts), you might need to increase this to 10 or even 15 seconds. I've found that WSL environments and SSH connections often need longer timeouts.
|
||||
|
||||
- The **Enable Aggressive Terminal Reuse** setting determines whether Cline tries to reuse existing terminals even when they're not in the correct directory. When this causes problems (commands running in the wrong directory, virtual environment issues), disabling it creates more terminal instances but ensures each command runs in a clean state.
|
||||
|
||||
- **Terminal Output Line Limit** sets how many lines of output Cline will read from commands. The default of 500 lines works for most cases, but you might need to adjust it. For verbose build outputs, increase it. For commands with progress bars that spam thousands of lines, decrease it to around 100 to avoid consuming too many tokens.
|
||||
</good_example_of_bullet_points>
|
||||
|
||||
## Write Like a Human, Not an AI
|
||||
|
||||
Keep it short and direct. If you can say something in fewer words, do it. Long explanations often confuse more than they help.
|
||||
|
||||
Use normal sentence lengths. Mix short and long sentences naturally, like you would when talking to someone. Avoid those meandering compound sentences that go on forever.
|
||||
|
||||
Cut the corporate speak. Instead of "utilize," say "use." Instead of "in order to," just say "to." Write like you're explaining something to a colleague, not drafting a press release.
|
||||
|
||||
Don't over-explain obvious things. If users are reading documentation, they probably understand basic concepts. Respect their intelligence.
|
||||
|
||||
## Never use em dashes or emojis
|
||||
|
||||
That's pretty much it. Never use them. Only AI writes with em dashes or emojis.
|
||||
|
||||
# Anthropomorphizing Cline
|
||||
|
||||
When referring to Cline, always call him a "him" not an "it".
|
||||
|
||||
Bad example:
|
||||
- When Cline can’t execute commands or read their output, you lose access to one of its most powerful capabilities.
|
||||
|
||||
Good Example:
|
||||
|
||||
- When Cline can’t execute commands or read their output, you lose access to one of his most powerful capabilities.
|
||||
|
||||
# Using "I" when sharing your workflow
|
||||
|
||||
Adding a personal touch goes a long way. There are great examples in the docs currently where I use "I" to share how I personally use cline, from dev to dev. It's a great technique.
|
||||
|
||||
# Crosslinking relevant documentation pages
|
||||
|
||||
Make sure you crosslink when you're done writing the docs. If there are relevant docs, just link to them.
|
||||
|
||||
# Brevity is the soul of wit
|
||||
|
||||
Don't ramble if you don't need to. Use bullet points and numbered lists. Keep things easy to read.
|
||||
|
||||
<bad_example>
|
||||
|
||||
When Cline can't execute commands or read their output, you lose access to one of his most powerful capabilities. Terminal integration problems are frustrating, but they're usually fixable with a few simple changes.
|
||||
|
||||
## The Most Common Problem: Shell Integration Issues
|
||||
|
||||
If you're seeing "Shell integration unavailable" or Cline isn't getting command output, the issue is almost always your shell configuration. Complex shell setups with custom prompts, plugins, and fancy configurations can interfere with VSCode's terminal integration.
|
||||
|
||||
**Switch to bash first.** This fixes the problem 90% of the time. Navigate to Cline Settings → Terminal → Default Terminal Profile and select "bash" from the dropdown. Restart VSCode after making this change.
|
||||
|
||||
Still having issues? Try increasing the shell integration timeout. Go to Cline Settings → Terminal → Shell Integration Timeout and change it from 4 seconds to 10 seconds. Heavy shell configurations need more time to initialize properly.
|
||||
|
||||
If commands are running in the wrong directories or you're seeing weird behavior, disable aggressive terminal reuse. In Cline Settings → Terminal, uncheck "Enable aggressive terminal reuse." This creates more terminal instances but ensures each command runs in a clean environment.
|
||||
|
||||
|
||||
</bad_exaxmple>
|
||||
|
||||
The first part is total filler, useless to any serious developer. You can tell it's written by a non technical person that doesn't value clean, straightforward information.
|
||||
|
||||
<good_example>
|
||||
## Shell Integration Issues
|
||||
|
||||
If you're seeing "Shell integration unavailable" or Cline can't read command output, your shell configuration is interfering with VSCode's terminal integration.
|
||||
|
||||
**Switch to bash first.** Go to Cline Settings → Terminal → Default Terminal Profile and select "bash." This fixes 90% of problems.
|
||||
|
||||
Still broken? Try these:
|
||||
- Increase shell integration timeout to 10 seconds in Cline Settings → Terminal
|
||||
- Disable "aggressive terminal reuse" if commands run in wrong directories
|
||||
- Restart VSCode after making changes
|
||||
</good_example>
|
||||
|
||||
The good version cuts straight to the problem and solution. No hand-holding, no emotional language about frustration, just the facts: what's wrong, how to fix it, what to try next. Respects that developers want information, not sympathy.RetryClaude can make mistakes. Please double-check responses.
|
||||
|
||||
ALWAYS consider your audience. And your audience is devs who don't want their time wasted. Give them the info. I cannot stress this enough. Use bullet points and numbered lists. Prose is good, but every word should actually mean something to the dev reading it.
|
||||
|
||||
# Lastly, before you start writing docs
|
||||
|
||||
1. Internalize these guidelines. I mean it.
|
||||
|
||||
2. Read `docs/docs.json` and get an understanding of the structure of the docs. This will come in handly at the end when you're doing a final pass so you can cross link to docs where relevant.
|
||||
|
||||
3. Read some good examples that I personally wrote and am proud of:
|
||||
|
||||
- docs/features/slash-commands/workflows.mdx
|
||||
- docs/features/slash-commands/new-task.mdx
|
||||
- docs/features/at-mentions/overview.mdx
|
||||
- docs/features/drag-and-drop.mdx
|
||||
|
||||
4. If the user specifies any other instructions make sure you follow them.
|
||||
@@ -0,0 +1,6 @@
|
||||
[codespell]
|
||||
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
|
||||
skip = .git*,*.svg,package-lock.json,*.css,.codespellrc,locales
|
||||
check-hidden = true
|
||||
ignore-regex = (\b(optIn|isTaller)\b|https://\S+)
|
||||
# ignore-words-list =
|
||||
@@ -1,49 +0,0 @@
|
||||
# THIS IS AUTOGENERATED. DO NOT EDIT MANUALLY
|
||||
version = 1
|
||||
name = "cline"
|
||||
|
||||
[setup]
|
||||
script = '''
|
||||
if [ ! -d "node_modules" ]; then
|
||||
MAIN_WORKTREE="$(git worktree list | head -n1 | awk '{print $1}')"
|
||||
ln -s "$MAIN_WORKTREE/node_modules" node_modules
|
||||
ln -s "$MAIN_WORKTREE/webview-ui/node_modules" webview-ui/node_modules
|
||||
fi
|
||||
'''
|
||||
|
||||
[[actions]]
|
||||
name = "VS Code"
|
||||
icon = "run"
|
||||
command = "chmod +x ./scripts/run-extension-host.sh && ./scripts/run-extension-host.sh production"
|
||||
|
||||
[[actions]]
|
||||
name = "CLI"
|
||||
icon = "run"
|
||||
command = '''
|
||||
npm run cli:build
|
||||
npm run cli:run
|
||||
'''
|
||||
|
||||
[[actions]]
|
||||
name = "npm install"
|
||||
icon = "tool"
|
||||
command = '''
|
||||
rm node_modules
|
||||
rm webview-ui/node_modules
|
||||
npm run install:all
|
||||
'''
|
||||
|
||||
[[actions]]
|
||||
name = "pull main"
|
||||
icon = "tool"
|
||||
command = '''
|
||||
git fetch origin main
|
||||
|
||||
if ! git merge-base --is-ancestor main origin/main; then
|
||||
echo "Local main has commits not on origin/main. Aborting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git update-ref refs/heads/main refs/remotes/origin/main
|
||||
echo "main updated to $(git rev-parse --short main)"
|
||||
'''
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
# Cline Development Environment Variables
|
||||
# Copy this file to .env and fill in your actual values
|
||||
# Values should be obtained from 1Password shared vault for development
|
||||
|
||||
# ============================================================================
|
||||
# DEVELOPMENT FLAGS
|
||||
# Recomend not changing these unless you know what you're doing they are set by the launch.json normally
|
||||
# ============================================================================
|
||||
# IS_DEV=true
|
||||
# CLINE_ENVIRONMENT=local
|
||||
|
||||
# ============================================================================
|
||||
# POSTHOG TELEMETRY (Existing)
|
||||
# ============================================================================
|
||||
# Get these values from 1Password shared vault
|
||||
TELEMETRY_SERVICE_API_KEY=your-posthog-telemetry-api-key
|
||||
ERROR_SERVICE_API_KEY=your-posthog-error-tracking-api-key
|
||||
|
||||
# ============================================================================
|
||||
# OPENTELEMETRY (Optional - for advanced telemetry)
|
||||
# ============================================================================
|
||||
# OpenTelemetry provides flexible telemetry collection with multiple export options
|
||||
# Can run alongside PostHog or independently
|
||||
# Primary focus: Logs (events), with optional metrics support
|
||||
|
||||
# Enable OpenTelemetry (set to 1 to enable)
|
||||
# OTEL_TELEMETRY_ENABLED=1
|
||||
|
||||
# Exporters: "console" for local debugging, "otlp" for remote collector
|
||||
# Logs are the primary signal (recommended)
|
||||
# OTEL_LOGS_EXPORTER=console
|
||||
# OTEL_METRICS_EXPORTER=otlp
|
||||
|
||||
# OTLP Protocol: "grpc", "http/json", or "http/protobuf"
|
||||
# OTEL_EXPORTER_OTLP_PROTOCOL=grpc
|
||||
|
||||
# OTLP Endpoint (without /v1/logs or /v1/metrics path - auto-appended)
|
||||
# For gRPC: use "localhost:4317" (no http:// prefix)
|
||||
# For HTTP: use "http://localhost:4318"
|
||||
# OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
|
||||
|
||||
# OTLP Headers (for authentication, e.g., bearer tokens)
|
||||
# OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer your-token-here
|
||||
|
||||
# Use insecure gRPC connections (for local testing only, NOT for production)
|
||||
# OTEL_EXPORTER_OTLP_INSECURE=true
|
||||
|
||||
# Metric export interval in milliseconds (default: 60000)
|
||||
# OTEL_METRIC_EXPORT_INTERVAL=10000
|
||||
|
||||
# Batch configuration for logs (optional)
|
||||
# OTEL_LOG_BATCH_SIZE=512 # Max logs per batch (default: 512)
|
||||
# OTEL_LOG_BATCH_TIMEOUT=5000 # Max wait time in ms (default: 5000)
|
||||
# OTEL_LOG_MAX_QUEUE_SIZE=2048 # Max queue size (default: 2048)
|
||||
|
||||
# Enable detailed export diagnostics (for debugging)
|
||||
# TEL_DEBUG_DIAGNOSTICS=true
|
||||
|
||||
# Advanced: Separate endpoints for metrics and logs (optional)
|
||||
# OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf
|
||||
# OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://metrics.example.com:4318
|
||||
# OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=grpc
|
||||
# OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=logs.example.com:4317
|
||||
|
||||
# Example configurations:
|
||||
#
|
||||
# Console debugging (logs only):
|
||||
# OTEL_TELEMETRY_ENABLED=true
|
||||
# OTEL_LOGS_EXPORTER=console
|
||||
# TEL_DEBUG_DIAGNOSTICS=true
|
||||
#
|
||||
# OTLP with gRPC (insecure, for local testing):
|
||||
# OTEL_TELEMETRY_ENABLED=true
|
||||
# OTEL_LOGS_EXPORTER=otlp
|
||||
# OTEL_EXPORTER_OTLP_PROTOCOL=grpc
|
||||
# OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317
|
||||
# OTEL_EXPORTER_OTLP_INSECURE=true
|
||||
# OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer your-token
|
||||
#
|
||||
# OTLP with HTTP/JSON (production):
|
||||
# OTEL_TELEMETRY_ENABLED=true
|
||||
# OTEL_LOGS_EXPORTER=otlp
|
||||
# OTEL_EXPORTER_OTLP_PROTOCOL=http/json
|
||||
# OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.com
|
||||
# OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer your-token
|
||||
|
||||
# ============================================================================
|
||||
# OBJECT STORE CONFIGURATION
|
||||
# ============================================================================
|
||||
# TO ENABLE S3 OR R2 STORAGE, UNCOMMENT AND FILL IN THE FOLLOWING:
|
||||
# CLINE_STORAGE_ADAPTER="s3" # Options: "s3" or "r2"
|
||||
# CLINE_STORAGE_BUCKET="cline"
|
||||
# CLINE_STORAGE_ACCESS_KEY_ID="key"
|
||||
# CLINE_STORAGE_SECRET_ACCESS_KEY="secrets"
|
||||
#
|
||||
# [OPTIONAL FIELDS FOR R2]
|
||||
# CLINE_STORAGE_ACCOUNT_ID = "account-id"
|
||||
# Default R2 endpoint (if not set): "https://<CLINE_STORAGE_ACCOUNT_ID>.r2.cloudflarestorage.com"
|
||||
# CLINE_STORAGE_ENDPOINT = "http://localhost:8333"
|
||||
#
|
||||
# [OPTIONAL FIELDS FOR S3]
|
||||
# CLINE_STORAGE_REGION = "us-west-1" # AWS Bucket Region (default: "us-east-1")
|
||||
# Default S3 endpoint (if not set): "https://s3.<CLINE_STORAGE_REGION>.amazonaws.com"
|
||||
# CLINE_STORAGE_ENDPOINT = "http://localhost:8333"
|
||||
#
|
||||
# [OPTIONAL FIELDS FOR ALL STORAGE TYPES]
|
||||
# CLINE_STORAGE_SYNC_INTERVAL_MS = 30000 # Interval for sync worker in milliseconds
|
||||
# CLINE_STORAGE_SYNC_MAX_RETRIES = 5 # Max retries for failed sync operations
|
||||
# CLINE_STORAGE_SYNC_BATCH_SIZE = 10 # Number of files to sync in each batch
|
||||
# CLINE_STORAGE_SYNC_BACKFILL_ENABLED = false # Enable backfill of existing data on startup
|
||||
|
||||
# ============================================================================
|
||||
# OPTIONAL DEVELOPMENT SETTINGS
|
||||
# ============================================================================
|
||||
# Uncomment and modify as needed for development
|
||||
|
||||
# Multi-root workspace debugging
|
||||
# MULTI_ROOT_TRACE=true
|
||||
|
||||
# gRPC recorder for testing
|
||||
# GRPC_RECORDER_ENABLED=true
|
||||
# GRPC_RECORDER_FILE_NAME=test-recording
|
||||
|
||||
# Test mode
|
||||
# E2E_TEST=true
|
||||
# IS_TEST=true
|
||||
|
||||
# ============================================================================
|
||||
# USAGE INSTRUCTIONS
|
||||
# ============================================================================
|
||||
# 1. Copy this file: cp .env.example .env
|
||||
# 2. Get PostHog keys from 1Password shared vault
|
||||
# 3. Update the values in .env
|
||||
# 4. The .env file is gitignored for security
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"warn",
|
||||
{
|
||||
"selector": "import",
|
||||
"format": ["camelCase", "PascalCase"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/semi": "off",
|
||||
"curly": "warn",
|
||||
"eqeqeq": "warn",
|
||||
"no-throw-literal": "warn",
|
||||
"semi": "off",
|
||||
"react-hooks/exhaustive-deps": "off"
|
||||
},
|
||||
"ignorePatterns": ["out", "dist", "**/*.d.ts"]
|
||||
}
|
||||
@@ -1,5 +1,2 @@
|
||||
demo.gif filter=lfs diff=lfs merge=lfs -text
|
||||
assets/docs/demo.gif filter=lfs diff=lfs merge=lfs -text
|
||||
webview-ui/src/assets/cline_kanban_demo.webm filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
* text=auto eol=lf
|
||||
|
||||
+1
-2
@@ -1,2 +1 @@
|
||||
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @candieduniverse
|
||||
/README.md @saoudrizwan @juanpflores
|
||||
* @saoudrizwan @ocasta181 @NightTrek @pashpashpash @dcbartlett @saito-sv @Garoth
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
name: 🐛 Bug Report
|
||||
description: File a bug report
|
||||
labels: ['bug']
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Important:** All bug reports must be reproducible using Claude Sonnet 4.5. Cline uses complex prompts so less capable models may not work as expected.
|
||||
- type: dropdown
|
||||
id: plugin-type
|
||||
attributes:
|
||||
label: Plugin Type
|
||||
description: Which plugin are you reporting a bug for?
|
||||
options:
|
||||
- VSCode Extension
|
||||
- JetBrains Plugin
|
||||
- CLI
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: cline-version
|
||||
attributes:
|
||||
label: Cline Version
|
||||
description: What version of Cline are you using? (You can find this at the bottom of the Settings view)
|
||||
placeholder: 'e.g., 1.2.3'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: How do you trigger this bug? Please walk us through it step by step.
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: provider-model
|
||||
attributes:
|
||||
label: Provider/Model
|
||||
description: What provider and model were you using when the issue occurred?
|
||||
placeholder: 'e.g., cline:anthropic/claude-sonnet-4.5, gemini:gemini-2.5-pro-exp-03-25'
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System Information
|
||||
description: What operating system and hardware are you using?
|
||||
placeholder: |
|
||||
Operating System: Windows 11, macOS Sonoma, Ubuntu 22.04, etc.
|
||||
Hardware: CPU, GPU, RAM specifications if relevant
|
||||
e.g.,
|
||||
OS: Windows 11
|
||||
CPU: Intel Core i7-11700K
|
||||
GPU: NVIDIA GeForce RTX 3070
|
||||
RAM: 32GB DDR4
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
**Important:** All bug reports must be reproducible using Claude 3.5 Sonnet. Cline uses complex prompts so less capable models may not work as expected.
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: How do you trigger this bug? Please walk us through it step by step.
|
||||
value: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant API REQUEST output
|
||||
description: Please copy and paste any relevant output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: input
|
||||
id: provider-model
|
||||
attributes:
|
||||
label: Provider/Model
|
||||
description: What provider and model were you using when the issue occurred?
|
||||
placeholder: "e.g., cline:anthropic/claude-3.7-sonnet, gemini:gemini-2.5-pro-exp-03-25"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: operating-system
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: What operating system are you using?
|
||||
placeholder: "e.g., Windows 11, macOS Sonoma, Ubuntu 22.04"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System Info
|
||||
description: What system information is relevant to the issue?
|
||||
placeholder: "e.g., CPU: Intel Core i7-11700K, GPU: NVIDIA GeForce RTX 3070, RAM: 32GB DDR4"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: cline-version
|
||||
attributes:
|
||||
label: Cline Version
|
||||
description: What version of Cline are you using? (You can find this at the bottom of the Settings view)
|
||||
placeholder: "e.g., 1.2.3"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here, such as screenshots or related issues.
|
||||
|
||||
@@ -6,3 +6,6 @@ contact_links:
|
||||
- name: 👋 Cline Discord
|
||||
url: https://discord.gg/cline
|
||||
about: Join our Discord community for discussions and support
|
||||
- name: ❓ Other Questions?
|
||||
url: https://x.com/sdrzn
|
||||
about: Contact the developer on X @sdrzn for other inquiries
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
# Copilot Instructions for Cline
|
||||
|
||||
This is a VS Code extension. Read `.clinerules/general.md` for tribal knowledge and nuanced patterns.
|
||||
|
||||
## Architecture
|
||||
- **Core** (`src/`): `extension.ts` → `WebviewProvider` → `Controller` (single source of truth) → `Task` (agent loop).
|
||||
- **Webview** (`webview-ui/`): React/Vite app. State via `ExtensionStateContext.tsx`, synced through message passing.
|
||||
- **CLI** (`cli/`): React Ink terminal UI sharing core logic. Update CLI when changing webview features.
|
||||
- **Communication**: Protobuf-defined gRPC-like protocol over VS Code message passing. Schemas in `proto/`.
|
||||
- **MCP**: `src/services/mcp/McpHub.ts`.
|
||||
|
||||
## Build & Test (Critical — non-obvious commands)
|
||||
- **Build**: `npm run compile` — NOT `npm run build`.
|
||||
- **Watch**: `npm run watch` (extension + webview).
|
||||
- **Protos**: `npm run protos` — run **immediately** after any `.proto` change. Generates into `src/shared/proto/`, `src/generated/`.
|
||||
- **Tests**: `npm run test:unit`. After prompt/tool changes: `UPDATE_SNAPSHOTS=true npm run test:unit`.
|
||||
|
||||
## Protobuf RPC Workflow (4 steps)
|
||||
1. **Define** in `proto/cline/*.proto`. Naming: `PascalCaseService`, `camelCase` RPCs, `PascalCase` Messages. Use `common.proto` shared types for simple data.
|
||||
2. **Generate**: `npm run protos`.
|
||||
3. **Backend handler**: `src/core/controller/<domain>/`.
|
||||
4. **Frontend call**: `UiServiceClient.myMethod(Request.create({...}))`.
|
||||
- Adding enums (e.g. `ClineSay`) → also update `src/shared/proto-conversions/cline-message.ts`.
|
||||
|
||||
## Adding API Providers (silent failure risk)
|
||||
Three proto conversion updates are **required** or the provider silently resets to Anthropic:
|
||||
1. `proto/cline/models.proto` — add to `ApiProvider` enum.
|
||||
2. `convertApiProviderToProto()` in `src/shared/proto-conversions/models/api-configuration-conversion.ts`.
|
||||
3. `convertProtoToApiProvider()` in the same file.
|
||||
|
||||
Also update: `src/shared/api.ts`, `src/shared/providers/providers.json`, `src/core/api/index.ts`, `webview-ui/.../providerUtils.ts`, `webview-ui/.../validate.ts`, `webview-ui/.../ApiOptions.tsx`, and `cli/src/components/ModelPicker.tsx`.
|
||||
|
||||
For Responses API providers: add to `isNextGenModelProvider()` in `src/utils/model-utils.ts` and set `apiFormat: ApiFormat.OPENAI_RESPONSES` on models.
|
||||
|
||||
## Adding Tools to System Prompt (5+ file chain)
|
||||
1. Add enum to `ClineDefaultTool` in `src/shared/tools.ts`.
|
||||
2. Create definition in `src/core/prompts/system-prompt/tools/` (export `[GENERIC]` minimum).
|
||||
3. Register in `src/core/prompts/system-prompt/tools/init.ts`.
|
||||
4. Whitelist in `src/core/prompts/system-prompt/variants/*/config.ts` for each model family.
|
||||
5. Handler in `src/core/task/tools/handlers/`, wire in `ToolExecutor.ts`.
|
||||
6. If tool has UI: add `ClineSay` enum in proto → `ExtensionMessage.ts` → `cline-message.ts` → `ChatRow.tsx`.
|
||||
7. Regenerate snapshots: `UPDATE_SNAPSHOTS=true npm run test:unit`.
|
||||
|
||||
## Modifying System Prompt
|
||||
Modular: `components/` (shared) + `variants/` (model-specific) + `templates/` (`{{PLACEHOLDER}}`). Variants override components via `componentOverrides` in `config.ts` or custom `template.ts`. XS variant is heavily condensed inline. Always regenerate snapshots after changes.
|
||||
|
||||
## Global State Keys (silent failure risk)
|
||||
Adding a key requires: type in `src/shared/storage/state-keys.ts`, read via `context.globalState.get()` in `src/core/storage/utils/state-helpers.ts` `readGlobalStateFromDisk()`, and add to return object. Missing the `.get()` call compiles fine but value is always `undefined`.
|
||||
|
||||
## Slash Commands (3 places)
|
||||
- `src/core/slash-commands/index.ts` — definitions.
|
||||
- `src/core/prompts/commands.ts` — system prompt integration.
|
||||
- `webview-ui/src/utils/slash-commands.ts` — webview autocomplete.
|
||||
|
||||
## Conventions
|
||||
- **Paths**: Always use `src/utils/path` helpers (`toPosixString`) for cross-platform compatibility.
|
||||
- **Logging**: `src/shared/services/Logger.ts`.
|
||||
- **Feature flags**: See PR #7566 as reference pattern.
|
||||
@@ -1,46 +1,10 @@
|
||||
<!--
|
||||
Thank you for contributing to Cline!
|
||||
|
||||
⚠️ Important: Before submitting this PR, please ensure you have:
|
||||
- For feature requests: Created a discussion in our Feature Requests discussions board https://github.com/cline/cline/discussions/categories/feature-requests and received approval from core maintainers before implementation
|
||||
- For all changes: Link the associated issue/discussion in the "Related Issue" section below
|
||||
|
||||
Limited exceptions:
|
||||
Small bug fixes, typo corrections, minor wording improvements, or simple type fixes that don't change functionality may be submitted directly without prior discussion.
|
||||
|
||||
Why this requirement?
|
||||
We deeply appreciate all community contributions - they are essential to Cline's success! To ensure the best use of everyone's time and maintain project direction, we use our Feature Requests discussions board to gauge community interest and validate feature ideas before implementation begins. This helps us focus development efforts on features that will benefit the most users.
|
||||
-->
|
||||
|
||||
### Related Issue
|
||||
|
||||
<!-- Replace XXXX with the issue number that this PR addresses -->
|
||||
**Issue:** #XXXX
|
||||
|
||||
### Description
|
||||
|
||||
<!--
|
||||
Help reviewers understand your changes by making this PR readable and well-organized:
|
||||
|
||||
- What problem does this PR solve?
|
||||
- Why were these changes introduced and what purpose do they serve?
|
||||
- For larger changes, provide context about your approach and reasoning
|
||||
|
||||
Small PRs may need minimal description, but larger changes benefit from explaining where you're coming from. Much of this context can be in the linked issue above, so feel free to reference it rather than repeating everything here.
|
||||
-->
|
||||
<!-- Describe your changes in detail. What problem does this PR solve? -->
|
||||
|
||||
### Test Procedure
|
||||
|
||||
<!--
|
||||
Please walk us through your testing approach and thought process. This helps reviewers understand that you've thoroughly considered the impact of your changes:
|
||||
|
||||
- How did you test this change?
|
||||
- What could potentially break and how did you verify it doesn't?
|
||||
- What existing functionality might be affected and how did you check it still works?
|
||||
- Why are you confident this is ready for merge?
|
||||
|
||||
We're not looking for exhaustive documentation - just evidence that you've thought through the implications of your changes and tested accordingly.
|
||||
-->
|
||||
<!-- How did you test this? Are you confident that it will not introduce bugs? If so, why? -->
|
||||
|
||||
### Type of Change
|
||||
|
||||
@@ -60,19 +24,12 @@ We're not looking for exhaustive documentation - just evidence that you've thoug
|
||||
|
||||
- [ ] Changes are limited to a single feature, bugfix or chore (split larger changes into separate PRs)
|
||||
- [ ] Tests are passing (`npm test`) and code is formatted and linted (`npm run format && npm run lint`)
|
||||
- [ ] I have created a changeset using `npm run changeset` (required for user-facing changes)
|
||||
- [ ] I have reviewed [contributor guidelines](https://github.com/cline/cline/blob/main/CONTRIBUTING.md)
|
||||
|
||||
### Screenshots
|
||||
|
||||
<!--
|
||||
Help reviewers quickly understand your changes:
|
||||
|
||||
- **UI Changes**: Please include screenshots showing before/after states
|
||||
- **Complex Workflows**: Consider uploading a screen recording (video) if your changes involve multiple steps or state transitions
|
||||
- **Backend Changes**: Not required, but feel free to include terminal output or other evidence that demonstrates functionality
|
||||
|
||||
This helps reviewers see what you've built without having to pull down and test your branch first.
|
||||
-->
|
||||
<!-- For UI changes, add screenshots here -->
|
||||
|
||||
### Additional Notes
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
This script updates a specific version's release notes section in CHANGELOG.md with new content
|
||||
or reformats existing content.
|
||||
|
||||
The script:
|
||||
1. Takes a version number, changelog path, and optionally new content as input from environment variables
|
||||
2. Finds the section in the changelog for the specified version
|
||||
3. Either:
|
||||
a) Replaces the content with new content if provided, or
|
||||
b) Reformats existing content by:
|
||||
- Removing the first two lines of the changeset format
|
||||
- Ensuring version numbers are wrapped in square brackets
|
||||
4. Writes the updated changelog back to the file
|
||||
|
||||
Environment Variables:
|
||||
CHANGELOG_PATH: Path to the changelog file (defaults to 'CHANGELOG.md')
|
||||
VERSION: The version number to update/format
|
||||
PREV_VERSION: The previous version number (used to locate section boundaries)
|
||||
NEW_CONTENT: Optional new content to insert for this version
|
||||
"""
|
||||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
|
||||
CHANGELOG_PATH = os.environ.get("CHANGELOG_PATH", "CHANGELOG.md")
|
||||
VERSION = os.environ['VERSION']
|
||||
PREV_VERSION = os.environ.get("PREV_VERSION", "")
|
||||
NEW_CONTENT = os.environ.get("NEW_CONTENT", "")
|
||||
|
||||
def overwrite_changelog_section(changelog_text: str, new_content: str):
|
||||
# Find the section for the specified version
|
||||
version_pattern = f"## {VERSION}\n"
|
||||
unformmatted_prev_version_pattern = f"## {PREV_VERSION}\n"
|
||||
prev_version_pattern = f"## [{PREV_VERSION}]\n"
|
||||
print(f"latest version: {VERSION}")
|
||||
print(f"prev_version: {PREV_VERSION}")
|
||||
|
||||
notes_start_index = changelog_text.find(version_pattern) + len(version_pattern)
|
||||
notes_end_index = changelog_text.find(prev_version_pattern, notes_start_index) if PREV_VERSION and (prev_version_pattern in changelog_text or unformmatted_prev_version_pattern in changelog_text) else len(changelog_text)
|
||||
|
||||
if new_content:
|
||||
return changelog_text[:notes_start_index] + f"{new_content}\n" + changelog_text[notes_end_index:]
|
||||
else:
|
||||
changeset_lines = changelog_text[notes_start_index:notes_end_index].split("\n")
|
||||
filtered_lines = []
|
||||
for line in changeset_lines:
|
||||
# If the previous line is a changeset format
|
||||
if len(filtered_lines) > 1 and filtered_lines[-1].startswith("### "):
|
||||
# Remove the last two lines from the filted_lines
|
||||
filtered_lines.pop()
|
||||
filtered_lines.pop()
|
||||
else:
|
||||
filtered_lines.append(line.strip())
|
||||
|
||||
# Prepend a new line to the first line of filtered_lines
|
||||
if filtered_lines:
|
||||
filtered_lines[0] = "\n" + filtered_lines[0]
|
||||
|
||||
# Print filted_lines wiht a "\n" at the end of each line
|
||||
for line in filtered_lines:
|
||||
print(line.strip())
|
||||
|
||||
parsed_lines = "\n".join(line for line in filtered_lines)
|
||||
updated_changelog = changelog_text[:notes_start_index] + parsed_lines + changelog_text[notes_end_index:]
|
||||
return updated_changelog
|
||||
|
||||
with open(CHANGELOG_PATH, 'r') as f:
|
||||
changelog_content = f.read()
|
||||
|
||||
new_changelog = overwrite_changelog_section(changelog_content, NEW_CONTENT)
|
||||
# print("----------------------------------------------------------------------------------")
|
||||
# print(new_changelog)
|
||||
# print("----------------------------------------------------------------------------------")
|
||||
# Write back to CHANGELOG.md
|
||||
with open(CHANGELOG_PATH, 'w') as f:
|
||||
f.write(new_changelog)
|
||||
|
||||
print(f"{CHANGELOG_PATH} updated successfully!")
|
||||
@@ -0,0 +1,112 @@
|
||||
name: Changeset Converter
|
||||
run-name: Changeset Conversion
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
env:
|
||||
REPO_PATH: ${{ github.repository }}
|
||||
GIT_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
|
||||
NODE_VERSION: 20.18.1
|
||||
|
||||
jobs:
|
||||
# Job 1: Create version bump PR when changesets are merged to main
|
||||
changeset-pr-version-bump:
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.base.ref == 'main' &&
|
||||
github.actor != 'github-actions'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check user for team affiliation
|
||||
id: team_check
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: morfien101/actions-authorized-user@4a3cfbf0bcb3cafe4a71710a278920c5d94bb38b
|
||||
with:
|
||||
username: ${{ github.actor }}
|
||||
team: "deployer"
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check if user is authorized
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
run: |
|
||||
if [ "${{ steps.team_check.outputs.authorized }}" != "true" ]; then
|
||||
echo "User is not authorized to run this workflow."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Git Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ env.GIT_REF }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install changeset
|
||||
|
||||
# Check if there are any new changesets to process
|
||||
- name: Check for changesets
|
||||
id: check-changesets
|
||||
run: |
|
||||
NEW_CHANGESETS=$(find .changeset -name "*.md" ! -name "README.md" | wc -l | tr -d ' ')
|
||||
echo "Changesets diff with previous version: $NEW_CHANGESETS"
|
||||
echo "new_changesets=$NEW_CHANGESETS" >> $GITHUB_OUTPUT
|
||||
|
||||
# Create version bump PR using changesets/action if there are new changesets
|
||||
- name: Create Changeset Pull Request
|
||||
if: steps.check-changesets.outputs.new_changesets != '0'
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
commit: "changeset version bump"
|
||||
title: "Changeset version bump"
|
||||
version: npm run version-packages # This performs the changeset version bump
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Get current and previous versions to edit changelog entry
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=$(git show HEAD:package.json | jq -r '.version')
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
PREV_VERSION=$(git show origin/main:package.json | jq -r '.version')
|
||||
echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "version=$VERSION"
|
||||
echo "prev_version=$PREV_VERSION"
|
||||
|
||||
# Update CHANGELOG.md with proper format
|
||||
- name: Update Changelog Format
|
||||
env:
|
||||
VERSION: ${{ steps.get_version.outputs.version }}
|
||||
PREV_VERSION: ${{ steps.get_version.outputs.prev_version }}
|
||||
run: python .github/scripts/overwrite_changeset_changelog.py
|
||||
|
||||
# Commit and push changelog updates
|
||||
- name: Push Changelog updates to Pull Request
|
||||
run: |
|
||||
git config user.name "github-actions"
|
||||
git config user.email github-actions@github.com
|
||||
echo "Running git add and commit..."
|
||||
git add CHANGELOG.md
|
||||
git commit -m "Updating CHANGELOG.md format"
|
||||
git status
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Pushing to remote..."
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
git push origin $CURRENT_BRANCH
|
||||
@@ -1,83 +0,0 @@
|
||||
name: CLI TUI Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
cli-tui-tests:
|
||||
name: CLI TUI Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build CLI
|
||||
run: npm run cli:build
|
||||
|
||||
- name: Run TUI Tests
|
||||
id: tui_tests
|
||||
run: |
|
||||
npm run test:e2e:cli:tui 2>&1 | tee tui-test-output.log
|
||||
exit_code=${PIPESTATUS[0]}
|
||||
echo "tui_exit_code=$exit_code" >> $GITHUB_OUTPUT
|
||||
exit $exit_code
|
||||
|
||||
- name: Write failure summary
|
||||
if: always() && steps.tui_tests.outcome != 'success' && steps.tui_tests.outcome != 'skipped'
|
||||
run: |
|
||||
echo "## ❌ CLI TUI Tests Failed" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Step outcome:** \`${{ steps.tui_tests.outcome }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Test Output" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
if [ -f tui-test-output.log ]; then
|
||||
cat tui-test-output.log >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "(no test output captured — process may have been killed before output was flushed)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Debugging" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **TUI traces** are attached as artifacts below — download and inspect them to see terminal state at the point of failure." >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **To view a trace replay/Run a TUI Trace: ** run \`npx tui-test show-trace path/to/trace/file\` in your terminal" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Full test log** is also attached as an artifact." >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Tests run with \`retries: 2\` so any failure shown is a consistent failure, not a flake." >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Upload TUI traces
|
||||
if: always() && steps.tui_tests.outcome != 'success' && steps.tui_tests.outcome != 'skipped'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tui-test-traces
|
||||
path: tests/e2e/cli/tui-traces/
|
||||
retention-days: 14
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Upload test log
|
||||
if: always() && steps.tui_tests.outcome != 'success' && steps.tui_tests.outcome != 'skipped'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tui-test-log
|
||||
path: tui-test-output.log
|
||||
retention-days: 14
|
||||
if-no-files-found: warn
|
||||
@@ -1,85 +0,0 @@
|
||||
name: Smoke Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/core/**'
|
||||
- 'src/shared/**'
|
||||
- 'proto/**'
|
||||
- 'evals/**'
|
||||
- '.github/workflows/cline-evals-regression.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/core/**'
|
||||
- 'src/shared/**'
|
||||
- 'proto/**'
|
||||
- 'evals/**'
|
||||
- '.github/workflows/cline-evals-regression.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: smoke-tests-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
smoke-tests:
|
||||
name: Smoke Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build and install CLI
|
||||
run: |
|
||||
npm run protos
|
||||
cd cli && npm install && npm run build && npm link
|
||||
echo "$(npm config get prefix)/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Verify CLI
|
||||
run: cline --version
|
||||
|
||||
- name: Run smoke tests
|
||||
env:
|
||||
CLINE_API_KEY: ${{ secrets.CLINE_API_KEY }}
|
||||
run: |
|
||||
cline auth -p cline -k "$CLINE_API_KEY" -m "anthropic/claude-sonnet-4.5"
|
||||
max_attempts=3
|
||||
for attempt in $(seq 1 $max_attempts); do
|
||||
echo "::group::Attempt $attempt of $max_attempts"
|
||||
if npx tsx evals/smoke-tests/run-smoke-tests.ts --trials 1 --parallel; then
|
||||
echo "::endgroup::"
|
||||
echo "Smoke tests passed on attempt $attempt"
|
||||
exit 0
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
if [ $attempt -lt $max_attempts ]; then
|
||||
echo "::warning::Smoke tests failed on attempt $attempt, retrying..."
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
echo "::error::Smoke tests failed after $max_attempts attempts"
|
||||
exit 1
|
||||
|
||||
- name: Generate summary
|
||||
if: always()
|
||||
run: cat evals/smoke-tests/results/latest/summary.md >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: smoke-test-results-${{ github.run_id }}
|
||||
path: evals/smoke-tests/results/latest/
|
||||
retention-days: 30
|
||||
@@ -0,0 +1,28 @@
|
||||
# Codespell configuration is within .codespellrc
|
||||
---
|
||||
name: Codespell
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
if: false
|
||||
name: Check for spelling errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Annotate locations with typos
|
||||
uses: codespell-project/codespell-problem-matcher@v1
|
||||
- name: Codespell
|
||||
uses: codespell-project/actions-codespell@v2
|
||||
with:
|
||||
only_warn: 1
|
||||
@@ -1,111 +0,0 @@
|
||||
name: E2E Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize, ready_for_review]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- id: set-matrix
|
||||
run: |
|
||||
echo 'matrix=[{"runner":"ubuntu"},{"runner":"windows"},{"runner":"macos"}]' >> $GITHUB_OUTPUT
|
||||
|
||||
e2e:
|
||||
needs: matrix_prep
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.matrix_prep.outputs.matrix) }}
|
||||
runs-on: ${{ matrix.runner }}-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
# Cache root dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache root dependencies
|
||||
uses: actions/cache@v4
|
||||
id: root-cache
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache webview-ui dependencies
|
||||
uses: actions/cache@v4
|
||||
id: webview-cache
|
||||
with:
|
||||
path: webview-ui/node_modules
|
||||
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
|
||||
|
||||
# Cache VS Code installation
|
||||
- name: Cache VS Code
|
||||
uses: actions/cache@v4
|
||||
id: vscode-cache
|
||||
with:
|
||||
path: .vscode-test
|
||||
key: vscode-${{ runner.os }}-stable-${{ hashFiles('.vscode-test.mjs', 'package.json') }}
|
||||
restore-keys: |
|
||||
vscode-${{ runner.os }}-stable-
|
||||
|
||||
# Cache Playwright browsers
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@v4
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: |
|
||||
~/.cache/ms-playwright
|
||||
~/Library/Caches/ms-playwright
|
||||
~/AppData/Local/ms-playwright
|
||||
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
playwright-browsers-${{ runner.os }}-
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Install vsce
|
||||
run: npm install -g @vscode/vsce
|
||||
|
||||
- name: Install xvfb on Linux
|
||||
if: matrix.runner == 'ubuntu'
|
||||
run: sudo apt-get update && sudo apt-get install -y xvfb
|
||||
|
||||
# Run optimized E2E tests (eliminates redundant builds)
|
||||
- name: Run E2E tests - Linux
|
||||
if: matrix.runner == 'ubuntu'
|
||||
run: xvfb-run -a npm run test:e2e:optimal
|
||||
|
||||
- name: Run E2E tests - Non-Linux
|
||||
if: matrix.runner != 'ubuntu'
|
||||
run: npm run test:e2e:optimal
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
name: playwright-recordings-${{ matrix.runner }}
|
||||
path: |
|
||||
test-results/playwright/
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Auto-label Issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
|
||||
jobs:
|
||||
label:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const body = context.payload.issue.body || '';
|
||||
const labels = context.payload.issue.labels.map(l => l.name);
|
||||
|
||||
// Check if JetBrains Plugin is selected
|
||||
if (body.match(/###\s*Plugin Type\s*\n+JetBrains Plugin/i)) {
|
||||
if (!labels.includes('JetBrains')) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
labels: ['JetBrains']
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check if VSCode Extension is selected
|
||||
if (body.match(/###\s*Plugin Type\s*\n+VSCode Extension/i)) {
|
||||
if (!labels.includes('VS Code')) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
labels: ['VS Code']
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check if CLI is selected
|
||||
if (body.match(/###\s*Plugin Type\s*\n+CLI/i)) {
|
||||
if (!labels.includes('CLI')) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
labels: ['CLI']
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
name: Publish NPM Release
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
confirm_publish:
|
||||
description: 'Type "publish" to confirm you want to publish to NPM'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write # Required for pushing tags
|
||||
id-token: write # Required for npm trusted publishing (OIDC)
|
||||
checks: write # Required by test workflow
|
||||
pull-requests: write # Required by test workflow
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: ./.github/workflows/test.yml
|
||||
|
||||
publish-npm-release:
|
||||
needs: test
|
||||
name: Publish Cline CLI to NPM
|
||||
if: github.repository == 'cline/cline' && github.ref == 'refs/heads/main' && inputs.confirm_publish == 'publish'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24.x"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Install root dependencies and CLI dependencies
|
||||
run: npm ci --include=optional # this will also install cli deps because "cli" in included in root package.json workspaces field
|
||||
|
||||
- name: Generate Protos
|
||||
run: npm run protos
|
||||
|
||||
- name: Read release version
|
||||
id: version
|
||||
run: |
|
||||
# Read version from cli/package.json
|
||||
VERSION=$(node -p "require('./cli/package.json').version")
|
||||
echo "Release version: $VERSION"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build standalone NPM package
|
||||
env:
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
CLINE_ENVIRONMENT: production
|
||||
OTEL_TELEMETRY_ENABLED: "1"
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_LOGS_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 }}
|
||||
run: node scripts/package-npm.mjs
|
||||
|
||||
- name: Verify build output
|
||||
run: |
|
||||
echo "Checking dist-standalone directory..."
|
||||
ls -la dist-standalone/
|
||||
|
||||
echo "Verifying CLI binaries..."
|
||||
ls -lh cli/bin/cline-* || echo "Warning: CLI binaries not found"
|
||||
|
||||
echo "Checking package.json in dist-standalone..."
|
||||
cat dist-standalone/package.json | grep version
|
||||
|
||||
- name: Publish to NPM with latest tag
|
||||
run: |
|
||||
echo "Publishing version ${{ steps.version.outputs.version }} to NPM with tag 'latest'..."
|
||||
cd dist-standalone
|
||||
npm publish --tag latest --access public
|
||||
|
||||
- name: Tag release
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag "v${{ steps.version.outputs.version }}-cli"
|
||||
git push origin "v${{ steps.version.outputs.version }}-cli"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "✅ Successfully published cline@${{ steps.version.outputs.version }} to NPM with tag 'latest'"
|
||||
echo ""
|
||||
echo "📦 Install with: npm install -g cline"
|
||||
echo "🔗 NPM: https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}"
|
||||
@@ -1,134 +0,0 @@
|
||||
name: Publish NPM Nightly
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
force_publish:
|
||||
description: "Force publish even if there are no commits in the last 24 hours"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # Required for npm trusted publishing (OIDC)
|
||||
checks: write # Required by test workflow
|
||||
pull-requests: write # Required by test workflow
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: ./.github/workflows/test.yml
|
||||
|
||||
publish-npm-nightly:
|
||||
needs: test
|
||||
name: Publish Cline CLI (Nightly) to NPM
|
||||
if: github.repository == 'cline/cline' && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check for recent commits
|
||||
id: check_commits
|
||||
run: |
|
||||
if [ "${{ inputs.force_publish }}" = "true" ]; then
|
||||
echo "force_publish enabled, proceeding with publish"
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $(git rev-list --count HEAD --since="24 hours ago") -eq 0 ]; then
|
||||
echo "No commits in last 24 hours, skipping publish"
|
||||
echo "skip=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Found recent commits, proceeding with publish"
|
||||
echo "skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Setup Node.js
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24.x"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Install root dependencies and CLI dependencies
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: npm ci --include=optional # this will also install cli deps because "cli" in included in root package.json workspaces field
|
||||
|
||||
- name: Generate Protos
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: npm run protos
|
||||
|
||||
- name: Generate nightly version with timestamp
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
id: version
|
||||
run: |
|
||||
# Read base version from cli/package.json (e.g., "2.0.0")
|
||||
BASE_VERSION=$(node -p "require('./cli/package.json').version")
|
||||
|
||||
# Generate timestamp (Unix epoch seconds)
|
||||
TIMESTAMP=$(date +%s)
|
||||
|
||||
# Create unique nightly version: 1.0.9-nightly.1736365200
|
||||
VERSION="${BASE_VERSION}-nightly.${TIMESTAMP}"
|
||||
|
||||
echo "Base version: $BASE_VERSION"
|
||||
echo "Generated nightly version: $VERSION"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update cli/package.json with nightly version
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: |
|
||||
# Update version with timestamp-based nightly version
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const pkg = JSON.parse(fs.readFileSync('cli/package.json', 'utf8'));
|
||||
pkg.version = '${{ steps.version.outputs.version }}';
|
||||
fs.writeFileSync('cli/package.json', JSON.stringify(pkg, null, '\t'));
|
||||
"
|
||||
|
||||
echo "Using version ${{ steps.version.outputs.version }} for build"
|
||||
cat cli/package.json | grep '"version"'
|
||||
|
||||
- name: Build and package CLI
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
env:
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
CLINE_ENVIRONMENT: production
|
||||
OTEL_TELEMETRY_ENABLED: "1"
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_LOGS_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 }}
|
||||
run: node scripts/package-npm.mjs
|
||||
|
||||
- name: Verify build output
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: |
|
||||
echo "Checking dist-standalone directory..."
|
||||
ls -la dist-standalone/
|
||||
|
||||
echo "Verifying CLI binaries..."
|
||||
ls -lh cli/bin/cline-* || echo "Warning: CLI binaries not found"
|
||||
|
||||
echo "Checking package.json in dist-standalone..."
|
||||
cat dist-standalone/package.json | grep version
|
||||
|
||||
- name: Publish to NPM with nightly tag
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: |
|
||||
echo "Publishing version ${{ steps.version.outputs.version }} to NPM with tag 'nightly'..."
|
||||
cd dist-standalone
|
||||
npm publish --tag nightly --access public
|
||||
|
||||
- name: Summary
|
||||
if: steps.check_commits.outputs.skip != 'true'
|
||||
run: |
|
||||
echo "✅ Successfully published cline@${{ steps.version.outputs.version }} to NPM with tag 'nightly'"
|
||||
echo ""
|
||||
echo "📦 Install with: npm install -g cline@nightly"
|
||||
echo "🔗 NPM: https://www.npmjs.com/package/cline/v/${{ steps.version.outputs.version }}"
|
||||
@@ -1,215 +0,0 @@
|
||||
# Build and Pack CLI
|
||||
#
|
||||
# Builds a CLI tarball from any branch/commit and publishes it as a GitHub Release.
|
||||
# Requires write access to the repository (maintainers/collaborators only).
|
||||
#
|
||||
# Security: Split into two jobs to isolate untrusted build code from write tokens.
|
||||
# The build job runs arbitrary ref code with zero permissions. The release job
|
||||
# only runs trusted GitHub Actions with write scope.
|
||||
#
|
||||
# Usage (helper script, auto-detects current branch):
|
||||
# ./scripts/build-cli-artifact.sh
|
||||
# ./scripts/build-cli-artifact.sh feature/my-changes
|
||||
# ./scripts/build-cli-artifact.sh feature/my-changes 1234 # comments on PR
|
||||
#
|
||||
# Usage (gh CLI directly):
|
||||
# gh workflow run pack-cli.yml -f ref=main
|
||||
# gh workflow run pack-cli.yml -f ref=abc123 -f pr_number=1234
|
||||
#
|
||||
# Install the built CLI (no auth required):
|
||||
# npm install -g https://github.com/cline/cline/releases/download/cli-build-<sha>/cline-<ver>.tgz
|
||||
#
|
||||
# Find releases:
|
||||
# gh release list --limit 10
|
||||
|
||||
name: Build and Pack CLI
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Branch, tag, or commit SHA to build (leave empty for default branch)'
|
||||
required: false
|
||||
type: string
|
||||
pr_number:
|
||||
description: 'PR number to comment on with install instructions (optional)'
|
||||
required: false
|
||||
type: number
|
||||
|
||||
jobs:
|
||||
# ── Build job: runs untrusted ref code with ZERO permissions ──
|
||||
build:
|
||||
name: Build CLI
|
||||
runs-on: ubuntu-latest
|
||||
permissions: {}
|
||||
outputs:
|
||||
commit_sha: ${{ steps.commit.outputs.sha }}
|
||||
tarball: ${{ steps.pack.outputs.tarball }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Get commit SHA
|
||||
id: commit
|
||||
run: |
|
||||
COMMIT_SHA=$(git rev-parse --short HEAD)
|
||||
echo "sha=$COMMIT_SHA" >> $GITHUB_OUTPUT
|
||||
echo "Building from commit: $COMMIT_SHA"
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20.x"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --include=optional
|
||||
|
||||
- name: Generate Protos
|
||||
run: npm run protos
|
||||
|
||||
- name: Build standalone package
|
||||
run: node scripts/package-npm.mjs
|
||||
|
||||
- name: Create Tarball
|
||||
id: pack
|
||||
run: |
|
||||
cd dist-standalone
|
||||
TARBALL=$(npm pack)
|
||||
echo "tarball=$TARBALL" >> $GITHUB_OUTPUT
|
||||
echo "Created tarball: $TARBALL"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cli-tarball
|
||||
path: dist-standalone/*.tgz
|
||||
|
||||
# ── Release job: only trusted Actions code, with write permissions ──
|
||||
release:
|
||||
name: Release CLI
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Download artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: cli-tarball
|
||||
path: dist-standalone
|
||||
|
||||
- name: Create GitHub Release
|
||||
id: create_release
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const commit = '${{ needs.build.outputs.commit_sha }}';
|
||||
const tarball = '${{ needs.build.outputs.tarball }}';
|
||||
|
||||
// Delete existing release/tag if re-running for the same commit
|
||||
const tagName = `cli-build-${commit}`;
|
||||
try {
|
||||
const existing = await github.rest.repos.getReleaseByTag({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag: tagName
|
||||
});
|
||||
await github.rest.repos.deleteRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: existing.data.id
|
||||
});
|
||||
await github.rest.git.deleteRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `tags/${tagName}`
|
||||
});
|
||||
core.info(`Deleted existing release for ${tagName}`);
|
||||
} catch (e) {
|
||||
// Release doesn't exist yet, that's fine
|
||||
}
|
||||
|
||||
// Create a release
|
||||
const release = await github.rest.repos.createRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag_name: tagName,
|
||||
name: `CLI Build (${commit})`,
|
||||
body: `Automated CLI build from commit ${commit}\n\nInstall with:\n\`\`\`bash\nnpm install -g https://github.com/${context.repo.owner}/${context.repo.repo}/releases/download/${tagName}/${tarball}\n\`\`\``,
|
||||
draft: false,
|
||||
prerelease: true
|
||||
});
|
||||
|
||||
// Upload the tarball as a release asset
|
||||
const tarballPath = path.join('dist-standalone', tarball);
|
||||
const tarballData = fs.readFileSync(tarballPath);
|
||||
|
||||
await github.rest.repos.uploadReleaseAsset({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: release.data.id,
|
||||
name: tarball,
|
||||
data: tarballData
|
||||
});
|
||||
|
||||
const downloadUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/releases/download/${tagName}/${tarball}`;
|
||||
core.setOutput('release_url', release.data.html_url);
|
||||
core.setOutput('download_url', downloadUrl);
|
||||
|
||||
- name: Comment on PR with download instructions
|
||||
if: inputs.pr_number != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const commit = '${{ needs.build.outputs.commit_sha }}';
|
||||
const releaseUrl = '${{ steps.create_release.outputs.release_url }}';
|
||||
const downloadUrl = '${{ steps.create_release.outputs.download_url }}';
|
||||
const prNumber = ${{ inputs.pr_number || 0 }};
|
||||
if (!prNumber) return;
|
||||
|
||||
const comment = `## 📦 CLI Build Ready
|
||||
|
||||
A CLI build has been created for commit \`${commit}\`.
|
||||
|
||||
### Install Directly from URL (No Authentication Required!)
|
||||
|
||||
\`\`\`bash
|
||||
npm install -g ${downloadUrl}
|
||||
\`\`\`
|
||||
|
||||
### Alternative: Download and Install
|
||||
|
||||
\`\`\`bash
|
||||
curl -L ${downloadUrl} -o cline.tgz
|
||||
npm install -g ./cline.tgz
|
||||
\`\`\`
|
||||
|
||||
📦 [View Release](${releaseUrl})
|
||||
`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: prNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: comment
|
||||
});
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "✅ CLI build complete!"
|
||||
echo ""
|
||||
echo "📦 Release: ${{ steps.create_release.outputs.release_url }}"
|
||||
echo "🔗 Download URL: ${{ steps.create_release.outputs.download_url }}"
|
||||
echo ""
|
||||
echo "Install from anywhere (no authentication required):"
|
||||
echo " npm install -g ${{ steps.create_release.outputs.download_url }}"
|
||||
@@ -1,60 +0,0 @@
|
||||
name: Publish CLI (Trusted)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 12 * * *" # 4 AM PST (UTC-8) = 12 UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
publish_target:
|
||||
description: "Which publish flow to run"
|
||||
required: true
|
||||
default: "main"
|
||||
type: choice
|
||||
options:
|
||||
- main
|
||||
- nightly
|
||||
confirm_publish:
|
||||
description: 'Required when publish_target=main. Type "publish" to confirm release publish.'
|
||||
required: false
|
||||
type: string
|
||||
force_nightly_publish:
|
||||
description: "Force nightly publish even with no commits in last 24h"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
id-token: write # Required for npm trusted publishing (OIDC)
|
||||
contents: write # Required because npm-main creates/pushes git tags
|
||||
checks: write # Required by nested reusable test workflow
|
||||
pull-requests: write # Required by nested reusable test workflow
|
||||
|
||||
jobs:
|
||||
cli-tui-tests:
|
||||
uses: ./.github/workflows/cli-tui-tests.yml
|
||||
|
||||
publish-main:
|
||||
needs: cli-tui-tests
|
||||
if: |
|
||||
github.repository == 'cline/cline' && (
|
||||
github.event_name == 'workflow_dispatch' &&
|
||||
github.event.inputs.publish_target == 'main' &&
|
||||
github.event.inputs.confirm_publish == 'publish' &&
|
||||
!endsWith(github.actor, '[bot]')
|
||||
)
|
||||
uses: ./.github/workflows/npm-main.yaml
|
||||
secrets: inherit
|
||||
with:
|
||||
confirm_publish: ${{ github.event.inputs.confirm_publish }}
|
||||
|
||||
publish-nightly:
|
||||
needs: cli-tui-tests
|
||||
if: |
|
||||
github.repository == 'cline/cline' && (
|
||||
github.event_name == 'schedule' ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.publish_target == 'nightly')
|
||||
)
|
||||
uses: ./.github/workflows/npm-nightly.yaml
|
||||
secrets: inherit
|
||||
with:
|
||||
force_publish: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.force_nightly_publish == 'true' }}
|
||||
@@ -1,76 +0,0 @@
|
||||
name: "Publish Nightly Release"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 12 * * *' # 4 AM PST (UTC-8) = 12 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
checks: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: ./.github/workflows/test.yml
|
||||
|
||||
publish:
|
||||
needs: test
|
||||
name: Publish Cline (Nightly) Extension
|
||||
if: github.repository == 'cline/cline'
|
||||
runs-on: ubuntu-latest
|
||||
environment: PublishNightly
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: Check for recent commits
|
||||
run: |
|
||||
if [ $(git rev-list --count HEAD --since="24 hours ago") -eq 0 ]; then
|
||||
echo "No commits in last 24 hours, exiting"
|
||||
exit 0
|
||||
fi
|
||||
echo "Found recent commits, proceeding with build"
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
# Keep publish environment aligned with test workflow/tooling lockfile expectations.
|
||||
# Newer LTS (Node 24 / npm 11) can make `npm list` fail with ELSPROBLEMS during vsce packaging.
|
||||
node-version: 22
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci --include=optional
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci --include=optional
|
||||
|
||||
- name: Install Publishing Tools
|
||||
run: npm install -g @vscode/vsce ovsx
|
||||
|
||||
- name: Verify LFS media assets are resolved
|
||||
run: |
|
||||
FILE="webview-ui/src/assets/cline_kanban_demo.webm"
|
||||
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
||||
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Publish Extension as Pre-release
|
||||
env:
|
||||
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
||||
OVSX_PAT: ${{ secrets.OVSX_PAT }}
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
CLINE_ENVIRONMENT: production
|
||||
# OpenTelemetry production defaults (can be overridden at runtime)
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_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 }}
|
||||
run: npm run publish:marketplace:nightly
|
||||
+40
-123
@@ -11,15 +11,6 @@ on:
|
||||
options:
|
||||
- pre-release
|
||||
- release
|
||||
auto_create_tag_from_main:
|
||||
description: "Auto-create and push the provided tag from the tested main commit (recommended)"
|
||||
required: true
|
||||
default: true
|
||||
type: boolean
|
||||
tag:
|
||||
description: "Tag to publish (required in both modes, e.g., v3.1.2)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -39,81 +30,35 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
lfs: true
|
||||
|
||||
- name: Resolve Release Tag
|
||||
id: resolve_tag
|
||||
run: |
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
AUTO_CREATE="${{ github.event.inputs.auto_create_tag_from_main }}"
|
||||
TESTED_SHA="${{ github.sha }}"
|
||||
WORKFLOW_REF="${{ github.ref }}"
|
||||
|
||||
if [[ -z "$TAG" ]]; then
|
||||
echo "Error: tag input is required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$ ]]; then
|
||||
echo "Error: tag must match vX.Y.Z (optionally with -suffix or .suffix)"
|
||||
exit 1
|
||||
fi
|
||||
TAG_REF="refs/tags/$TAG"
|
||||
|
||||
git fetch origin main --tags
|
||||
|
||||
if [[ "$AUTO_CREATE" == "true" ]]; then
|
||||
if [[ "$WORKFLOW_REF" != "refs/heads/main" ]]; then
|
||||
echo "Error: auto-create mode requires dispatching from main (current ref: $WORKFLOW_REF)"
|
||||
exit 1
|
||||
fi
|
||||
echo "Auto-create enabled. Using tested workflow SHA: $TESTED_SHA"
|
||||
|
||||
if ! git merge-base --is-ancestor "$TESTED_SHA" origin/main; then
|
||||
echo "Error: tested SHA $TESTED_SHA is not on origin/main"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if git show-ref --verify --quiet "$TAG_REF"; then
|
||||
TAG_SHA=$(git rev-list -n 1 "$TAG_REF^{commit}")
|
||||
if [[ "$TAG_SHA" != "$TESTED_SHA" ]]; then
|
||||
echo "Error: tag '$TAG' already exists at $TAG_SHA, not at tested SHA ($TESTED_SHA)"
|
||||
exit 1
|
||||
fi
|
||||
echo "Tag '$TAG' already exists at tested SHA. Continuing."
|
||||
else
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag "$TAG" "$TESTED_SHA"
|
||||
git push origin "$TAG_REF"
|
||||
echo "Created and pushed tag '$TAG' from tested SHA $TESTED_SHA."
|
||||
fi
|
||||
else
|
||||
if ! git show-ref --verify --quiet "$TAG_REF"; then
|
||||
echo "Error: tag '$TAG' does not exist in the repository"
|
||||
exit 1
|
||||
fi
|
||||
echo "Using existing tag '$TAG'."
|
||||
fi
|
||||
|
||||
git checkout --detach "$TAG_REF^{commit}"
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "resolved_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
node-version: "lts/*"
|
||||
|
||||
# Cache root dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache root dependencies
|
||||
uses: actions/cache@v4
|
||||
id: root-cache
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache webview-ui dependencies
|
||||
uses: actions/cache@v4
|
||||
id: webview-cache
|
||||
with:
|
||||
path: webview-ui/node_modules
|
||||
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm install --include=optional
|
||||
if: steps.root-cache.outputs.cache-hit != 'true'
|
||||
run: npm ci --include=optional
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm install --include=optional
|
||||
if: steps.webview-cache.outputs.cache-hit != 'true'
|
||||
run: cd webview-ui && npm ci --include=optional
|
||||
|
||||
- name: Install Publishing Tools
|
||||
run: npm install -g @vscode/vsce ovsx
|
||||
@@ -124,41 +69,22 @@ jobs:
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Verify Tag Matches Package Version
|
||||
- name: Create Git Tag
|
||||
id: create_tag
|
||||
run: |
|
||||
TAG="${{ steps.resolve_tag.outputs.tag }}"
|
||||
VERSION="v${{ steps.get_version.outputs.version }}"
|
||||
if [[ "$TAG" != "$VERSION" ]]; then
|
||||
echo "Error: tag '$TAG' does not match package version '$VERSION'"
|
||||
exit 1
|
||||
fi
|
||||
echo "Tag and package version match: $TAG"
|
||||
|
||||
- name: Verify LFS media assets are resolved
|
||||
run: |
|
||||
FILE="webview-ui/src/assets/cline_kanban_demo.webm"
|
||||
if grep -q "git-lfs.github.com/spec/v1" "$FILE"; then
|
||||
echo "Error: $FILE is still a Git LFS pointer in CI checkout"
|
||||
exit 1
|
||||
fi
|
||||
VERSION=v${{ steps.get_version.outputs.version }}
|
||||
echo "tag=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Tagging with $VERSION"
|
||||
git tag "$VERSION"
|
||||
git push origin "$VERSION"
|
||||
|
||||
- name: Package and Publish Extension
|
||||
env:
|
||||
VSCE_PAT: ${{ secrets.VSCE_PAT }}
|
||||
OVSX_PAT: ${{ secrets.OVSX_PAT }}
|
||||
CLINE_ENVIRONMENT: production
|
||||
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
|
||||
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
|
||||
# OpenTelemetry production defaults (can be overridden at runtime)
|
||||
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
|
||||
OTEL_LOGS_EXPORTER: otlp
|
||||
OTEL_METRICS_EXPORTER: otlp
|
||||
OTEL_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 }}
|
||||
run: |
|
||||
# Required to generate the .vsix
|
||||
vsce package --allow-package-secrets sendgrid --out "cline-${{ steps.get_version.outputs.version }}.vsix"
|
||||
vsce package --out "cline-${{ steps.get_version.outputs.version }}.vsix"
|
||||
|
||||
if [ "${{ github.event.inputs.release-type }}" = "pre-release" ]; then
|
||||
npm run publish:marketplace:prerelease
|
||||
@@ -168,31 +94,22 @@ jobs:
|
||||
echo "Successfully published release version ${{ steps.get_version.outputs.version }} to VS Code Marketplace and Open VSX Registry"
|
||||
fi
|
||||
|
||||
- name: Get Previous Tag
|
||||
id: prev_tag
|
||||
run: |
|
||||
CURRENT_TAG="${{ steps.resolve_tag.outputs.tag }}"
|
||||
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
|
||||
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get Changelog Entry
|
||||
id: changelog
|
||||
run: |
|
||||
# Get content between first ## [ and second ## [
|
||||
CONTENT=$(awk '/^## \[/{if(found) exit; found=1; next} found{print}' CHANGELOG.md)
|
||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CONTENT" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
# - name: Get Changelog Entry
|
||||
# id: changelog
|
||||
# uses: mindsers/changelog-reader-action@v2
|
||||
# with:
|
||||
# # This expects a standard Keep a Changelog format
|
||||
# # "latest" means it will read whichever is the most recent version
|
||||
# # set in "## [1.2.3] - 2025-01-28" style
|
||||
# version: latest
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ steps.resolve_tag.outputs.tag }}
|
||||
tag_name: ${{ steps.create_tag.outputs.tag }}
|
||||
files: "*.vsix"
|
||||
body: |
|
||||
${{ steps.changelog.outputs.content }}
|
||||
|
||||
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.prev_tag.outputs.prev_tag }}...${{ steps.resolve_tag.outputs.tag }}
|
||||
# body: ${{ steps.changelog.outputs.content }}
|
||||
generate_release_notes: true
|
||||
prerelease: ${{ github.event.inputs.release-type == 'pre-release' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# This workflow will only label and/or close 30 issues at a time in order to avoid exceeding a rate limit.
|
||||
# More info: https://docs.github.com/en/actions/use-cases-and-examples/project-management/closing-inactive-issues
|
||||
name: Close inactive issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-issue-stale: 60
|
||||
days-before-issue-close: 14
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 60 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
exempt-issue-labels: "pinned,security"
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,32 +0,0 @@
|
||||
name: Test Stale Issues Workflow
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
days-before-stale:
|
||||
description: "Days before an issue becomes stale"
|
||||
required: true
|
||||
default: "1"
|
||||
days-before-close:
|
||||
description: "Days before a stale issue is closed"
|
||||
required: true
|
||||
default: "1"
|
||||
|
||||
jobs:
|
||||
test-stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@28ca103
|
||||
with:
|
||||
days-before-issue-stale: ${{ github.event.inputs.days-before-stale }}
|
||||
days-before-issue-close: ${{ github.event.inputs.days-before-close }}
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for ${{ github.event.inputs.days-before-stale }} days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for ${{ github.event.inputs.days-before-close }} days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
exempt-issue-labels: "pinned,security"
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
debug-only: true
|
||||
+129
-155
@@ -1,9 +1,6 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -17,44 +14,8 @@ permissions:
|
||||
pull-requests: write # Needed to add comments/annotations to PRs
|
||||
|
||||
jobs:
|
||||
quality-checks:
|
||||
runs-on: ubuntu-latest
|
||||
name: Quality Checks
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
package-lock.json
|
||||
webview-ui/package-lock.json
|
||||
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Run Quality Checks (Parallel)
|
||||
run: npm run ci:check-all
|
||||
|
||||
test:
|
||||
needs: quality-checks
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os == 'ubuntu-latest' && 'test' || format('test ({0})', matrix.os) }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -63,162 +24,175 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
package-lock.json
|
||||
webview-ui/package-lock.json
|
||||
|
||||
# Setup Python for coverage script
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
# Cache root dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache root dependencies
|
||||
uses: actions/cache@v4
|
||||
id: root-cache
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache webview-ui dependencies
|
||||
uses: actions/cache@v4
|
||||
id: webview-cache
|
||||
with:
|
||||
path: webview-ui/node_modules
|
||||
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
|
||||
|
||||
- name: Install root dependencies
|
||||
if: steps.root-cache.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
if: steps.webview-cache.outputs.cache-hit != 'true'
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Set up NPM on Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
npm config set script-shell "C:\\Program Files\\Git\\bin\\bash.exe"
|
||||
- name: Type Check
|
||||
run: npm run check-types
|
||||
|
||||
# Build the extension and tests (without redundant checks)
|
||||
- name: ESLint Check
|
||||
run: npm run lint
|
||||
|
||||
- name: Prettier / Format Check
|
||||
run: npm run format
|
||||
|
||||
# Build the extension before running tests
|
||||
- name: Build Tests and Extension
|
||||
id: build_step
|
||||
run: npm run ci:build
|
||||
run: npm run pretest
|
||||
|
||||
- name: Unit Tests with coverage - Linux
|
||||
id: unit_tests_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
|
||||
- name: Unit Tests
|
||||
run: npm run test:unit
|
||||
|
||||
# Run extension tests with coverage
|
||||
- name: Extension Tests with Coverage
|
||||
id: extension_coverage
|
||||
continue-on-error: true
|
||||
run: |
|
||||
npx nyc --nycrc-path .nycrc.unit.json --reporter=lcov npm run test:unit
|
||||
|
||||
- name: Unit Tests - Non-Linux
|
||||
id: unit_tests_non_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
|
||||
run: |
|
||||
npm run test:unit
|
||||
|
||||
- name: Extension Integration Tests - Linux
|
||||
id: integration_tests_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
|
||||
run: xvfb-run -a npm run test:coverage
|
||||
|
||||
- name: Extension Integration Tests - Non-Linux
|
||||
id: integration_tests_non_linux
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os != 'Linux' }}
|
||||
run: npm run test:integration
|
||||
xvfb-run -a npm run test:coverage > extension_coverage.txt 2>&1
|
||||
PYTHONPATH=.github/scripts python -m coverage_check extract-coverage extension_coverage.txt --type=extension --github-output --verbose
|
||||
|
||||
# Run webview tests with coverage
|
||||
- name: Webview Tests with Coverage
|
||||
id: webview_tests
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
|
||||
id: webview_coverage
|
||||
continue-on-error: true
|
||||
run: |
|
||||
cd webview-ui
|
||||
npm run test:coverage
|
||||
|
||||
- name: CLI Tests
|
||||
id: cli_tests
|
||||
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
|
||||
run: cd cli && npm run test:run
|
||||
# Ensure coverage dependency is installed
|
||||
npm install --no-save @vitest/coverage-v8
|
||||
npm run test:coverage > webview_coverage.txt 2>&1 || true
|
||||
cd ..
|
||||
PYTHONPATH=.github/scripts python -m coverage_check extract-coverage webview-ui/webview_coverage.txt --type=webview --github-output --verbose
|
||||
|
||||
# Save coverage reports as artifacts (workflow-scoped)
|
||||
- name: Save Coverage Reports
|
||||
uses: actions/upload-artifact@v4
|
||||
# Only upload artifacts on Linux - We only need coverage from one OS
|
||||
if: runner.os == 'Linux'
|
||||
with:
|
||||
name: pr-coverage-reports
|
||||
path: |
|
||||
coverage-unit/lcov.info
|
||||
webview-ui/coverage/lcov.info
|
||||
extension_coverage.txt
|
||||
webview-ui/webview_coverage.txt
|
||||
retention-period: workflow # Artifacts are automatically deleted when the workflow completes
|
||||
|
||||
test-platform-integration:
|
||||
needs: quality-checks
|
||||
# Set the check as failed if any of the tests failed
|
||||
- name: Check for test failures
|
||||
run: |
|
||||
# Check if any of the test steps failed
|
||||
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#steps-context
|
||||
if [ "${{ steps.extension_coverage.outcome }}" != "success" ] || [ "${{ steps.webview_coverage.outcome }}" != "success" ]; then
|
||||
echo "Tests failed."
|
||||
cat extension_coverage.txt
|
||||
cat webview-ui/webview_coverage.txt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
coverage:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
# Only run on PRs to main branch
|
||||
if: github.event_name == 'pull_request' && github.base_ref == 'main'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for accurate comparison
|
||||
|
||||
# Setup Python for coverage script
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
cache-dependency-path: |
|
||||
package-lock.json
|
||||
webview-ui/package-lock.json
|
||||
testing-platform/package-lock.json
|
||||
|
||||
# Cache root dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache root dependencies
|
||||
uses: actions/cache@v4
|
||||
id: root-cache
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
|
||||
|
||||
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
|
||||
- name: Cache webview-ui dependencies
|
||||
uses: actions/cache@v4
|
||||
id: webview-cache
|
||||
with:
|
||||
path: webview-ui/node_modules
|
||||
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
|
||||
|
||||
- name: Install root dependencies
|
||||
if: steps.root-cache.outputs.cache-hit != 'true'
|
||||
run: npm ci
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install webview-ui dependencies
|
||||
if: steps.webview-cache.outputs.cache-hit != 'true'
|
||||
run: cd webview-ui && npm ci
|
||||
|
||||
- name: Download ripgrep binaries
|
||||
run: npm run download-ripgrep
|
||||
# Build the extension before running tests
|
||||
- name: Build Extension
|
||||
run: npm run compile
|
||||
|
||||
- name: Compile Standalone
|
||||
run: npm run compile-standalone
|
||||
|
||||
- name: Install testing platform dependencies
|
||||
run: cd testing-platform && npm ci
|
||||
|
||||
- name: Running testing platform integration spec tests
|
||||
timeout-minutes: 7
|
||||
run: npm run test:tp-orchestrator -- tests/specs/ --count=1 --coverage
|
||||
|
||||
- name: Save Coverage Reports
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-platform-integration-core-coverage
|
||||
path: coverage/**/lcov.info
|
||||
|
||||
qlty:
|
||||
needs: [test, test-platform-integration]
|
||||
runs-on: ubuntu-latest
|
||||
# Run on PRs to main, pushes to main, and manual dispatches
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download unit tests coverage reports
|
||||
# Download coverage artifacts from test job
|
||||
- name: Download Coverage Reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: pr-coverage-reports
|
||||
path: .
|
||||
path: . # Download to root directory to match expected paths
|
||||
|
||||
- name: Upload core unit tests coverage to Qlty
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
# we can merge multiple files if necessary
|
||||
files: |
|
||||
coverage-unit/lcov.info
|
||||
tag: unit:core
|
||||
# Process coverage workflow
|
||||
- name: Process coverage workflow
|
||||
id: coverage
|
||||
run: |
|
||||
# Extract PR number from GITHUB_REF
|
||||
PR_NUMBER=$(echo "$GITHUB_REF" | sed -e 's/refs\/pull\///' -e 's/\/merge//')
|
||||
|
||||
- name: Upload webview-ui unit tests coverage to Qlty
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
# we can merge multiple files if necessary
|
||||
files: |
|
||||
webview-ui/coverage/lcov.info
|
||||
tag: unit:webview-ui
|
||||
add-prefix: webview-ui/
|
||||
|
||||
- name: Download test platform integration core coverage artifact
|
||||
uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
id: download-integration-coverage
|
||||
with:
|
||||
name: test-platform-integration-core-coverage
|
||||
path: integration-core-coverage-reports
|
||||
|
||||
- name: Upload core integration tests coverage to Qlty
|
||||
if: steps.download-integration-coverage.outcome == 'success'
|
||||
uses: qltysh/qlty-action/coverage@v2
|
||||
with:
|
||||
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
|
||||
files: integration-core-coverage-reports/**/lcov.info
|
||||
tag: integration:core
|
||||
# Run the coverage workflow from root directory
|
||||
PYTHONPATH=.github/scripts python -m coverage_check process-workflow \
|
||||
--base-branch ${{ github.base_ref }} \
|
||||
--pr-number $PR_NUMBER \
|
||||
--repo $GITHUB_REPOSITORY \
|
||||
--token ${{ secrets.GITHUB_TOKEN }} \
|
||||
--verbose
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
name: Trigger Jetbrains Plugin <-> Cline Tests
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
concurrency:
|
||||
group: jetbrains-trigger-${{ github.event.pull_request.number || github.event.issue.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
trigger-integration-test:
|
||||
name: Run Tests
|
||||
runs-on: ubuntu-latest
|
||||
# Run on PR open/reopen, or when someone comments /test-jetbrains on a PR
|
||||
if: |
|
||||
github.event_name == 'pull_request_target' ||
|
||||
(github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/test-jetbrains') &&
|
||||
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association))
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: 1998650
|
||||
private-key: ${{ secrets.CLINE_JETBRAINS_WORKFLOW_KEY }}
|
||||
owner: cline
|
||||
repositories: intellij-plugin
|
||||
|
||||
- name: Get PR details (for issue_comment trigger)
|
||||
id: pr-details
|
||||
if: github.event_name == 'issue_comment'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }})
|
||||
echo "head_ref=$(echo "$PR_DATA" | jq -r '.head.ref')" >> $GITHUB_OUTPUT
|
||||
echo "head_sha=$(echo "$PR_DATA" | jq -r '.head.sha')" >> $GITHUB_OUTPUT
|
||||
echo "title=$(echo "$PR_DATA" | jq -r '.title')" >> $GITHUB_OUTPUT
|
||||
echo "html_url=$(echo "$PR_DATA" | jq -r '.html_url')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Sanitize untrusted inputs
|
||||
id: sanitize
|
||||
env:
|
||||
RAW_BRANCH_NAME: ${{ github.event_name == 'pull_request_target' && github.head_ref || steps.pr-details.outputs.head_ref }}
|
||||
RAW_PR_TITLE: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.title || steps.pr-details.outputs.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 }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
|
||||
PR_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || steps.pr-details.outputs.head_sha }}
|
||||
PR_URL: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.html_url || steps.pr-details.outputs.html_url }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer ${{ steps.app-token.outputs.token }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "User-Agent: cline-pr-trigger" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://api.github.com/repos/cline/intellij-plugin/dispatches \
|
||||
-d @- <<EOF
|
||||
{
|
||||
"event_type": "cline-pr-check",
|
||||
"client_payload": {
|
||||
"pr_number": "$PR_NUMBER",
|
||||
"branch_name": $BRANCH_NAME,
|
||||
"action": "${{ github.event.action }}",
|
||||
"sha": "$PR_SHA",
|
||||
"pr_title": $PR_TITLE,
|
||||
"pr_url": "$PR_URL"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Log trigger details
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
|
||||
PR_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || steps.pr-details.outputs.head_sha }}
|
||||
run: |
|
||||
echo "Triggered IntelliJ Plugin integration test for:"
|
||||
echo " PR #$PR_NUMBER"
|
||||
echo " Trigger: ${{ github.event_name }}"
|
||||
echo " Action: ${{ github.event.action }}"
|
||||
echo " SHA: $PR_SHA"
|
||||
+1
-38
@@ -1,58 +1,21 @@
|
||||
out
|
||||
dist
|
||||
dist-standalone
|
||||
node_modules
|
||||
tmp
|
||||
.vscode-test/
|
||||
*.vsix
|
||||
|
||||
.DS_Store
|
||||
.idea
|
||||
.husky/_/
|
||||
|
||||
pnpm-lock.yaml
|
||||
|
||||
.clineignore
|
||||
.venv
|
||||
.actrc
|
||||
CLAUDE.local.md
|
||||
|
||||
webview-ui/src/**/*.js
|
||||
webview-ui/src/**/*.js.map
|
||||
|
||||
# Ignore coverage directories and files
|
||||
coverage
|
||||
coverage-unit
|
||||
.nyc_output
|
||||
# But don't ignore the coverage scripts in .github/scripts/
|
||||
!.github/scripts/coverage/
|
||||
|
||||
*evals.env
|
||||
.env
|
||||
.secrets
|
||||
.github/act/.secrets
|
||||
|
||||
.worktrees
|
||||
|
||||
## Generated files ##
|
||||
src/generated/
|
||||
src/shared/proto/
|
||||
webview-ui/src/services/grpc-client.ts
|
||||
*.tsbuildinfo
|
||||
|
||||
# E2E Tests
|
||||
test-results
|
||||
|
||||
/.github/act
|
||||
/pkg
|
||||
.secrets
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
# Smoke test results (generated)
|
||||
evals/smoke-tests/results/
|
||||
|
||||
.tui-test
|
||||
secrets.json
|
||||
tui-traces
|
||||
tests/**/cache
|
||||
*evals.env
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "evals/cline-bench"]
|
||||
path = evals/cline-bench
|
||||
url = https://github.com/cline/cline-bench.git
|
||||
Regular → Executable
+17
-1
@@ -1 +1,17 @@
|
||||
lint-staged
|
||||
echo "Running pre-commit checks..."
|
||||
|
||||
# Run ESLint
|
||||
echo "Running ESLint..."
|
||||
npm run lint || {
|
||||
echo "❌ ESLint check failed. Please fix the errors and try committing again."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Run Prettier
|
||||
echo "Running Prettier..."
|
||||
npm run format || {
|
||||
echo "❌ Prettier check failed. Run 'npm run format:fix' to automatically fix formatting issues."
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "✅ All checks passed!"
|
||||
|
||||
+4
-13
@@ -1,15 +1,6 @@
|
||||
{
|
||||
"extension": [
|
||||
"ts"
|
||||
],
|
||||
"spec": [
|
||||
"src/**/__tests__/*.ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register",
|
||||
"source-map-support/register",
|
||||
"./src/test/requires.ts"
|
||||
],
|
||||
"recursive": true,
|
||||
"exit": true
|
||||
"extension": ["ts"],
|
||||
"spec": "src/**/__tests__/*.ts",
|
||||
"require": ["ts-node/register", "source-map-support/register", "./src/test/requires.ts"],
|
||||
"recursive": true
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"all": true,
|
||||
"check-coverage": false,
|
||||
"reporter": [
|
||||
"text",
|
||||
"lcov"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"**/*.d.ts",
|
||||
|
||||
"**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs}",
|
||||
"**/__tests__/**",
|
||||
"**/test/**",
|
||||
"**/tests/**",
|
||||
"**/.nyc_output/**",
|
||||
"**/.vscode-test/**",
|
||||
"**/tests-results/**",
|
||||
"src/test/**",
|
||||
|
||||
"src/generated/**",
|
||||
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"**/out/**",
|
||||
"**/build/**",
|
||||
"**/coverage/**",
|
||||
"**/coverage-unit/**",
|
||||
"**/proto/**",
|
||||
|
||||
"**/*.{config,setup}.{js,ts,mjs,cjs}",
|
||||
"**/vite-env.d.ts",
|
||||
|
||||
"**/*.{css,scss,sass,less,styl}",
|
||||
"**/*.{svg,png,jpg,jpeg,gif,ico}",
|
||||
"**/*.{json,yaml,yml}"
|
||||
],
|
||||
"extension": [
|
||||
".ts",
|
||||
".js"
|
||||
],
|
||||
"cache": true,
|
||||
"sourceMap": true,
|
||||
"instrument": true,
|
||||
"report-dir": "./coverage-unit"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
dist/
|
||||
node_modules
|
||||
webview-ui/build/
|
||||
*.md
|
||||
package-lock.json
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"printWidth": 130,
|
||||
"semi": false,
|
||||
"bracketSameLine": true
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { defineConfig } from "@vscode/test-cli"
|
||||
import path from "path"
|
||||
|
||||
export default defineConfig({
|
||||
files: "{out/**/*.test.js,src/**/*.test.js,!src/test/e2e/**/*.test.js,!out/src/test/e2e/**/*.test.js}",
|
||||
files: "{out/**/*.test.js,src/**/*.test.js}",
|
||||
mocha: {
|
||||
ui: "bdd",
|
||||
timeout: 20000, // Maximum time (in ms) that a test can run before failing
|
||||
|
||||
Vendored
+1
-6
@@ -1,10 +1,5 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"connor4312.esbuild-problem-matchers",
|
||||
"ms-vscode.extension-test-runner",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"biomejs.biome"
|
||||
]
|
||||
"recommendations": ["dbaeumer.vscode-eslint", "connor4312.esbuild-problem-matchers", "ms-vscode.extension-test-runner"]
|
||||
}
|
||||
|
||||
Vendored
+4
-187
@@ -6,198 +6,15 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension (production)",
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}",
|
||||
"--disable-extensions"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
],
|
||||
"args": ["--extensionDevelopmentPath=${workspaceFolder}", "--disable-workspace-trust", "${workspaceFolder}"],
|
||||
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
|
||||
"preLaunchTask": "${defaultBuildTask}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
"CLINE_ENVIRONMENT": "production"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Run Extension (staging)",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
"CLINE_ENVIRONMENT": "staging"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Run Extension (local)",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--disable-workspace-trust",
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
"CLINE_ENVIRONMENT": "local"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Run Extension (Fresh Install Mode)",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--user-data-dir=${workspaceFolder}/dist/tmp/user",
|
||||
"--profile-temp",
|
||||
"--sync=off",
|
||||
"--disable-extension",
|
||||
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
|
||||
"--disable-extension",
|
||||
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "clean-tmp-user",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"postDebugTask": "stop",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"TEMP_PROFILE": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
"CLINE_ENVIRONMENT": "production"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Test Standalone Core Api Server (test:sca-server)",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"resolveSourceMapLocations": [
|
||||
"${workspaceFolder}/**",
|
||||
"!**/node_modules/**"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js",
|
||||
"${workspaceFolder}/dist-standalone/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "compile-standalone",
|
||||
"runtimeExecutable": "npx",
|
||||
"runtimeArgs": [
|
||||
"tsx"
|
||||
],
|
||||
"program": "scripts/test-standalone-core-api-server.ts",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"env": {
|
||||
"PROTOBUS_PORT": "26040",
|
||||
"HOSTBRIDGE_PORT": "26041",
|
||||
"WORKSPACE_DIR": "${workspaceFolder}",
|
||||
"E2E_TEST": "true",
|
||||
"CLINE_ENVIRONMENT": "local"
|
||||
},
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Current Test File",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"resolveSourceMapLocations": [
|
||||
"${workspaceFolder}/**",
|
||||
"!**/node_modules/**"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeExecutable": "npx",
|
||||
"runtimeArgs": [
|
||||
"mocha"
|
||||
],
|
||||
"args": [
|
||||
"--require",
|
||||
"ts-node/register",
|
||||
"--require",
|
||||
"source-map-support/register",
|
||||
"--require",
|
||||
"./src/test/requires.ts",
|
||||
"--exit",
|
||||
"${file}"
|
||||
],
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"env": {
|
||||
"TS_NODE_PROJECT": "./tsconfig.unit-test.json",
|
||||
"NODE_ENV": "test",
|
||||
"IS_DEV": "true",
|
||||
"CLINE_ENVIRONMENT": "local"
|
||||
},
|
||||
"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"
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Vendored
+2
-22
@@ -6,28 +6,8 @@
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true, // set this to false to include "out" folder in search results
|
||||
"dist": true, // set this to false to include "dist" folder in search results,
|
||||
"node_modules": true,
|
||||
"dist-standalone": true
|
||||
"dist": true // set this to false to include "dist" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
"typescript.preferences.quoteStyle": "double",
|
||||
// Protobuf settings
|
||||
"protoc": {
|
||||
"options": [
|
||||
"--proto_path=proto"
|
||||
]
|
||||
},
|
||||
// Enable Lint and format using Biome
|
||||
"biome.enabled": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.biome": "explicit",
|
||||
"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
|
||||
"typescript.tsc.autoDetect": "off"
|
||||
}
|
||||
|
||||
Vendored
+11
-109
@@ -3,16 +3,6 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "compile-standalone",
|
||||
"type": "npm",
|
||||
"script": "compile-standalone",
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "npm: protos",
|
||||
"type": "npm",
|
||||
@@ -30,13 +20,7 @@
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"dependsOn": [
|
||||
"npm: protos",
|
||||
"npm: build:webview",
|
||||
"npm: dev:webview",
|
||||
"npm: watch:tsc",
|
||||
"npm: watch:esbuild"
|
||||
],
|
||||
"dependsOn": ["npm: protos", "npm: build:webview", "npm: dev:webview", "npm: watch:tsc", "npm: watch:esbuild"],
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
@@ -66,9 +50,7 @@
|
||||
"problemMatcher": [],
|
||||
"isBackground": true,
|
||||
"label": "npm: build:webview",
|
||||
"dependsOn": [
|
||||
"npm: protos"
|
||||
],
|
||||
"dependsOn": ["npm: protos"],
|
||||
"presentation": {
|
||||
"group": "watch",
|
||||
"reveal": "always"
|
||||
@@ -86,9 +68,7 @@
|
||||
"problemMatcher": [],
|
||||
"isBackground": true,
|
||||
"label": "npm: build:webview:test",
|
||||
"dependsOn": [
|
||||
"npm: protos"
|
||||
],
|
||||
"dependsOn": ["npm: protos"],
|
||||
"presentation": {
|
||||
"group": "watch",
|
||||
"reveal": "always"
|
||||
@@ -123,9 +103,7 @@
|
||||
],
|
||||
"isBackground": true,
|
||||
"label": "npm: dev:webview",
|
||||
"dependsOn": [
|
||||
"npm: protos"
|
||||
],
|
||||
"dependsOn": ["npm: protos"],
|
||||
"presentation": {
|
||||
"group": "watch",
|
||||
"reveal": "always"
|
||||
@@ -140,30 +118,10 @@
|
||||
"type": "npm",
|
||||
"script": "watch:esbuild",
|
||||
"group": "build",
|
||||
"problemMatcher": {
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^✘ \\[ERROR\\] (.*)$",
|
||||
"message": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\s+(.*):(\\d+):(\\d+):$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": "^\\[watch\\] build started$",
|
||||
"endsPattern": "^\\[watch\\] build finished$"
|
||||
}
|
||||
},
|
||||
"problemMatcher": "$esbuild-watch",
|
||||
"isBackground": true,
|
||||
"label": "npm: watch:esbuild",
|
||||
"dependsOn": [
|
||||
"npm: protos"
|
||||
],
|
||||
"dependsOn": ["npm: protos"],
|
||||
"presentation": {
|
||||
"group": "watch",
|
||||
"reveal": "always"
|
||||
@@ -178,30 +136,10 @@
|
||||
"type": "npm",
|
||||
"script": "watch:esbuild:test",
|
||||
"group": "build",
|
||||
"problemMatcher": {
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^✘ \\[ERROR\\] (.*)$",
|
||||
"message": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\s+(.*):(\\d+):(\\d+):$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": "^\\[watch\\] build started$",
|
||||
"endsPattern": "^\\[watch\\] build finished$"
|
||||
}
|
||||
},
|
||||
"problemMatcher": "$esbuild-watch",
|
||||
"isBackground": true,
|
||||
"label": "npm: watch:esbuild:test",
|
||||
"dependsOn": [
|
||||
"npm: protos"
|
||||
],
|
||||
"dependsOn": ["npm: protos"],
|
||||
"presentation": {
|
||||
"group": "watch",
|
||||
"reveal": "always"
|
||||
@@ -220,9 +158,7 @@
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"label": "npm: watch:tsc",
|
||||
"dependsOn": [
|
||||
"npm: protos"
|
||||
],
|
||||
"dependsOn": ["npm: protos"],
|
||||
"presentation": {
|
||||
"group": "watch",
|
||||
"reveal": "always"
|
||||
@@ -233,9 +169,7 @@
|
||||
"script": "watch-tests",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"dependsOn": [
|
||||
"npm: protos"
|
||||
],
|
||||
"dependsOn": ["npm: protos"],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"group": "watchers"
|
||||
@@ -244,45 +178,13 @@
|
||||
},
|
||||
{
|
||||
"label": "tasks: watch-tests",
|
||||
"dependsOn": [
|
||||
"npm: protos",
|
||||
"npm: watch",
|
||||
"npm: watch-tests"
|
||||
],
|
||||
"dependsOn": ["npm: protos", "npm: watch", "npm: watch-tests"],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "stop",
|
||||
"command": "echo ${input:terminate}",
|
||||
"type": "shell"
|
||||
},
|
||||
{
|
||||
"label": "clean-tmp-user",
|
||||
"type": "shell",
|
||||
"dependsOn": [
|
||||
"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": [
|
||||
|
||||
+8
-36
@@ -1,44 +1,24 @@
|
||||
# Default
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
.worktrees/**
|
||||
CLAUDE.local.md
|
||||
out/
|
||||
dist-standalone/
|
||||
node_modules/
|
||||
out/**
|
||||
node_modules/**
|
||||
src/**
|
||||
standalone/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
esbuild.js
|
||||
vsc-extension-quickstart.md
|
||||
tsconfig*.json
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
**/.vscode-test.*
|
||||
eslint-rules/**
|
||||
.github/**
|
||||
.husky/**
|
||||
.env
|
||||
|
||||
# cli
|
||||
cli/**
|
||||
|
||||
# Custom
|
||||
**/demo.gif
|
||||
demo.gif
|
||||
.nvmrc
|
||||
.gitattributes
|
||||
.prettierignore
|
||||
.husky/
|
||||
.github/
|
||||
eslint-rules/
|
||||
old_docs/
|
||||
evals/
|
||||
.codespellrc
|
||||
.mocharc.json
|
||||
buf.yaml
|
||||
.clinerules/
|
||||
|
||||
# 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/**
|
||||
@@ -52,25 +32,17 @@ webview-ui/node_modules/**
|
||||
|
||||
# Ignore docs
|
||||
docs/**
|
||||
old_docs/**
|
||||
|
||||
# Fix issue where codicons don't get packaged (https://github.com/microsoft/vscode-extension-samples/issues/692)
|
||||
!node_modules/@vscode/codicons/dist/codicon.css
|
||||
!node_modules/@vscode/codicons/dist/codicon.ttf
|
||||
|
||||
# Include KaTeX CSS and fonts for LaTeX rendering
|
||||
!webview-ui/node_modules/katex/dist/katex.min.css
|
||||
!webview-ui/node_modules/katex/dist/fonts/**
|
||||
|
||||
# Include default themes JSON files used in getTheme
|
||||
!src/integrations/theme/default-themes/**
|
||||
|
||||
# Include icons
|
||||
!assets/icons/**
|
||||
|
||||
# Ignore E2E build files
|
||||
e2e-build.mjs
|
||||
e2e.vsix
|
||||
test-results/
|
||||
|
||||
# Ignore Storybook files
|
||||
**/*.stories.tsx
|
||||
*storybook.log
|
||||
storybook-static
|
||||
**/StorybookDecorator.tsx
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.gitignore
|
||||
+338
-1790
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
@.clinerules/general.md
|
||||
@.clinerules/network.md
|
||||
@.clinerules/cli.md
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user