mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Merge branch 'main' of https://github.com/RooVetGit/Roo-Cline into feature/pupeteer-updates
This commit is contained in:
commit
8e9218cfbb
27 changed files with 1650 additions and 119 deletions
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
|
|
|
|||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
11
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,11 +0,0 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature Request
|
||||
url: https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop
|
||||
about: Share and vote on feature requests for Cline
|
||||
- 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
|
||||
86
.github/actions/ai-release-notes/action.yml
vendored
Normal file
86
.github/actions/ai-release-notes/action.yml
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
name: AI Release Notes
|
||||
description: Generate AI release notes using git and openai, outputs 'RELEASE_NOTES' and 'OPENAI_PROMPT'
|
||||
|
||||
inputs:
|
||||
OPENAI_API_KEY:
|
||||
required: true
|
||||
type: string
|
||||
GHA_PAT:
|
||||
required: true
|
||||
type: string
|
||||
model_name:
|
||||
required: false
|
||||
type: string
|
||||
default: gpt-4o-mini
|
||||
repo_path:
|
||||
required: false
|
||||
type: string
|
||||
custom_prompt:
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
git_ref:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
head_ref:
|
||||
required: false
|
||||
type: string
|
||||
default: main
|
||||
base_ref:
|
||||
required: false
|
||||
type: string
|
||||
default: main
|
||||
|
||||
outputs:
|
||||
RELEASE_NOTES:
|
||||
description: "AI generated release notes"
|
||||
value: ${{ steps.ai_release_notes.outputs.RELEASE_NOTES }}
|
||||
OPENAI_PROMPT:
|
||||
description: "Prompt used to generate release notes"
|
||||
value: ${{ steps.ai_prompt.outputs.OPENAI_PROMPT }}
|
||||
|
||||
env:
|
||||
GITHUB_REF: ${{ inputs.git_ref == '' && github.event.pull_request.head.ref || inputs.git_ref }}
|
||||
BASE_REF: ${{ inputs.base_ref == '' && github.base_ref || inputs.base_ref }}
|
||||
HEAD_REF: ${{ inputs.head_ref == '' && github.event.pull_request.head.sha || inputs.head_ref }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ inputs.repo_path }}
|
||||
token: ${{ inputs.GHA_PAT }}
|
||||
ref: ${{ env.GITHUB_REF }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set Workspace
|
||||
shell: bash
|
||||
run: |
|
||||
pip install tiktoken
|
||||
pip install pytz
|
||||
|
||||
# Github outputs: 'OPENAI_PROMPT'
|
||||
- name: Add Git Info to base prompt
|
||||
id: ai_prompt
|
||||
shell: bash
|
||||
env:
|
||||
BASE_REF: ${{ env.BASE_REF }}
|
||||
HEAD_SHA: ${{ env.HEAD_SHA }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_BODY: ${{ github.event.pull_request.body }}
|
||||
MODEL_NAME: ${{ inputs.model_name }}
|
||||
CUSTOM_PROMPT: ${{ inputs.custom_prompt }} # Default: ''
|
||||
run: python .github/scripts/release-notes-prompt.py
|
||||
|
||||
# Github outputs: 'RELEASE_NOTES'
|
||||
- name: Generate AI release notes
|
||||
id: ai_release_notes
|
||||
shell: bash
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ inputs.OPENAI_API_KEY }}
|
||||
CUSTOM_PROMPT: ${{ steps.ai_prompt.outputs.OPENAI_PROMPT }}
|
||||
MODEL_NAME: ${{ inputs.model_name }}
|
||||
run: python .github/scripts/ai-release-notes.py
|
||||
|
||||
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
28
.github/pull_request_template.md
vendored
Normal file
28
.github/pull_request_template.md
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<!-- **Note:** Consider creating PRs as a DRAFT. For early feedback and self-review. -->
|
||||
## Description
|
||||
|
||||
## Type of change
|
||||
<!-- Please ignore options that are not relevant -->
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
## How Has This Been Tested?
|
||||
<!-- Please describe the tests that you ran to verify your changes -->
|
||||
|
||||
## Checklist:
|
||||
<!-- Go over all the following points, and put an `x` in all the boxes that apply -->
|
||||
- [ ] My code follows the patterns of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
|
||||
## Additional context
|
||||
<!-- Add any other context or screenshots about the pull request here -->
|
||||
|
||||
## Related Issues
|
||||
<!-- List any related issues here. Use the GitHub issue linking syntax: #issue-number -->
|
||||
|
||||
## Reviewers
|
||||
<!-- @mention specific team members or individuals who should review this PR -->
|
||||
123
.github/scripts/ai-release-notes.py
vendored
Normal file
123
.github/scripts/ai-release-notes.py
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
"""
|
||||
AI-powered release notes generator that creates concise and informative release notes from git changes.
|
||||
|
||||
This script uses OpenAI's API to analyze git changes (summary, diff, and commit log) and generate
|
||||
well-formatted release notes in markdown. It focuses on important changes and their impact,
|
||||
particularly highlighting new types and schemas while avoiding repetitive information.
|
||||
|
||||
Environment Variables Required:
|
||||
OPENAI_API_KEY: OpenAI API key for authentication
|
||||
CHANGE_SUMMARY: Summary of changes made (optional if CUSTOM_PROMPT provided)
|
||||
CHANGE_DIFF: Git diff of changes (optional if CUSTOM_PROMPT provided)
|
||||
CHANGE_LOG: Git commit log (optional if CUSTOM_PROMPT provided)
|
||||
GITHUB_OUTPUT: Path to GitHub output file
|
||||
CUSTOM_PROMPT: Custom prompt to override default (optional)
|
||||
"""
|
||||
|
||||
import os
|
||||
import requests # type: ignore
|
||||
import json
|
||||
import tiktoken # type: ignore
|
||||
|
||||
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
|
||||
CHANGE_SUMMARY = os.environ.get('CHANGE_SUMMARY', '')
|
||||
CHANGE_DIFF = os.environ.get('CHANGE_DIFF', '')
|
||||
CHANGE_LOG = os.environ.get('CHANGE_LOG', '')
|
||||
GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT")
|
||||
OPEN_AI_BASE_URL = "https://api.openai.com/v1"
|
||||
OPEN_API_HEADERS = {"Authorization": f"Bearer {OPENAI_API_KEY}", "Content-Type": "application/json"}
|
||||
CUSTOM_PROMPT = os.environ.get('CUSTOM_PROMPT', '')
|
||||
MODEL_NAME = os.environ.get('MODEL_NAME', 'gpt-3.5-turbo-16k')
|
||||
|
||||
def num_tokens_from_string(string: str, model_name: str) -> int:
|
||||
"""
|
||||
Calculate the number of tokens in a text string for a specific model.
|
||||
|
||||
Args:
|
||||
string: The input text to count tokens for
|
||||
model_name: Name of the OpenAI model to use for token counting
|
||||
|
||||
Returns:
|
||||
int: Number of tokens in the input string
|
||||
"""
|
||||
encoding = tiktoken.encoding_for_model(model_name)
|
||||
num_tokens = len(encoding.encode(string))
|
||||
return num_tokens
|
||||
|
||||
def truncate_to_token_limit(text, max_tokens, model_name):
|
||||
"""
|
||||
Truncate text to fit within a maximum token limit for a specific model.
|
||||
|
||||
Args:
|
||||
text: The input text to truncate
|
||||
max_tokens: Maximum number of tokens allowed
|
||||
model_name: Name of the OpenAI model to use for tokenization
|
||||
|
||||
Returns:
|
||||
str: Truncated text that fits within the token limit
|
||||
"""
|
||||
encoding = tiktoken.encoding_for_model(model_name)
|
||||
encoded = encoding.encode(text)
|
||||
truncated = encoded[:max_tokens]
|
||||
return encoding.decode(truncated)
|
||||
|
||||
def generate_release_notes(model_name):
|
||||
"""
|
||||
Generate release notes using OpenAI's API based on git changes.
|
||||
|
||||
Uses the GPT-3.5-turbo model to analyze change summary, commit log, and code diff
|
||||
to generate concise and informative release notes in markdown format. The notes
|
||||
focus on important changes and their impact, with sections for new types/schemas
|
||||
and other updates.
|
||||
|
||||
Returns:
|
||||
str: Generated release notes in markdown format
|
||||
|
||||
Raises:
|
||||
requests.exceptions.RequestException: If the OpenAI API request fails
|
||||
"""
|
||||
max_tokens = 14000 # Reserve some tokens for the response
|
||||
|
||||
# Truncate inputs if necessary to fit within token limits
|
||||
change_summary = '' if CUSTOM_PROMPT else truncate_to_token_limit(CHANGE_SUMMARY, 1000, model_name)
|
||||
change_log = '' if CUSTOM_PROMPT else truncate_to_token_limit(CHANGE_LOG, 2000, model_name)
|
||||
change_diff = '' if CUSTOM_PROMPT else truncate_to_token_limit(CHANGE_DIFF, max_tokens - num_tokens_from_string(change_summary, model_name) - num_tokens_from_string(change_log, model_name) - 1000, model_name)
|
||||
|
||||
url = f"{OPEN_AI_BASE_URL}/chat/completions"
|
||||
|
||||
# Construct prompt for OpenAI API
|
||||
openai_prompt = CUSTOM_PROMPT if CUSTOM_PROMPT else f"""Based on the following summary of changes, commit log and code diff, please generate concise and informative release notes:
|
||||
Summary of changes:
|
||||
{change_summary}
|
||||
Commit log:
|
||||
{change_log}
|
||||
Code Diff:
|
||||
{json.dumps(change_diff)}
|
||||
"""
|
||||
|
||||
data = {
|
||||
"model": model_name,
|
||||
"messages": [{"role": "user", "content": openai_prompt}],
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 1000,
|
||||
}
|
||||
|
||||
print("----------------------------------------------------------------------------------------------------------")
|
||||
print("POST request to OpenAI")
|
||||
print("----------------------------------------------------------------------------------------------------------")
|
||||
ai_response = requests.post(url, headers=OPEN_API_HEADERS, json=data)
|
||||
print(f"Status Code: {str(ai_response.status_code)}")
|
||||
print(f"Response: {ai_response.text}")
|
||||
ai_response.raise_for_status()
|
||||
|
||||
return ai_response.json()["choices"][0]["message"]["content"]
|
||||
|
||||
release_notes = generate_release_notes(MODEL_NAME)
|
||||
print("----------------------------------------------------------------------------------------------------------")
|
||||
print("OpenAI generated release notes")
|
||||
print("----------------------------------------------------------------------------------------------------------")
|
||||
print(release_notes)
|
||||
|
||||
# Write the release notes to GITHUB_OUTPUT
|
||||
with open(GITHUB_OUTPUT, "a") as outputs_file:
|
||||
outputs_file.write(f"RELEASE_NOTES<<EOF\n{release_notes}\nEOF")
|
||||
52
.github/scripts/get_prev_version_refs.py
vendored
Normal file
52
.github/scripts/get_prev_version_refs.py
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
def run_git_command(command):
|
||||
result = subprocess.getoutput(command)
|
||||
print(f"Git Command: {command}")
|
||||
print(f"Git Output: {result}")
|
||||
return result
|
||||
|
||||
def parse_merge_commit(line):
|
||||
# Parse merge commit messages like:
|
||||
# "355dc82 Merge pull request #71 from RooVetGit/better-error-handling"
|
||||
pattern = r"([a-f0-9]+)\s+Merge pull request #(\d+) from (.+)"
|
||||
match = re.match(pattern, line)
|
||||
if match:
|
||||
sha, pr_number, branch = match.groups()
|
||||
return {
|
||||
'sha': sha,
|
||||
'pr_number': pr_number,
|
||||
'branch': branch
|
||||
}
|
||||
return None
|
||||
|
||||
def get_version_refs():
|
||||
# Get the merge commits with full message
|
||||
command = 'git log --merges --pretty=oneline -n 3'
|
||||
result = run_git_command(command)
|
||||
|
||||
if result:
|
||||
commits = result.split('\n')
|
||||
if len(commits) >= 3:
|
||||
# Parse HEAD~1 (PR to generate notes for)
|
||||
head_info = parse_merge_commit(commits[1])
|
||||
# Parse HEAD~2 (previous PR to compare against)
|
||||
base_info = parse_merge_commit(commits[2])
|
||||
|
||||
if head_info and base_info:
|
||||
# Set output for GitHub Actions
|
||||
with open(os.environ['GITHUB_OUTPUT'], 'a') as gha_outputs:
|
||||
gha_outputs.write(f"head_ref={head_info['sha']}\n")
|
||||
gha_outputs.write(f"base_ref={base_info['sha']}")
|
||||
|
||||
print(f"Head ref (PR #{head_info['pr_number']}): {head_info['sha']}")
|
||||
print(f"Base ref (PR #{base_info['pr_number']}): {base_info['sha']}")
|
||||
return head_info, base_info
|
||||
|
||||
print("Could not find or parse sufficient merge history")
|
||||
return None, None
|
||||
|
||||
if __name__ == "__main__":
|
||||
head_info, base_info = get_version_refs()
|
||||
60
.github/scripts/overwrite_changeset_changelog.py
vendored
Executable file
60
.github/scripts/overwrite_changeset_changelog.py
vendored
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
"""
|
||||
This script updates a specific version's release notes section in CHANGELOG.md with new content.
|
||||
|
||||
The script:
|
||||
1. Takes a version number, changelog path, and new content as input from environment variables
|
||||
2. Finds the section in the changelog for the specified version
|
||||
3. Replaces the content between the current version header and the next version header
|
||||
(or end of file if it's the latest version) with the new content
|
||||
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 notes for
|
||||
PREV_VERSION: The previous version number (optional)
|
||||
NEW_CONTENT: The 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['NEW_CONTENT']
|
||||
|
||||
def overwrite_changelog_section(content: str):
|
||||
"""Replace a specific version section in the changelog content.
|
||||
|
||||
Args:
|
||||
content: The full changelog content as a string
|
||||
|
||||
Returns:
|
||||
The updated changelog content with the new section
|
||||
|
||||
Example:
|
||||
>>> content = "## 1.2.0\\nOld changes\\n## 1.1.0\\nOld changes"
|
||||
>>> NEW_CONTENT = "New changes"
|
||||
>>> overwrite_changelog_section(content)
|
||||
'## 1.2.0\\nNew changes\\n## 1.1.0\\nOld changes'
|
||||
"""
|
||||
# Find the section for the specified version
|
||||
version_pattern = f"## {VERSION}\n"
|
||||
print(f"latest version: {VERSION}")
|
||||
notes_start_index = content.find(version_pattern) + len(version_pattern)
|
||||
print(f"prev_version: {PREV_VERSION}")
|
||||
prev_version_pattern = f"## {PREV_VERSION}\n"
|
||||
notes_end_index = content.find(prev_version_pattern, notes_start_index) if PREV_VERSION and prev_version_pattern in content else len(content)
|
||||
return content[:notes_start_index] + f"{NEW_CONTENT}\n" + content[notes_end_index:]
|
||||
|
||||
with open(CHANGELOG_PATH, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
new_changelog = overwrite_changelog_section(content)
|
||||
|
||||
print(new_changelog)
|
||||
|
||||
# Write back to CHANGELOG.md
|
||||
with open(CHANGELOG_PATH, 'w') as f:
|
||||
f.write(new_changelog)
|
||||
64
.github/scripts/parse_changeset_changelog.py
vendored
Executable file
64
.github/scripts/parse_changeset_changelog.py
vendored
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
"""
|
||||
This script extracts the release notes section for a specific version from CHANGELOG.md.
|
||||
|
||||
The script:
|
||||
1. Takes a version number and changelog path as input from environment variables
|
||||
2. Finds the section in the changelog for the specified version
|
||||
3. Extracts the content between the current version header and the next version header
|
||||
(or end of file if it's the latest version)
|
||||
4. Outputs the extracted release notes to GITHUB_OUTPUT for use in creating GitHub releases
|
||||
|
||||
Environment Variables:
|
||||
GITHUB_OUTPUT: Path to GitHub Actions output file
|
||||
CHANGELOG_PATH: Path to the changelog file (defaults to 'CHANGELOG.md')
|
||||
VERSION: The version number to extract notes for
|
||||
"""
|
||||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT")
|
||||
CHANGELOG_PATH = os.environ.get("CHANGELOG_PATH", "CHANGELOG.md")
|
||||
VERSION = os.environ['VERSION']
|
||||
|
||||
def parse_changelog_section(content: str):
|
||||
"""Parse a specific version section from the changelog content.
|
||||
|
||||
Args:
|
||||
content: The full changelog content as a string
|
||||
|
||||
Returns:
|
||||
The formatted content for this version, or None if version not found
|
||||
|
||||
Example:
|
||||
>>> content = "## 1.2.0\\nChanges\\n## 1.1.0\\nOld changes"
|
||||
>>> parse_changelog_section(content)
|
||||
'Changes\\n'
|
||||
"""
|
||||
# Find the section for the specified version
|
||||
version_pattern = f"## {VERSION}\n"
|
||||
print(f"latest version: {VERSION}")
|
||||
notes_start_index = content.find(version_pattern) + len(version_pattern)
|
||||
prev_version = subprocess.getoutput("git show origin/main:package.json | grep '\"version\":' | cut -d'\"' -f4")
|
||||
print(f"prev_version: {prev_version}")
|
||||
prev_version_pattern = f"## {prev_version}\n"
|
||||
notes_end_index = content.find(prev_version_pattern, notes_start_index) if prev_version_pattern in content else len(content)
|
||||
|
||||
return content[notes_start_index:notes_end_index]
|
||||
|
||||
with open(CHANGELOG_PATH, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
formatted_content = parse_changelog_section(content)
|
||||
if not formatted_content:
|
||||
print(f"Version {VERSION} not found in changelog", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(formatted_content)
|
||||
|
||||
# Write the extracted release notes to GITHUB_OUTPUT
|
||||
with open(GITHUB_OUTPUT, "a") as gha_output:
|
||||
gha_output.write(f"release-notes<<EOF\n{formatted_content}\nEOF")
|
||||
125
.github/scripts/release-notes-prompt.py
vendored
Normal file
125
.github/scripts/release-notes-prompt.py
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import os
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
import tiktoken # type: ignore
|
||||
from datetime import datetime;
|
||||
from pytz import timezone
|
||||
|
||||
GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT")
|
||||
BASE_REF = os.getenv("BASE_REF", "main")
|
||||
HEAD_SHA = os.environ["HEAD_SHA"]
|
||||
PR_TITLE = os.environ["PR_TITLE"]
|
||||
PR_BODY = os.environ["PR_BODY"]
|
||||
EXISTING_NOTES = os.environ.get("EXISTING_NOTES", "null")
|
||||
MODEL_NAME = os.environ.get('MODEL_NAME', 'gpt-3.5-turbo-16k')
|
||||
CUSTOM_PROMPT = os.environ.get('CUSTOM_PROMPT', '')
|
||||
|
||||
def extract_description_section(pr_body):
|
||||
# Find content between ## Description and the next ## or end of text
|
||||
description_match = re.search(r'## Description\s*\n(.*?)(?=\n##|$)', pr_body, re.DOTALL)
|
||||
if description_match:
|
||||
content = description_match.group(1).strip()
|
||||
# Remove the comment line if it exists
|
||||
comment_pattern = r'\[comment\]:.+?\n'
|
||||
content = re.sub(comment_pattern, '', content)
|
||||
return content.strip()
|
||||
return ""
|
||||
|
||||
def extract_ellipsis_important(pr_body):
|
||||
# Find content between <!-- ELLIPSIS_HIDDEN --> and <!-- ELLIPSIS_HIDDEN --> that contains [!IMPORTANT]
|
||||
ellipsis_match = re.search(r'<!--\s*ELLIPSIS_HIDDEN\s*-->(.*?)<!--\s*ELLIPSIS_HIDDEN\s*-->', pr_body, re.DOTALL)
|
||||
if ellipsis_match:
|
||||
content = ellipsis_match.group(1).strip()
|
||||
important_match = re.search(r'\[!IMPORTANT\](.*?)(?=\[!|$)', content, re.DOTALL)
|
||||
if important_match:
|
||||
important_text = important_match.group(1).strip()
|
||||
important_text = re.sub(r'^-+\s*', '', important_text)
|
||||
return important_text.strip()
|
||||
return ""
|
||||
|
||||
def extract_coderabbit_summary(pr_body):
|
||||
# Find content between ## Summary by CodeRabbit and the next ## or end of text
|
||||
summary_match = re.search(r'## Summary by CodeRabbit\s*\n(.*?)(?=\n##|$)', pr_body, re.DOTALL)
|
||||
return summary_match.group(1).strip() if summary_match else ""
|
||||
|
||||
def num_tokens_from_string(string: str, model_name: str) -> int:
|
||||
"""
|
||||
Calculate the number of tokens in a text string for a specific model.
|
||||
|
||||
Args:
|
||||
string: The input text to count tokens for
|
||||
model_name: Name of the OpenAI model to use for token counting
|
||||
|
||||
Returns:
|
||||
int: Number of tokens in the input string
|
||||
"""
|
||||
encoding = tiktoken.encoding_for_model(model_name)
|
||||
num_tokens = len(encoding.encode(string))
|
||||
return num_tokens
|
||||
|
||||
def truncate_to_token_limit(text, max_tokens, model_name):
|
||||
"""
|
||||
Truncate text to fit within a maximum token limit for a specific model.
|
||||
|
||||
Args:
|
||||
text: The input text to truncate
|
||||
max_tokens: Maximum number of tokens allowed
|
||||
model_name: Name of the OpenAI model to use for tokenization
|
||||
|
||||
Returns:
|
||||
str: Truncated text that fits within the token limit
|
||||
"""
|
||||
encoding = tiktoken.encoding_for_model(model_name)
|
||||
encoded = encoding.encode(text)
|
||||
truncated = encoded[:max_tokens]
|
||||
return encoding.decode(truncated)
|
||||
|
||||
# Extract sections and combine into PR_OVERVIEW
|
||||
description = extract_description_section(PR_BODY)
|
||||
important = extract_ellipsis_important(PR_BODY)
|
||||
summary = extract_coderabbit_summary(PR_BODY)
|
||||
|
||||
PR_OVERVIEW = "\n\n".join(filter(None, [description, important, summary]))
|
||||
|
||||
# Get git information
|
||||
base_sha = subprocess.getoutput(f"git rev-parse origin/{BASE_REF}") if BASE_REF == 'main' else BASE_REF
|
||||
diff_overview = subprocess.getoutput(f"git diff {base_sha}..{HEAD_SHA} --name-status | awk '{{print $2}}' | sort | uniq -c | awk '{{print $2 \": \" $1 \" files changed\"}}'")
|
||||
git_log = subprocess.getoutput(f"git log {base_sha}..{HEAD_SHA} --pretty=format:'%h - %s (%an)' --reverse | head -n 50")
|
||||
git_diff = subprocess.getoutput(f"git diff {base_sha}..{HEAD_SHA} --minimal --abbrev --ignore-cr-at-eol --ignore-space-at-eol --ignore-space-change --ignore-all-space --ignore-blank-lines --unified=0 --diff-filter=ACDMRT")
|
||||
|
||||
max_tokens = 14000 # Reserve some tokens for the response
|
||||
changes_summary = truncate_to_token_limit(diff_overview, 1000, MODEL_NAME)
|
||||
git_logs = truncate_to_token_limit(git_log, 2000, MODEL_NAME)
|
||||
changes_diff = truncate_to_token_limit(git_diff, max_tokens - num_tokens_from_string(changes_summary, MODEL_NAME) - num_tokens_from_string(git_logs, MODEL_NAME) - 1000, MODEL_NAME)
|
||||
|
||||
# Get today's existing changelog if any
|
||||
existing_changelog = EXISTING_NOTES if EXISTING_NOTES != "null" else None
|
||||
existing_changelog_text = f"\nAdditional context:\n{existing_changelog}" if existing_changelog else ""
|
||||
TODAY = datetime.now(timezone('US/Eastern')).isoformat(sep=' ', timespec='seconds')
|
||||
|
||||
BASE_PROMPT = CUSTOM_PROMPT if CUSTOM_PROMPT else f"""Based on the following 'PR Information', please generate concise and informative release notes to be read by developers.
|
||||
Format the release notes with markdown, and always use this structure: a descriptive and very short title (no more than 8 words) with heading level 2, a paragraph with a summary of changes (no header), and if applicable, sections for '🚀 New Features & Improvements', '🐛 Bugs Fixed' and '🔧 Other Updates', with heading level 3, skip respectively the sections if not applicable.
|
||||
Finally include the following markdown comment with the PR merged date: <!-- PR_DATE: {TODAY} -->.
|
||||
Avoid being repetitive and focus on the most important changes and their impact, discard any mention of version bumps/updates, changeset files, environment variables or syntax updates.
|
||||
PR Information:"""
|
||||
|
||||
OPENAI_PROMPT = f"""{BASE_PROMPT}
|
||||
Git log summary:
|
||||
{changes_summary}
|
||||
Commit Messages:
|
||||
{git_logs}
|
||||
PR Title:
|
||||
{PR_TITLE}
|
||||
PR Overview:
|
||||
{PR_OVERVIEW}{existing_changelog_text}
|
||||
Code Diff:
|
||||
{json.dumps(changes_diff)}"""
|
||||
|
||||
print("OpenAI Prompt")
|
||||
print("----------------------------------------------------------------")
|
||||
print(OPENAI_PROMPT)
|
||||
|
||||
# Write the prompt to GITHUB_OUTPUT
|
||||
with open(GITHUB_OUTPUT, "a") as outputs_file:
|
||||
outputs_file.write(f"OPENAI_PROMPT<<EOF\n{OPENAI_PROMPT}\nEOF")
|
||||
215
.github/workflows/changeset-ai-releases.yml
vendored
Normal file
215
.github/workflows/changeset-ai-releases.yml
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
name: Changeset AI Release
|
||||
run-name: Changeset AI Release ${{ github.actor != 'R00-B0T' && '- Create PR' || '- Approve & Release' }}
|
||||
|
||||
# This workflow automates the release process by:
|
||||
# 1. Creating a version bump PR when changesets are merged to main
|
||||
# 2. Using AI to generate release notes for the version bump PR
|
||||
# 3. Auto-approving and merging the version bump PR
|
||||
# 4. Creating a GitHub release with the AI-generated notes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed, opened, synchronize, labeled]
|
||||
|
||||
env:
|
||||
REPO_PATH: ${{ github.repository }}
|
||||
GIT_REF: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
jobs:
|
||||
# Job 1: Create version bump PR when changesets are merged to main
|
||||
changeset-pr-version-bump:
|
||||
if: >
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.base.ref == 'main' &&
|
||||
github.actor != 'R00-B0T'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- 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: 20
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
# 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: Changeset Pull Request
|
||||
if: steps.check-changesets.outputs.new_changesets != '0'
|
||||
id: changesets
|
||||
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.CROSS_REPO_ACCESS_TOKEN }}
|
||||
|
||||
# Job 2: Process version bump PR created by R00-B0T
|
||||
changeset-pr-approve-merge:
|
||||
name: Auto approve and merge Bump version PRs
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
if: >
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.base.ref == 'main' &&
|
||||
github.actor == 'R00-B0T' &&
|
||||
contains(github.event.pull_request.title, 'Changeset version bump')
|
||||
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.CROSS_REPO_ACCESS_TOKEN }}
|
||||
fetch-depth: 0
|
||||
ref: ${{ env.GIT_REF }}
|
||||
|
||||
# Get current and previous versions for changelog processing
|
||||
- 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"
|
||||
|
||||
# Get previous version refs, GITHUB_OUTPUT: 'BASE_REF' and 'HEAD_REF'
|
||||
- name: Get Previous Version Refs
|
||||
id: version_refs
|
||||
run: python .github/scripts/get_prev_version_refs.py
|
||||
|
||||
# Generate release notes using OpenAI if not already edited, GITHUB_OUTPUT: 'RELEASE_NOTES' and 'OPENAI_PROMPT'
|
||||
- name: AI Release Notes
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'openai-edited') }}
|
||||
uses: ./.github/actions/ai-release-notes
|
||||
id: ai_release_notes
|
||||
with:
|
||||
GHA_PAT: ${{ secrets.CROSS_REPO_ACCESS_TOKEN }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
model_name: gpt-4o-mini
|
||||
repo_path: ${{ env.REPO_PATH }}
|
||||
base_ref: ${{ steps.version_refs.outputs.base_ref }}
|
||||
head_ref: ${{ steps.version_refs.outputs.head_ref }}
|
||||
|
||||
# Update CHANGELOG.md with AI-generated notes
|
||||
- name: Update Changeset Changelog
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'openai-edited') }}
|
||||
env:
|
||||
VERSION: ${{ steps.get_version.outputs.version }}
|
||||
PREV_VERSION: ${{ steps.get_version.outputs.prev_version }}
|
||||
NEW_CONTENT: ${{ steps.ai_release_notes.outputs.RELEASE_NOTES }}
|
||||
run: python .github/scripts/overwrite_changeset_changelog.py
|
||||
|
||||
# Commit and push changelog updates
|
||||
- name: Push Changelog updates
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'openai-edited') }}
|
||||
run: |
|
||||
git config user.name "R00-B0T"
|
||||
git config user.email github-actions@github.com
|
||||
git status
|
||||
|
||||
echo "Updating changelog.md..."
|
||||
git add CHANGELOG.md
|
||||
git commit -m "Updating changeset changelog"
|
||||
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
echo "Pushing to remote..."
|
||||
echo "--------------------------------------------------------------------------------"
|
||||
git push
|
||||
|
||||
# Add label to indicate OpenAI has processed this PR
|
||||
- name: Add openai-edited label
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'openai-edited') }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
labels: ['openai-edited']
|
||||
});
|
||||
|
||||
# Auto-approve PR once OpenAI has processed it
|
||||
- name: Auto approve PR
|
||||
if: contains(github.event.pull_request.labels.*.name, 'openai-edited')
|
||||
uses: hmarr/auto-approve-action@v4
|
||||
with:
|
||||
review-message: "I'm approving since it's a bump version PR"
|
||||
|
||||
# Enable auto-merge for the PR
|
||||
- name: Enable automerge on PR
|
||||
if: contains(github.event.pull_request.labels.*.name, 'openai-edited')
|
||||
run: gh pr merge --squash --auto ${{ github.event.pull_request.number }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.CROSS_REPO_ACCESS_TOKEN }}
|
||||
|
||||
# Job 3: Create GitHub release after version bump PR is merged
|
||||
github-release:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.base.ref == 'main' &&
|
||||
github.actor == 'R00-B0T' &&
|
||||
contains(github.event.pull_request.title, 'Changeset version bump')
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=$(git show HEAD:package.json | jq -r '.version')
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
# Extract release notes from CHANGELOG.md, GITHUB_OUTPUT: 'release-notes'
|
||||
- name: Parse CHANGELOG.md
|
||||
id: changelog
|
||||
env:
|
||||
CHANGELOG_PATH: CHANGELOG.md
|
||||
VERSION: ${{ steps.get_version.outputs.version }}
|
||||
run: python .github/scripts/parse_changeset_changelog.py
|
||||
|
||||
# Create GitHub release with extracted notes
|
||||
- name: Create or Update Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: v${{ steps.get_version.outputs.version }}
|
||||
name: Release v${{ steps.get_version.outputs.version }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
append_body: false
|
||||
make_latest: true
|
||||
body: ${{ steps.changelog.outputs.release-notes }}
|
||||
1
.github/workflows/marketplace-publish.yml
vendored
1
.github/workflows/marketplace-publish.yml
vendored
|
|
@ -8,6 +8,7 @@ jobs:
|
|||
publish-extension:
|
||||
if: ${{ github.actor == 'R00-B0T'}}
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor == 'R00-B0T'}}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -9,6 +9,6 @@ node_modules
|
|||
bin
|
||||
roo-cline-*.vsix
|
||||
|
||||
# Prompts
|
||||
# Local prompts
|
||||
prompts
|
||||
.clinerules
|
||||
|
|
|
|||
9
.husky/pre-commit
Normal file
9
.husky/pre-commit
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
|
||||
if [ "$branch" = "main" ]; then
|
||||
echo "You can't commit directly to main - please check out a branch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
npx lint-staged
|
||||
npm run compile
|
||||
17
.husky/pre-push
Normal file
17
.husky/pre-push
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
|
||||
if [ "$branch" = "main" ]; then
|
||||
echo "You can't push directly to main - please check out a branch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
npm run compile
|
||||
|
||||
# Check for new changesets
|
||||
NEW_CHANGESETS=$(find .changeset -name "*.md" ! -name "README.md" | wc -l | tr -d ' ')
|
||||
echo "Changeset files: $NEW_CHANGESETS"
|
||||
if [ "$NEW_CHANGESETS" == "0" ]; then
|
||||
echo "-------------------------------------------------------------------------------------"
|
||||
echo "Changes detected. Please run 'npm run changeset' to create a changeset if applicable."
|
||||
echo "-------------------------------------------------------------------------------------"
|
||||
fi
|
||||
54
CHANGELOG.md
54
CHANGELOG.md
|
|
@ -1,72 +1,86 @@
|
|||
# Roo Cline Changelog
|
||||
|
||||
## 2.1.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8dbd019: Larger Promp Text Input
|
||||
|
||||
## [2.1.20]
|
||||
|
||||
- Add Gemini 2.0
|
||||
|
||||
## [2.1.19]
|
||||
|
||||
- Better error handling for diff editing
|
||||
|
||||
## [2.1.18]
|
||||
|
||||
- Diff editing bugfix to handle Windows line endings
|
||||
- Diff editing bugfix to handle Windows line endings
|
||||
|
||||
## [2.1.17]
|
||||
|
||||
- Switch to search/replace diffs in experimental diff editing mode
|
||||
- Switch to search/replace diffs in experimental diff editing mode
|
||||
|
||||
## [2.1.16]
|
||||
|
||||
- Allow copying prompts from the history screen
|
||||
- Allow copying prompts from the history screen
|
||||
|
||||
## [2.1.15]
|
||||
|
||||
- Incorporate dbasclpy's [PR](https://github.com/RooVetGit/Roo-Cline/pull/54) to add support for gemini-exp-1206
|
||||
- Make it clear that diff editing is very experimental
|
||||
- Incorporate dbasclpy's [PR](https://github.com/RooVetGit/Roo-Cline/pull/54) to add support for gemini-exp-1206
|
||||
- Make it clear that diff editing is very experimental
|
||||
|
||||
## [2.1.14]
|
||||
|
||||
- Fix bug where diffs were not being applied correctly and try Aider's [unified diff prompt](https://github.com/Aider-AI/aider/blob/3995accd0ca71cea90ef76d516837f8c2731b9fe/aider/coders/udiff_prompts.py#L75-L105)
|
||||
- If diffs are enabled, automatically reject write_to_file commands that lead to truncated output
|
||||
- Fix bug where diffs were not being applied correctly and try Aider's [unified diff prompt](https://github.com/Aider-AI/aider/blob/3995accd0ca71cea90ef76d516837f8c2731b9fe/aider/coders/udiff_prompts.py#L75-L105)
|
||||
- If diffs are enabled, automatically reject write_to_file commands that lead to truncated output
|
||||
|
||||
## [2.1.13]
|
||||
|
||||
- Fix https://github.com/RooVetGit/Roo-Cline/issues/50 where sound effects were not respecting settings
|
||||
- Fix https://github.com/RooVetGit/Roo-Cline/issues/50 where sound effects were not respecting settings
|
||||
|
||||
## [2.1.12]
|
||||
|
||||
- Incorporate JoziGila's [PR](https://github.com/cline/cline/pull/158) to add support for editing through diffs
|
||||
- Incorporate JoziGila's [PR](https://github.com/cline/cline/pull/158) to add support for editing through diffs
|
||||
|
||||
## [2.1.11]
|
||||
|
||||
- Incorporate lloydchang's [PR](https://github.com/RooVetGit/Roo-Cline/pull/42) to add support for OpenRouter compression
|
||||
- Incorporate lloydchang's [PR](https://github.com/RooVetGit/Roo-Cline/pull/42) to add support for OpenRouter compression
|
||||
|
||||
## [2.1.10]
|
||||
|
||||
- Incorporate HeavenOSK's [PR](https://github.com/cline/cline/pull/818) to add sound effects to Cline
|
||||
- Incorporate HeavenOSK's [PR](https://github.com/cline/cline/pull/818) to add sound effects to Cline
|
||||
|
||||
## [2.1.9]
|
||||
|
||||
- Add instructions for using .clinerules on the settings screen
|
||||
- Add instructions for using .clinerules on the settings screen
|
||||
|
||||
## [2.1.8]
|
||||
|
||||
- Roo Cline now allows configuration of which commands are allowed without approval!
|
||||
- Roo Cline now allows configuration of which commands are allowed without approval!
|
||||
|
||||
## [2.1.7]
|
||||
|
||||
- Updated extension icon and metadata
|
||||
- Updated extension icon and metadata
|
||||
|
||||
## [2.1.6]
|
||||
|
||||
- Roo Cline now runs in all VSCode-compatible editors
|
||||
- Roo Cline now runs in all VSCode-compatible editors
|
||||
|
||||
## [2.1.5]
|
||||
|
||||
- Fix bug in browser action approval
|
||||
- Fix bug in browser action approval
|
||||
|
||||
## [2.1.4]
|
||||
|
||||
- Roo Cline now can run side-by-side with Cline
|
||||
- Roo Cline now can run side-by-side with Cline
|
||||
|
||||
## [2.1.3]
|
||||
|
||||
- Roo Cline now allows browser actions without approval when `alwaysAllowBrowser` is true
|
||||
- Roo Cline now allows browser actions without approval when `alwaysAllowBrowser` is true
|
||||
|
||||
## [2.1.2]
|
||||
|
||||
- Support for auto-approval of write operations and command execution
|
||||
- Support for .clinerules custom instructions
|
||||
- Support for auto-approval of write operations and command execution
|
||||
- Support for .clinerules custom instructions
|
||||
|
|
|
|||
70
README.md
70
README.md
|
|
@ -14,11 +14,12 @@ Here's an example of Roo-Cline autonomously creating a snake game with "Always a
|
|||
|
||||
https://github.com/user-attachments/assets/c2bb31dc-e9b2-4d73-885d-17f1471a4987
|
||||
|
||||
### Installation
|
||||
If you want to install the latest extension from the Marketplace, just search for "Roo Cline" in your VSCode-compatible editor's Extensions panel (Cmd/Ctrl+Shift+X).
|
||||
## Contributing
|
||||
To contribute to the project, start by exploring [open issues](https://github.com/RooVetGit/Roo-Cline/issues) or checking our [feature request board](https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop). We'd also love to have you join our [Discord](https://discord.gg/cline) to share ideas and connect with other contributors.
|
||||
|
||||
<details>
|
||||
<summary>Local Setup</summary>
|
||||
|
||||
### Testing Builds
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
npm run install:all
|
||||
|
|
@ -40,7 +41,12 @@ If you want to install the latest extension from the Marketplace, just search fo
|
|||
# Ex: code --install-extension bin/roo-cline-2.0.1.vsix
|
||||
```
|
||||
|
||||
### Publishing
|
||||
5. Launch by pressing `F5` (or `Run`->`Start Debugging`) to open a new VSCode window with the extension loaded. (You may need to install the [esbuild problem matchers extension](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers) if you run into issues building the project.)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Publishing</summary>
|
||||
We use [changesets](https://github.com/changesets/changesets) for versioning and publishing this package. To make changes:
|
||||
|
||||
1. Create a PR with your changes
|
||||
|
|
@ -54,34 +60,15 @@ Once your merge is successful:
|
|||
- The release workflow will automatically create a new "Changeset version bump" PR
|
||||
- This PR will:
|
||||
- Update the version based on your changeset
|
||||
- Update the CHANGELOG.md file
|
||||
- Update the `CHANGELOG.md` file
|
||||
- Create a git tag
|
||||
- The PR will be automatically approved and merged
|
||||
- A new version will be published
|
||||
- A new version and git release will be published
|
||||
|
||||
### Browser Actions
|
||||
</details>
|
||||
|
||||
The browser action tool allows Cline to interact with web pages through a Puppeteer-controlled browser. This can be used in two modes:
|
||||
|
||||
#### Interactive Mode
|
||||
- Triggered by adding the string `interactive mode` to your prompt, it also requires the string `(browserPort = 7333)`, where the port number can be modified.
|
||||
- Requires a browser instance running on the specified port, ex: `/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary --remote-debugging-port=7333`
|
||||
- Useful for debugging and development scenarios where you need to interact with a running browser
|
||||
|
||||
#### Non-Interactive Mode
|
||||
- Default mode when no `interactive mode` string is present in your prompt
|
||||
- Launches a new browser instance for each session
|
||||
- Better suited for quick simple browser tasks, ex: testing a URL
|
||||
|
||||
The browser action, in both modes, supports operations like:
|
||||
- Launching a browser at a specified URL
|
||||
- Clicking elements at specific coordinates
|
||||
- Typing text
|
||||
- Scrolling the page
|
||||
- Capturing screenshots and console logs
|
||||
- Closing the browser
|
||||
|
||||
Each browser action provides feedback through screenshots and console logs, allowing for detailed interaction tracking and debugging.
|
||||
## Stay Updated!
|
||||
Subscribe to our [Github releases](https://github.com/RooVetGit/Roo-Cline/releases) to keep up with the latest updates! You can also view our [CHANGELOG.md](Roo-Cline/CHANGELOG.md) for more details.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -181,30 +168,3 @@ Try asking Cline to "test the app", and watch as he runs a command like `npm run
|
|||
- **`@problems`:** Add workspace errors and warnings ('Problems' panel) for Cline to fix
|
||||
- **`@file`:** Adds a file's contents so you don't have to waste API requests approving read file (+ type to search files)
|
||||
- **`@folder`:** Adds folder's files all at once to speed up your workflow even more
|
||||
|
||||
## Contributing
|
||||
|
||||
To contribute to the project, start by exploring [open issues](https://github.com/cline/cline/issues) or checking our [feature request board](https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop). We'd also love to have you join our [Discord](https://discord.gg/cline) to share ideas and connect with other contributors. If you're interested in joining the team, check out our [careers page](https://cline.bot/join-us)!
|
||||
|
||||
<details>
|
||||
<summary>Local Development Instructions</summary>
|
||||
|
||||
1. Clone the repository _(Requires [git-lfs](https://git-lfs.com/))_:
|
||||
```bash
|
||||
git clone https://github.com/cline/cline.git
|
||||
```
|
||||
2. Open the project in VSCode:
|
||||
```bash
|
||||
code cline
|
||||
```
|
||||
3. Install the necessary dependencies for the extension and webview-gui:
|
||||
```bash
|
||||
npm run install:all
|
||||
```
|
||||
4. Launch by pressing `F5` (or `Run`->`Start Debugging`) to open a new VSCode window with the extension loaded. (You may need to install the [esbuild problem matchers extension](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers) if you run into issues building the project.)
|
||||
|
||||
</details>
|
||||
|
||||
## License
|
||||
|
||||
[Apache 2.0 © 2024 Cline Bot Inc.](./LICENSE)
|
||||
|
|
|
|||
609
package-lock.json
generated
609
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "roo-cline",
|
||||
"version": "2.1.18",
|
||||
"version": "2.1.21",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "roo-cline",
|
||||
"version": "2.1.18",
|
||||
"version": "2.1.21",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/bedrock-sdk": "^0.10.2",
|
||||
"@anthropic-ai/sdk": "^0.26.0",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
"web-tree-sitter": "^0.22.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.27.1",
|
||||
"@changesets/cli": "^2.27.10",
|
||||
"@types/diff": "^5.2.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/mocha": "^10.0.7",
|
||||
|
|
@ -53,7 +53,9 @@
|
|||
"@vscode/test-electron": "^2.4.0",
|
||||
"esbuild": "^0.24.0",
|
||||
"eslint": "^8.57.0",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.7.0",
|
||||
"lint-staged": "^15.2.11",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.4.5"
|
||||
|
|
@ -7332,6 +7334,45 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
|
||||
"integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"slice-ansi": "^5.0.0",
|
||||
"string-width": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cli-truncate/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
|
|
@ -7443,6 +7484,12 @@
|
|||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
|
|
@ -8077,6 +8124,18 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/environment": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
|
||||
"integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
|
|
@ -8505,6 +8564,12 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
|
|
@ -9009,6 +9074,18 @@
|
|||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-east-asian-width": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
|
||||
"integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
|
|
@ -9382,6 +9459,21 @@
|
|||
"ms": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/husky": {
|
||||
"version": "9.1.7",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
||||
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"husky": "bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/typicode"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
|
|
@ -11022,6 +11114,18 @@
|
|||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lilconfig": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antonk52"
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
|
|
@ -11036,6 +11140,257 @@
|
|||
"uc.micro": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged": {
|
||||
"version": "15.2.11",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.11.tgz",
|
||||
"integrity": "sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chalk": "~5.3.0",
|
||||
"commander": "~12.1.0",
|
||||
"debug": "~4.4.0",
|
||||
"execa": "~8.0.1",
|
||||
"lilconfig": "~3.1.3",
|
||||
"listr2": "~8.2.5",
|
||||
"micromatch": "~4.0.8",
|
||||
"pidtree": "~0.6.0",
|
||||
"string-argv": "~0.3.2",
|
||||
"yaml": "~2.6.1"
|
||||
},
|
||||
"bin": {
|
||||
"lint-staged": "bin/lint-staged.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/lint-staged"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/chalk": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/commander": {
|
||||
"version": "12.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/execa": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
|
||||
"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"get-stream": "^8.0.1",
|
||||
"human-signals": "^5.0.0",
|
||||
"is-stream": "^3.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^5.1.0",
|
||||
"onetime": "^6.0.0",
|
||||
"signal-exit": "^4.1.0",
|
||||
"strip-final-newline": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/get-stream": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
|
||||
"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/human-signals": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
|
||||
"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=16.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/is-stream": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
|
||||
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/mimic-fn": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
|
||||
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/npm-run-path": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
|
||||
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/onetime": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
|
||||
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-fn": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/path-key": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
|
||||
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/pidtree": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
|
||||
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"pidtree": "bin/pidtree.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/strip-final-newline": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
|
||||
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2": {
|
||||
"version": "8.2.5",
|
||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz",
|
||||
"integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cli-truncate": "^4.0.0",
|
||||
"colorette": "^2.0.20",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"log-update": "^6.1.0",
|
||||
"rfdc": "^1.4.1",
|
||||
"wrap-ansi": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/listr2/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/wrap-ansi": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
|
||||
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"string-width": "^7.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/load-json-file": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
|
||||
|
|
@ -11122,6 +11477,169 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
|
||||
"integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-escapes": "^7.0.0",
|
||||
"cli-cursor": "^5.0.0",
|
||||
"slice-ansi": "^7.1.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"wrap-ansi": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/ansi-escapes": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
|
||||
"integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"environment": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/cli-cursor": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
||||
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"restore-cursor": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/log-update/node_modules/is-fullwidth-code-point": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
|
||||
"integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"get-east-asian-width": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/onetime": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
|
||||
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mimic-function": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/restore-cursor": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
|
||||
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"onetime": "^7.0.0",
|
||||
"signal-exit": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/slice-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"is-fullwidth-code-point": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/wrap-ansi": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
|
||||
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"string-width": "^7.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/lop": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lop/-/lop-0.4.2.tgz",
|
||||
|
|
@ -11319,6 +11837,18 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-function": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
|
||||
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-response": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
||||
|
|
@ -12283,9 +12813,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/package-manager-detector": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.5.tgz",
|
||||
"integrity": "sha512-3dS7y28uua+UDbRCLBqltMBrbI+A5U2mI9YuxHRxIWYmLj3DwntEBmERYzIAQ4DMeuCUOBSak7dBHHoXKpOTYQ==",
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.7.tgz",
|
||||
"integrity": "sha512-g4+387DXDKlZzHkP+9FLt8yKj8+/3tOkPv7DVTJGGRm00RkEWgqbFstX1mXJ4M0VDYhUqsTOiISqNOJnhAu3PQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/pako": {
|
||||
|
|
@ -13230,6 +13760,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rfdc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
|
|
@ -13593,6 +14129,46 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
|
||||
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.0.0",
|
||||
"is-fullwidth-code-point": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi/node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
|
||||
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
|
|
@ -13756,6 +14332,15 @@
|
|||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/string-argv": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
|
||||
"integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.6.19"
|
||||
}
|
||||
},
|
||||
"node_modules/string-length": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
||||
|
|
@ -15243,6 +15828,18 @@
|
|||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz",
|
||||
"integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
|
|
|
|||
13
package.json
13
package.json
|
|
@ -3,7 +3,7 @@
|
|||
"displayName": "Roo Cline",
|
||||
"description": "A fork of Cline, an autonomous coding agent, with some added experimental configuration and automation features.",
|
||||
"publisher": "RooVeterinaryInc",
|
||||
"version": "2.1.18",
|
||||
"version": "2.1.21",
|
||||
"icon": "assets/icons/rocket.png",
|
||||
"galleryBanner": {
|
||||
"color": "#617A91",
|
||||
|
|
@ -152,14 +152,14 @@
|
|||
"publish": "npm run build && changeset publish && npm install --package-lock-only",
|
||||
"version-packages": "changeset version && npm install --package-lock-only",
|
||||
"vscode:prepublish": "npm run package",
|
||||
"vsix": "npx vsce package --out bin",
|
||||
"vsix": "mkdir -p bin && npx vsce package --out bin",
|
||||
"watch": "npm-run-all -p watch:*",
|
||||
"watch:esbuild": "node esbuild.js --watch",
|
||||
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
|
||||
"watch-tests": "tsc -p . -w --outDir out"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.27.1",
|
||||
"@changesets/cli": "^2.27.10",
|
||||
"@types/diff": "^5.2.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/mocha": "^10.0.7",
|
||||
|
|
@ -170,7 +170,9 @@
|
|||
"@vscode/test-electron": "^2.4.0",
|
||||
"esbuild": "^0.24.0",
|
||||
"eslint": "^8.57.0",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.7.0",
|
||||
"lint-staged": "^15.2.11",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.4.5"
|
||||
|
|
@ -208,5 +210,10 @@
|
|||
"turndown": "^7.2.0",
|
||||
"vsce": "^2.15.0",
|
||||
"web-tree-sitter": "^0.22.6"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{ts,tsx}": [
|
||||
"npx eslint -c .eslintrc.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ export class OpenAiHandler implements ApiHandler {
|
|||
constructor(options: ApiHandlerOptions) {
|
||||
this.options = options
|
||||
// Azure API shape slightly differs from the core API shape: https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai
|
||||
if (this.options.openAiBaseUrl?.toLowerCase().includes("azure.com")) {
|
||||
const urlHost = new URL(this.options.openAiBaseUrl ?? "").host;
|
||||
if (urlHost === "azure.com" || urlHost.endsWith(".azure.com")) {
|
||||
this.client = new AzureOpenAI({
|
||||
baseURL: this.options.openAiBaseUrl,
|
||||
apiKey: this.options.openAiApiKey,
|
||||
|
|
|
|||
|
|
@ -1234,12 +1234,12 @@ export class Cline {
|
|||
pushToolResult(await this.sayAndCreateMissingParamError("apply_diff", "diff"))
|
||||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
|
||||
const absolutePath = path.resolve(cwd, relPath)
|
||||
const fileExists = await fileExistsAtPath(absolutePath)
|
||||
|
||||
if (!fileExists) {
|
||||
this.consecutiveMistakeCount++
|
||||
await this.say("error", `File does not exist at path: ${absolutePath}`)
|
||||
pushToolResult(`Error: File does not exist at path: ${absolutePath}`)
|
||||
break
|
||||
|
|
@ -1250,11 +1250,14 @@ export class Cline {
|
|||
// Apply the diff to the original content
|
||||
let newContent = this.diffStrategy?.applyDiff(originalContent, diffContent) ?? false
|
||||
if (newContent === false) {
|
||||
await this.say("error", `Error applying diff to file: ${absolutePath}`)
|
||||
pushToolResult(`Error applying diff to file: ${absolutePath}`)
|
||||
this.consecutiveMistakeCount++
|
||||
await this.say("error", `Unable to apply diff to file - contents are out of sync: ${absolutePath}`)
|
||||
pushToolResult(`Error applying diff to file: ${absolutePath} - contents are out of sync. Try re-reading the relevant lines of the file and applying the diff again.`)
|
||||
break
|
||||
}
|
||||
|
||||
this.consecutiveMistakeCount = 0
|
||||
|
||||
// Show diff view before asking for approval
|
||||
this.diffViewProvider.editType = "modify"
|
||||
await this.diffViewProvider.open(relPath);
|
||||
|
|
|
|||
|
|
@ -124,17 +124,18 @@ export class DiffViewProvider {
|
|||
edit.delete(document.uri, new vscode.Range(this.streamedLines.length, 0, document.lineCount, 0))
|
||||
await vscode.workspace.applyEdit(edit)
|
||||
}
|
||||
// Add empty last line if original content had one
|
||||
// Preserve empty last line if original content had one
|
||||
const hasEmptyLastLine = this.originalContent?.endsWith("\n")
|
||||
if (hasEmptyLastLine) {
|
||||
const accumulatedLines = accumulatedContent.split("\n")
|
||||
if (accumulatedLines[accumulatedLines.length - 1] !== "") {
|
||||
accumulatedContent += "\n"
|
||||
}
|
||||
if (hasEmptyLastLine && !accumulatedContent.endsWith("\n")) {
|
||||
accumulatedContent += "\n"
|
||||
}
|
||||
// Clear all decorations at the end (before applying final edit)
|
||||
this.fadedOverlayController.clear()
|
||||
this.activeLineController.clear()
|
||||
// Apply the final content
|
||||
const finalEdit = new vscode.WorkspaceEdit()
|
||||
finalEdit.replace(document.uri, new vscode.Range(0, 0, document.lineCount, 0), accumulatedContent)
|
||||
await vscode.workspace.applyEdit(finalEdit)
|
||||
// Clear all decorations at the end (after applying final edit)
|
||||
this.fadedOverlayController.clear()
|
||||
this.activeLineController.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -351,4 +352,4 @@ export class DiffViewProvider {
|
|||
this.streamedLines = []
|
||||
this.preDiagnostics = []
|
||||
}
|
||||
}
|
||||
}
|
||||
100
src/integrations/editor/__tests__/DiffViewProvider.test.ts
Normal file
100
src/integrations/editor/__tests__/DiffViewProvider.test.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { DiffViewProvider } from '../DiffViewProvider';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
// Mock vscode
|
||||
jest.mock('vscode', () => ({
|
||||
workspace: {
|
||||
applyEdit: jest.fn(),
|
||||
},
|
||||
window: {
|
||||
createTextEditorDecorationType: jest.fn(),
|
||||
},
|
||||
WorkspaceEdit: jest.fn().mockImplementation(() => ({
|
||||
replace: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
})),
|
||||
Range: jest.fn(),
|
||||
Position: jest.fn(),
|
||||
Selection: jest.fn(),
|
||||
TextEditorRevealType: {
|
||||
InCenter: 2,
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock DecorationController
|
||||
jest.mock('../DecorationController', () => ({
|
||||
DecorationController: jest.fn().mockImplementation(() => ({
|
||||
setActiveLine: jest.fn(),
|
||||
updateOverlayAfterLine: jest.fn(),
|
||||
clear: jest.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('DiffViewProvider', () => {
|
||||
let diffViewProvider: DiffViewProvider;
|
||||
const mockCwd = '/mock/cwd';
|
||||
let mockWorkspaceEdit: { replace: jest.Mock; delete: jest.Mock };
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockWorkspaceEdit = {
|
||||
replace: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
(vscode.WorkspaceEdit as jest.Mock).mockImplementation(() => mockWorkspaceEdit);
|
||||
|
||||
diffViewProvider = new DiffViewProvider(mockCwd);
|
||||
// Mock the necessary properties and methods
|
||||
(diffViewProvider as any).relPath = 'test.txt';
|
||||
(diffViewProvider as any).activeDiffEditor = {
|
||||
document: {
|
||||
uri: { fsPath: `${mockCwd}/test.txt` },
|
||||
getText: jest.fn(),
|
||||
lineCount: 10,
|
||||
},
|
||||
selection: {
|
||||
active: { line: 0, character: 0 },
|
||||
anchor: { line: 0, character: 0 },
|
||||
},
|
||||
edit: jest.fn().mockResolvedValue(true),
|
||||
revealRange: jest.fn(),
|
||||
};
|
||||
(diffViewProvider as any).activeLineController = { setActiveLine: jest.fn(), clear: jest.fn() };
|
||||
(diffViewProvider as any).fadedOverlayController = { updateOverlayAfterLine: jest.fn(), clear: jest.fn() };
|
||||
});
|
||||
|
||||
describe('update method', () => {
|
||||
it('should preserve empty last line when original content has one', async () => {
|
||||
(diffViewProvider as any).originalContent = 'Original content\n';
|
||||
await diffViewProvider.update('New content', true);
|
||||
|
||||
expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
'New content\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not add extra newline when accumulated content already ends with one', async () => {
|
||||
(diffViewProvider as any).originalContent = 'Original content\n';
|
||||
await diffViewProvider.update('New content\n', true);
|
||||
|
||||
expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
'New content\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not add newline when original content does not end with one', async () => {
|
||||
(diffViewProvider as any).originalContent = 'Original content';
|
||||
await diffViewProvider.update('New content', true);
|
||||
|
||||
expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
'New content'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -286,6 +286,14 @@ export const geminiModels = {
|
|||
inputPrice: 0,
|
||||
outputPrice: 0,
|
||||
},
|
||||
"gemini-2.0-flash-exp": {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0,
|
||||
outputPrice: 0,
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
|
||||
// OpenAI Native
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
|
|||
import ContextMenu from "./ContextMenu"
|
||||
import Thumbnails from "../common/Thumbnails"
|
||||
|
||||
declare const vscode: any;
|
||||
|
||||
interface ChatTextAreaProps {
|
||||
inputValue: string
|
||||
setInputValue: (value: string) => void
|
||||
|
|
@ -427,7 +429,62 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
opacity: textAreaDisabled ? 0.5 : 1,
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
}}>
|
||||
}}
|
||||
onDrop={async (e) => {
|
||||
console.log("onDrop called")
|
||||
e.preventDefault()
|
||||
const files = Array.from(e.dataTransfer.files)
|
||||
const text = e.dataTransfer.getData("text")
|
||||
if (text) {
|
||||
const newValue =
|
||||
inputValue.slice(0, cursorPosition) + text + inputValue.slice(cursorPosition)
|
||||
setInputValue(newValue)
|
||||
const newCursorPosition = cursorPosition + text.length
|
||||
setCursorPosition(newCursorPosition)
|
||||
setIntendedCursorPosition(newCursorPosition)
|
||||
return
|
||||
}
|
||||
const acceptedTypes = ["png", "jpeg", "webp"]
|
||||
const imageFiles = files.filter((file) => {
|
||||
const [type, subtype] = file.type.split("/")
|
||||
return type === "image" && acceptedTypes.includes(subtype)
|
||||
})
|
||||
if (!shouldDisableImages && imageFiles.length > 0) {
|
||||
const imagePromises = imageFiles.map((file) => {
|
||||
return new Promise<string | null>((resolve) => {
|
||||
const reader = new FileReader()
|
||||
reader.onloadend = () => {
|
||||
if (reader.error) {
|
||||
console.error("Error reading file:", reader.error)
|
||||
resolve(null)
|
||||
} else {
|
||||
const result = reader.result
|
||||
console.log("File read successfully", result)
|
||||
resolve(typeof result === "string" ? result : null)
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
})
|
||||
})
|
||||
const imageDataArray = await Promise.all(imagePromises)
|
||||
const dataUrls = imageDataArray.filter((dataUrl): dataUrl is string => dataUrl !== null)
|
||||
if (dataUrls.length > 0) {
|
||||
setSelectedImages((prevImages) => [...prevImages, ...dataUrls].slice(0, MAX_IMAGES_PER_MESSAGE))
|
||||
if (typeof vscode !== 'undefined') {
|
||||
vscode.postMessage({
|
||||
type: 'draggedImages',
|
||||
dataUrls: dataUrls
|
||||
})
|
||||
}
|
||||
} else {
|
||||
console.warn("No valid images were processed")
|
||||
}
|
||||
}
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
{showContextMenu && (
|
||||
<div ref={contextMenuContainerRef}>
|
||||
<ContextMenu
|
||||
|
|
@ -508,7 +565,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
onHeightChange?.(height)
|
||||
}}
|
||||
placeholder={placeholderText}
|
||||
maxRows={10}
|
||||
minRows={2}
|
||||
maxRows={20}
|
||||
autoFocus={true}
|
||||
style={{
|
||||
width: "100%",
|
||||
|
|
@ -523,7 +581,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
resize: "none",
|
||||
overflowX: "hidden",
|
||||
overflowY: "scroll",
|
||||
scrollbarWidth: "none",
|
||||
// Since we have maxRows, when text is long enough it starts to overflow the bottom padding, appearing behind the thumbnails. To fix this, we use a transparent border to push the text up instead. (https://stackoverflow.com/questions/42631947/maintaining-a-padding-inside-of-text-area/52538410#52538410)
|
||||
// borderTop: "9px solid transparent",
|
||||
borderLeft: 0,
|
||||
|
|
@ -560,11 +617,11 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
|
|||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 23,
|
||||
right: 28,
|
||||
display: "flex",
|
||||
alignItems: "flex-center",
|
||||
alignItems: "flex-end",
|
||||
height: textAreaBaseHeight || 31,
|
||||
bottom: 9.5, // should be 10 but doesnt look good on mac
|
||||
bottom: 18,
|
||||
zIndex: 2,
|
||||
}}>
|
||||
<div style={{ display: "flex", flexDirection: "row", alignItems: "center" }}>
|
||||
|
|
|
|||
|
|
@ -689,7 +689,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
useEvent("wheel", handleWheel, window, { passive: true }) // passive improves scrolling performance
|
||||
|
||||
const placeholderText = useMemo(() => {
|
||||
const text = task ? "Type a message (@ to add context)..." : "Type your task here (@ to add context)..."
|
||||
const text = task ? "Type a message...\n(@ to add context, hold shift to drag in images)" : "Type your task here...\n(@ to add context, hold shift to drag in images)"
|
||||
return text
|
||||
}, [task])
|
||||
|
||||
|
|
|
|||
|
|
@ -462,10 +462,13 @@ export const highlight = (
|
|||
let i: number
|
||||
|
||||
for (i = 0; i < pathValue.length - 1; i++) {
|
||||
if (pathValue[i] === "__proto__" || pathValue[i] === "constructor") return
|
||||
obj = obj[pathValue[i]] as Record<string, any>
|
||||
}
|
||||
|
||||
obj[pathValue[i]] = value
|
||||
if (pathValue[i] !== "__proto__" && pathValue[i] !== "constructor") {
|
||||
obj[pathValue[i]] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Function to merge overlapping regions
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue