Merge branch 'main' into will/edit-w-checkpoints

This commit is contained in:
Will Li 2025-07-23 03:03:58 -07:00
commit 6657a7d894
57 changed files with 1504 additions and 148 deletions

View file

@ -0,0 +1,142 @@
<merge_resolver_workflow>
<mode_overview>
This mode resolves merge conflicts for a specific pull request by analyzing git history,
commit messages, and code changes to make intelligent resolution decisions. It receives
a PR number (e.g., "#123") and handles the entire conflict resolution process.
</mode_overview>
<initialization_steps>
<step number="1">
<action>Parse PR number from user input</action>
<details>
Extract the PR number from input like "#123" or "PR #123"
Validate that a PR number was provided
</details>
</step>
<step number="2">
<action>Fetch PR information</action>
<tools>
<tool>gh pr view [PR_NUMBER] --json title,body,headRefName,baseRefName</tool>
</tools>
<details>
Get PR title and description to understand the intent
Identify the source and target branches
</details>
</step>
<step number="3">
<action>Checkout PR branch and prepare for rebase</action>
<tools>
<tool>gh pr checkout [PR_NUMBER] --force</tool>
<tool>git fetch origin main</tool>
<tool>git rebase origin/main</tool>
</tools>
<details>
Force checkout the PR branch to ensure clean state
Fetch the latest main branch
Attempt to rebase onto main to reveal conflicts
</details>
</step>
<step number="4">
<action>Check for merge conflicts</action>
<tools>
<tool>git status --porcelain</tool>
<tool>git diff --name-only --diff-filter=U</tool>
</tools>
<details>
Identify files with merge conflicts (marked with 'UU')
Create a list of files that need resolution
</details>
</step>
</initialization_steps>
<main_workflow>
<phase name="conflict_analysis">
<description>Analyze each conflicted file to understand the changes</description>
<steps>
<step>Read the conflicted file to identify conflict markers</step>
<step>Extract the conflicting sections between <<<<<<< and >>>>>>></step>
<step>Run git blame on both sides of the conflict</step>
<step>Fetch commit messages and diffs for relevant commits</step>
<step>Analyze the intent behind each change</step>
</steps>
</phase>
<phase name="resolution_strategy">
<description>Determine the best resolution strategy for each conflict</description>
<steps>
<step>Categorize changes by intent (bugfix, feature, refactor, etc.)</step>
<step>Evaluate recency and relevance of changes</step>
<step>Check for structural overlap vs formatting differences</step>
<step>Identify if changes can be combined or if one should override</step>
<step>Consider test updates and related changes</step>
</steps>
</phase>
<phase name="conflict_resolution">
<description>Apply the resolution strategy to resolve conflicts</description>
<steps>
<step>For each conflict, apply the chosen resolution</step>
<step>Ensure proper escaping of conflict markers in diffs</step>
<step>Validate that resolved code is syntactically correct</step>
<step>Stage resolved files with git add</step>
</steps>
</phase>
<phase name="validation">
<description>Verify the resolution and prepare for commit</description>
<steps>
<step>Run git status to confirm all conflicts are resolved</step>
<step>Check for any compilation or syntax errors</step>
<step>Review the final diff to ensure sensible resolutions</step>
<step>Prepare a summary of resolution decisions</step>
</steps>
</phase>
</main_workflow>
<git_commands>
<command name="checkout_pr">
<syntax>gh pr checkout [PR_NUMBER] --force</syntax>
<purpose>Force checkout the PR branch to ensure clean state</purpose>
</command>
<command name="fetch_main">
<syntax>git fetch origin main</syntax>
<purpose>Get the latest main branch from origin</purpose>
</command>
<command name="rebase_main">
<syntax>git rebase origin/main</syntax>
<purpose>Rebase current branch onto main to reveal conflicts</purpose>
</command>
<command name="get_blame_info">
<syntax>git blame -L [start_line],[end_line] [commit_sha] -- [file_path]</syntax>
<purpose>Get commit information for specific lines</purpose>
</command>
<command name="get_commit_details">
<syntax>git show --format="%H%n%an%n%ae%n%ad%n%s%n%b" --no-patch [commit_sha]</syntax>
<purpose>Get commit metadata including message</purpose>
</command>
<command name="get_commit_diff">
<syntax>git show [commit_sha] -- [file_path]</syntax>
<purpose>Get the actual changes made in a commit</purpose>
</command>
<command name="check_merge_status">
<syntax>git ls-files -u</syntax>
<purpose>List unmerged files with stage information</purpose>
</command>
</git_commands>
<completion_criteria>
<criterion>All merge conflicts have been resolved</criterion>
<criterion>Resolved files have been staged</criterion>
<criterion>No syntax errors in resolved code</criterion>
<criterion>Resolution decisions are documented</criterion>
</completion_criteria>
</merge_resolver_workflow>

View file

@ -0,0 +1,165 @@
<merge_resolver_best_practices>
<general_principles>
<principle priority="high">
<name>Intent-Based Resolution</name>
<description>
Always prioritize understanding the intent behind changes rather than
just looking at the code differences. Commit messages, PR descriptions,
and issue references provide crucial context.
</description>
<rationale>
Code changes have purpose - bugfixes should be preserved, features
should be integrated properly, and refactors should maintain consistency.
</rationale>
<example>
<scenario>Conflict between a bugfix and a refactor</scenario>
<good>Apply the bugfix logic within the refactored structure</good>
<bad>Simply choose one side without considering both intents</bad>
</example>
</principle>
<principle priority="high">
<name>Preserve All Valuable Changes</name>
<description>
When possible, combine non-conflicting changes from both sides rather
than discarding one side entirely.
</description>
<rationale>
Both sides of a conflict often contain valuable changes that can coexist
if properly integrated.
</rationale>
</principle>
<principle priority="high">
<name>Escape Conflict Markers</name>
<description>
When using apply_diff or search_and_replace tools, always escape merge
conflict markers with backslashes to prevent parsing errors.
</description>
<example><![CDATA[
Correct: \<<<<<<< HEAD
Wrong: <<<<<<< HEAD
]]></example>
</principle>
<principle priority="medium">
<name>Consider Related Changes</name>
<description>
Look beyond the immediate conflict to understand related changes in
tests, documentation, or dependent code.
</description>
<rationale>
A change might seem isolated but could be part of a larger feature
or fix that spans multiple files.
</rationale>
</principle>
</general_principles>
<resolution_heuristics>
<heuristic category="bugfix_vs_feature">
<rule>Bugfixes generally take precedence over features</rule>
<reasoning>
Bugfixes address existing problems and should be preserved,
while features can be reintegrated around the fix.
</reasoning>
</heuristic>
<heuristic category="recent_vs_old">
<rule>More recent changes are often more relevant</rule>
<reasoning>
Recent changes likely reflect the current understanding of
requirements and may supersede older implementations.
</reasoning>
<exception>
When older changes are bugfixes or security patches that
haven't been addressed in newer code.
</exception>
</heuristic>
<heuristic category="test_updates">
<rule>Changes that include test updates are likely more complete</rule>
<reasoning>
Developers who update tests alongside code changes demonstrate
thoroughness and understanding of the impact.
</reasoning>
</heuristic>
<heuristic category="formatting_vs_logic">
<rule>Logic changes take precedence over formatting changes</rule>
<reasoning>
Formatting can be reapplied, but logic changes represent
functional improvements or fixes.
</reasoning>
</heuristic>
</resolution_heuristics>
<common_pitfalls>
<pitfall>
<description>Blindly choosing one side without analysis</description>
<why_problematic>
You might lose important changes or introduce regressions
</why_problematic>
<correct_approach>
Always analyze both sides using git blame and commit history
</correct_approach>
</pitfall>
<pitfall>
<description>Ignoring the PR description and context</description>
<why_problematic>
The PR description often explains the why behind changes,
which is crucial for proper resolution
</why_problematic>
<correct_approach>
Always fetch and read the PR information before resolving
</correct_approach>
</pitfall>
<pitfall>
<description>Not validating the resolved code</description>
<why_problematic>
Merged code might be syntactically incorrect or introduce
logical errors
</why_problematic>
<correct_approach>
Always check for syntax errors and review the final diff
</correct_approach>
</pitfall>
<pitfall>
<description>Not escaping conflict markers in diffs</description>
<why_problematic>
Unescaped conflict markers (<<<<<<, =======, >>>>>>) in SEARCH
or REPLACE sections will be interpreted as actual diff syntax,
causing the apply_diff tool to fail or produce incorrect results
</why_problematic>
<correct_approach>
Always escape conflict markers with a backslash (\) when they
appear in the content you're searching for or replacing.
Example: \<<<<<<< HEAD instead of <<<<<<< HEAD
</correct_approach>
</pitfall>
</common_pitfalls>
<quality_checklist>
<category name="before_resolution">
<item>Fetch PR title and description for context</item>
<item>Identify all files with conflicts</item>
<item>Understand the overall change being merged</item>
</category>
<category name="during_resolution">
<item>Run git blame on conflicting sections</item>
<item>Read commit messages for intent</item>
<item>Consider if changes can be combined</item>
<item>Escape conflict markers in diffs</item>
</category>
<category name="after_resolution">
<item>Verify no conflict markers remain</item>
<item>Check for syntax/compilation errors</item>
<item>Review the complete diff</item>
<item>Document resolution decisions</item>
</category>
</quality_checklist>
</merge_resolver_best_practices>

View file

@ -0,0 +1,228 @@
<merge_resolver_tool_usage>
<tool_priorities>
<priority level="1">
<tool>execute_command</tool>
<when>For all git and gh CLI operations</when>
<why>Git commands provide the historical context needed for intelligent resolution</why>
</priority>
<priority level="2">
<tool>read_file</tool>
<when>To examine conflicted files and understand the conflict structure</when>
<why>Need to see the actual conflict markers and code</why>
</priority>
<priority level="3">
<tool>apply_diff or search_and_replace</tool>
<when>To resolve conflicts by replacing conflicted sections</when>
<why>Precise editing of specific conflict blocks</why>
</priority>
</tool_priorities>
<tool_specific_guidance>
<tool name="execute_command">
<best_practices>
<practice>Always use gh CLI for GitHub operations instead of MCP tools</practice>
<practice>Chain git commands with && for efficiency</practice>
<practice>Use --format options for structured output</practice>
<practice>Capture command output for parsing</practice>
</best_practices>
<common_commands>
<command>
<purpose>Get PR information</purpose>
<syntax>gh pr view [PR_NUMBER] --json title,body,headRefName,baseRefName</syntax>
</command>
<command>
<purpose>Checkout PR branch</purpose>
<syntax>gh pr checkout [PR_NUMBER] --force</syntax>
</command>
<command>
<purpose>Fetch latest main branch</purpose>
<syntax>git fetch origin main</syntax>
</command>
<command>
<purpose>Rebase onto main to reveal conflicts</purpose>
<syntax>git rebase origin/main</syntax>
</command>
<command>
<purpose>Check conflict status</purpose>
<syntax>git status --porcelain | grep "^UU"</syntax>
</command>
<command>
<purpose>Get blame for specific lines</purpose>
<syntax>git blame -L [start],[end] HEAD -- [file] | cut -d' ' -f1</syntax>
</command>
<command>
<purpose>Get commit message</purpose>
<syntax>git log -1 --format="%s%n%n%b" [commit_sha]</syntax>
</command>
<command>
<purpose>Stage resolved file</purpose>
<syntax>git add [file_path]</syntax>
</command>
<command>
<purpose>Continue rebase after resolution</purpose>
<syntax>git rebase --continue</syntax>
</command>
</common_commands>
</tool>
<tool name="read_file">
<best_practices>
<practice>Read the entire conflicted file first to understand structure</practice>
<practice>Note line numbers of conflict markers for precise editing</practice>
<practice>Identify the pattern of conflicts (multiple vs single)</practice>
</best_practices>
<conflict_parsing>
<marker><<<<<<< HEAD - Start of current branch changes</marker>
<marker>======= - Separator between versions</marker>
<marker>>>>>>>> [branch] - End of incoming changes</marker>
</conflict_parsing>
</tool>
<tool name="apply_diff">
<best_practices>
<practice>Always escape conflict markers with backslash</practice>
<practice>Include enough context to ensure unique matches</practice>
<practice>Use :start_line: for precision</practice>
<practice>Combine multiple resolutions in one diff when possible</practice>
</best_practices>
<example><![CDATA[
<apply_diff>
<path>src/feature.ts</path>
<diff>
<<<<<<< SEARCH
:start_line:45
-------
\<<<<<<< HEAD
function oldImplementation() {
return "old";
}
\=======
function newImplementation() {
return "new";
}
\>>>>>>> feature-branch
=======
function mergedImplementation() {
// Combining both approaches
return "merged";
}
>>>>>>> REPLACE
</diff>
</apply_diff>
]]></example>
</tool>
<tool name="search_and_replace">
<best_practices>
<practice>Use for simple conflict resolutions</practice>
<practice>Enable regex mode for complex patterns</practice>
<practice>Always escape special characters</practice>
</best_practices>
<example><![CDATA[
<search_and_replace>
<path>src/config.ts</path>
<search>\<<<<<<< HEAD[\s\S]*?\>>>>>>> \w+</search>
<replace>// Resolved configuration
const config = {
// Merged settings from both branches
}</replace>
<use_regex>true</use_regex>
</search_and_replace>
]]></example>
</tool>
</tool_specific_guidance>
<tool_combination_patterns>
<pattern name="initialize_pr_resolution">
<sequence>
<step>execute_command - Get PR info with gh CLI</step>
<step>execute_command - Checkout PR with gh pr checkout --force</step>
<step>execute_command - Fetch origin main</step>
<step>execute_command - Rebase onto origin/main</step>
<step>execute_command - Check for conflicts with git status</step>
</sequence>
</pattern>
<pattern name="analyze_conflict">
<sequence>
<step>execute_command - List conflicted files</step>
<step>read_file - Examine conflict structure</step>
<step>execute_command - Git blame on conflict regions</step>
<step>execute_command - Fetch commit messages</step>
</sequence>
</pattern>
<pattern name="resolve_conflict">
<sequence>
<step>read_file - Get exact conflict content</step>
<step>apply_diff - Replace conflict with resolution</step>
<step>execute_command - Stage resolved file</step>
<step>execute_command - Verify resolution status</step>
</sequence>
</pattern>
<pattern name="complete_rebase">
<sequence>
<step>execute_command - Check all conflicts resolved</step>
<step>execute_command - Continue rebase with git rebase --continue</step>
<step>execute_command - Verify clean status</step>
</sequence>
</pattern>
</tool_combination_patterns>
<error_handling>
<scenario name="no_conflicts_after_rebase">
<description>Rebase completes without conflicts</description>
<approach>
Inform user that PR can be merged without conflicts
No resolution needed
</approach>
</scenario>
<scenario name="rebase_in_progress">
<description>A rebase is already in progress</description>
<approach>
Check status with git status
Either continue existing rebase or abort with git rebase --abort
</approach>
</scenario>
<scenario name="malformed_conflicts">
<description>Conflict markers are incomplete or nested</description>
<approach>
Use search_and_replace with careful regex patterns
Manual inspection may be required
</approach>
</scenario>
<scenario name="binary_conflicts">
<description>Binary files cannot be merged automatically</description>
<approach>
Identify which version to keep based on PR intent
Use git checkout --theirs or --ours
</approach>
</scenario>
<scenario name="escaped_markers">
<description>Code contains literal conflict marker strings</description>
<approach>
Extra careful escaping in diffs
Consider using search_and_replace with precise patterns
</approach>
</scenario>
</error_handling>
</merge_resolver_tool_usage>

View file

@ -0,0 +1,315 @@
<merge_resolver_example>
<scenario>
User provides PR #123 which has merge conflicts between a bugfix branch
and a feature branch that refactored the same code.
</scenario>
<user_request>
#123
</user_request>
<workflow>
<step number="1">
<description>Parse PR number and fetch PR information</description>
<tool_use><![CDATA[
<execute_command>
<command>gh pr view 123 --json title,body,headRefName,baseRefName</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
{
"title": "Fix memory leak in data processor",
"body": "This PR fixes a memory leak that occurs when processing large datasets. The leak was caused by event listeners not being properly removed.",
"headRefName": "fix/memory-leak",
"baseRefName": "main"
}
]]></expected_output>
<analysis>
The PR is a bugfix for a memory leak issue. This context will help
prioritize preserving the fix during conflict resolution.
</analysis>
</step>
<step number="2">
<description>Checkout PR branch and prepare for rebase</description>
<tool_use><![CDATA[
<execute_command>
<command>gh pr checkout 123 --force</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
Switched to branch 'fix/memory-leak'
Your branch is up to date with 'origin/fix/memory-leak'.
]]></expected_output>
<tool_use><![CDATA[
<execute_command>
<command>git fetch origin main</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
From github.com:user/repo
* branch main -> FETCH_HEAD
]]></expected_output>
<tool_use><![CDATA[
<execute_command>
<command>git rebase origin/main</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
Auto-merging src/data/processor.ts
CONFLICT (content): Merge conflict in src/data/processor.ts
Auto-merging src/data/processor.test.ts
CONFLICT (content): Merge conflict in src/data/processor.test.ts
error: could not apply abc123... Fix memory leak in data processor
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
]]></expected_output>
</step>
<step number="3">
<description>Check for merge conflicts</description>
<tool_use><![CDATA[
<execute_command>
<command>git status --porcelain | grep "^UU"</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
UU src/data/processor.ts
UU src/data/processor.test.ts
]]></expected_output>
<analysis>
Two files have conflicts: the main processor file and its test file.
Both need to be resolved before continuing the rebase.
</analysis>
</step>
<step number="4">
<description>Examine the conflicted processor file</description>
<tool_use><![CDATA[
<read_file>
<path>src/data/processor.ts</path>
</read_file>
]]></tool_use>
<expected_output><![CDATA[
45 | export class DataProcessor {
46 | private listeners: Map<string, EventListener> = new Map();
47 |
48 | <<<<<<< HEAD
49 | async processData(data: DataInput): Promise<ProcessedData> {
50 | // Refactored to use async/await
51 | const result = await this.transform(data);
52 | return result;
53 | }
54 | =======
55 | processData(data: any[]): ProcessedData {
56 | const listener = this.createListener();
57 | this.listeners.set(data.id, listener);
58 |
59 | return this.transform(data);
60 | }
61 | >>>>>>> abc123 (Fix memory leak in data processor)
62 |
63 | cleanup() {
64 | <<<<<<< HEAD
65 | // TODO: Implement cleanup
66 | =======
67 | // Fix: Clear all listeners to prevent memory leak
68 | this.listeners.forEach(listener => {
69 | listener.removeAllListeners();
70 | });
71 | this.listeners.clear();
72 | >>>>>>> abc123 (Fix memory leak in data processor)
73 | }
74 | }
]]></expected_output>
</step>
<step number="5">
<description>Analyze the conflicts using git blame</description>
<tool_use><![CDATA[
<execute_command>
<command>git blame -L 49,53 HEAD -- src/data/processor.ts | cut -d' ' -f1 | sort -u | xargs -I {} git log -1 --format="%h %s" {}</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
def456 Refactor: Convert processData to async/await pattern
]]></expected_output>
<tool_use><![CDATA[
<execute_command>
<command>git log -1 --format="%h %s" abc123</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
abc123 Fix: Add listener cleanup to prevent memory leak
]]></expected_output>
<analysis>
HEAD (main after rebase): Has async/await refactor but missing listener logic
Incoming (bugfix branch): Adds listener tracking and cleanup for memory leak fix
Resolution strategy: Combine both changes - keep async/await refactor
but include the listener tracking and cleanup logic.
</analysis>
</step>
<step number="6">
<description>Resolve the conflicts</description>
<tool_use><![CDATA[
<apply_diff>
<path>src/data/processor.ts</path>
<diff>
<<<<<<< SEARCH
:start_line:48
-------
\<<<<<<< HEAD
async processData(data: DataInput): Promise<ProcessedData> {
// Refactored to use async/await
const result = await this.transform(data);
return result;
}
\=======
processData(data: any[]): ProcessedData {
const listener = this.createListener();
this.listeners.set(data.id, listener);
return this.transform(data);
}
\>>>>>>> abc123 (Fix memory leak in data processor)
=======
async processData(data: DataInput): Promise<ProcessedData> {
// Create and track listener for cleanup (memory leak fix)
const listener = this.createListener();
this.listeners.set(data.id, listener);
// Refactored to use async/await
const result = await this.transform(data);
return result;
}
>>>>>>> REPLACE
<<<<<<< SEARCH
:start_line:63
-------
cleanup() {
\<<<<<<< HEAD
// TODO: Implement cleanup
\=======
// Fix: Clear all listeners to prevent memory leak
this.listeners.forEach(listener => {
listener.removeAllListeners();
});
this.listeners.clear();
\>>>>>>> abc123 (Fix memory leak in data processor)
}
=======
cleanup() {
// Fix: Clear all listeners to prevent memory leak
this.listeners.forEach(listener => {
listener.removeAllListeners();
});
this.listeners.clear();
}
>>>>>>> REPLACE
</diff>
</apply_diff>
]]></tool_use>
<explanation>
Combined both changes: kept the async/await refactor from main
while preserving the memory leak fix (listener tracking and cleanup)
from the bugfix branch.
</explanation>
</step>
<step number="7">
<description>Resolve test file conflicts</description>
<tool_use><![CDATA[
<read_file>
<path>src/data/processor.test.ts</path>
</read_file>
]]></tool_use>
<!-- Similar process for test file -->
<tool_use><![CDATA[
<apply_diff>
<path>src/data/processor.test.ts</path>
<diff>
<!-- Test resolution would go here -->
</diff>
</apply_diff>
]]></tool_use>
</step>
<step number="8">
<description>Stage resolved files and continue rebase</description>
<tool_use><![CDATA[
<execute_command>
<command>git add src/data/processor.ts src/data/processor.test.ts</command>
</execute_command>
]]></tool_use>
<tool_use><![CDATA[
<execute_command>
<command>git rebase --continue</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
[detached HEAD def789] Fix memory leak in data processor
2 files changed, 12 insertions(+), 2 deletions(-)
Successfully rebased and updated refs/heads/fix/memory-leak.
]]></expected_output>
</step>
<step number="9">
<description>Final validation</description>
<tool_use><![CDATA[
<execute_command>
<command>git status</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
On branch fix/memory-leak
Your branch is ahead of 'origin/fix/memory-leak' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
]]></expected_output>
<tool_use><![CDATA[
<execute_command>
<command>git diff origin/main --stat</command>
</execute_command>
]]></tool_use>
<expected_output><![CDATA[
src/data/processor.ts | 8 ++++++--
src/data/processor.test.ts | 4 ++--
2 files changed, 8 insertions(+), 4 deletions(-)
]]></expected_output>
</step>
</workflow>
<completion><![CDATA[
<attempt_completion>
<result>
Successfully resolved merge conflicts for PR #123 "Fix memory leak in data processor".
Resolution Summary:
- src/data/processor.ts: Combined the async/await refactor with the memory leak fix by preserving listener tracking and cleanup logic within the refactored async method
- src/data/processor.test.ts: Updated tests to work with the async implementation while maintaining memory leak test coverage
Both the feature refactor and the critical bugfix have been preserved in the resolution. The rebase has been completed successfully and the branch is ready to be pushed.
</result>
</attempt_completion>
]]></completion>
<key_takeaways>
<takeaway>Always checkout PR with --force and rebase to reveal conflicts</takeaway>
<takeaway>Fetch PR context to understand the intent of changes</takeaway>
<takeaway>Use git blame and commit messages to understand the history</takeaway>
<takeaway>Combine non-conflicting improvements when possible</takeaway>
<takeaway>Prioritize bugfixes while accommodating refactors</takeaway>
<takeaway>Complete the rebase process with git rebase --continue</takeaway>
<takeaway>Validate that both sets of changes work together</takeaway>
</key_takeaways>
</merge_resolver_example>

View file

@ -0,0 +1,153 @@
<merge_resolver_communication>
<tone_and_style>
<principle>Be direct and technical when explaining resolution decisions</principle>
<principle>Focus on the rationale behind each conflict resolution</principle>
<principle>Provide clear summaries of what was merged and why</principle>
<avoid>
<phrase>I'll help you resolve these conflicts...</phrase>
<phrase>Let me handle this for you...</phrase>
<phrase>Don't worry about the conflicts...</phrase>
</avoid>
<prefer>
<phrase>Analyzing PR #123 for merge conflicts...</phrase>
<phrase>Resolving conflicts based on commit history analysis...</phrase>
<phrase>Applied resolution strategy: [specific strategy]</phrase>
</prefer>
</tone_and_style>
<initial_response>
<structure>
<element>Acknowledge the PR number</element>
<element>State that you're fetching PR information</element>
<element>Indicate the analysis will begin</element>
</structure>
<example>
Fetching information for PR #123 to understand the context and identify merge conflicts...
</example>
</initial_response>
<progress_updates>
<when>During each major phase of resolution</when>
<format>
<update>Analyzing [X] conflicted files...</update>
<update>Running git blame on [file] to understand change history...</update>
<update>Resolving conflicts in [file] by [strategy]...</update>
<update>Validating resolved changes...</update>
</format>
<include_details>
<detail>Number of conflicts found</detail>
<detail>Files being processed</detail>
<detail>Resolution strategy being applied</detail>
</include_details>
</progress_updates>
<conflict_explanations>
<guideline>Explain each significant resolution decision</guideline>
<guideline>Reference specific commits when relevant</guideline>
<guideline>Justify why certain changes were kept or merged</guideline>
<format>
<explanation>
Conflict in [file]:
- HEAD: [brief description of changes]
- Incoming: [brief description of changes]
- Resolution: [what was decided and why]
</explanation>
</format>
</conflict_explanations>
<error_handling>
<scenario name="no_pr_number">
<response>
Expected a PR number (e.g., "#123" or "123"). Please provide the PR number to resolve conflicts for.
</response>
</scenario>
<scenario name="no_conflicts">
<response>
PR #[number] does not have any merge conflicts. The branch can be merged without conflict resolution.
</response>
</scenario>
<scenario name="pr_not_found">
<response>
Could not find PR #[number]. Please verify the PR number and ensure you have access to the repository.
</response>
</scenario>
<scenario name="complex_conflicts">
<response>
Found complex conflicts in [file] that require careful analysis. Examining commit history to determine the best resolution strategy...
</response>
</scenario>
</error_handling>
<completion_messages>
<structure>
<element>State that conflicts are resolved</element>
<element>Provide resolution summary</element>
<element>List files that were resolved</element>
<element>Mention key decisions made</element>
</structure>
<template><![CDATA[
Successfully resolved merge conflicts for PR #[number] "[title]".
Resolution Summary:
- [file1]: [brief description of resolution]
- [file2]: [brief description of resolution]
[Key decision explanation if applicable]
All conflicts have been resolved and files have been staged for commit.
]]></template>
<avoid>
<element>Questions about next steps</element>
<element>Offers to do additional work</element>
<element>Uncertain language about the resolution</element>
</avoid>
</completion_messages>
<decision_documentation>
<principle>Document why specific resolutions were chosen</principle>
<principle>Reference commit SHAs when they influenced decisions</principle>
<principle>Explain trade-offs when both sides had valid changes</principle>
<examples>
<example>
Preserved bugfix from commit abc123 while adapting it to the refactored structure from def456
</example>
<example>
Combined both implementations as they addressed different aspects of the same feature
</example>
<example>
Chose the more recent implementation as it included additional error handling
</example>
</examples>
</decision_documentation>
<special_cases>
<case name="binary_files">
<message>
Binary file conflict in [file]. Based on PR intent "[title]", choosing [which version] version.
</message>
</case>
<case name="deleted_vs_modified">
<message>
Conflict: [file] was deleted in one branch but modified in another. Based on the changes, [keeping/removing] the file because [reason].
</message>
</case>
<case name="whitespace_only">
<message>
Conflict in [file] involves only whitespace/formatting. Applying consistent formatting from [which] branch.
</message>
</case>
</special_cases>
</merge_resolver_communication>

View file

@ -45,7 +45,7 @@
<step>Determine if the PR is from a fork by checking 'gh pr view [PR_NUMBER] --repo [owner]/[repo] --json isCrossRepository'.</step>
<step>Apply code changes based on review feedback using file editing tools.</step>
<step>Fix failing tests by modifying test files or source code as needed.</step>
<step>For conflict resolution: Use GIT_EDITOR=true for non-interactive rebases, then resolve conflicts via file editing.</step>
<step>For conflict resolution: Delegate to merge-resolver mode using new_task with the PR number.</step>
<step>If changes affect user-facing content (i18n files, UI components, announcements), delegate translation updates using the new_task tool with translate mode.</step>
<step>Review modified files with 'git status --porcelain' to ensure no temporary files are included.</step>
<step>Stage files selectively using 'git add -u' (for modified tracked files) or 'git add <specific-files>' (for new files).</step>

View file

@ -41,33 +41,25 @@
<code_conventions>
<convention category="merge_conflicts">
<rule>How to correctly escape conflict markers when using apply_diff.</rule>
<rule>Delegate merge conflict resolution to the merge-resolver mode.</rule>
<template>
When removing merge conflict markers from files, you must **escape** them in your `SEARCH` section by prepending a backslash (`\`) at the beginning of the line. This prevents the system from mistaking them for actual diff syntax.
When merge conflicts are detected, do not attempt to resolve them manually. Instead, use the new_task tool to create a task for the merge-resolver mode:
**Correct Format Example:**
```
<<<<<<< SEARCH
content before
\<<<<<<< HEAD <-- Note the backslash here
content after
=======
replacement content
>>>>>>> REPLACE
```xml
<new_task>
<mode>merge-resolver</mode>
<message>#[PR_NUMBER]</message>
</new_task>
```
Without escaping, the system confuses your content with real diff markers.
The merge-resolver mode will:
- Checkout the PR branch
- Perform the rebase
- Intelligently resolve conflicts based on commit history and intent
- Push the resolved changes
- Return control back to pr-fixer mode
You may include multiple diff blocks in a single request, but if any of the following markers appear within your `SEARCH` or `REPLACE` content, they must be escaped:
```
\<<<<<<< SEARCH
\=======
\>>>>>>> REPLACE
```
Only these three need to be escaped when used in content.
This ensures consistent and intelligent conflict resolution across all PRs.
</template>
</convention>
</code_conventions>

View file

@ -27,32 +27,26 @@
<pattern name="detecting_conflicts">
<usage>Commands to detect merge conflicts.</usage>
<template>
<comment>Fetch latest main branch</comment>
<command tool="git">git fetch origin main</command>
<comment>Check if rebase would create conflicts</comment>
<command tool="git">git rebase --dry-run origin/main</command>
<comment>Check PR mergeable status</comment>
<command tool="gh">gh pr view <pr_number> --json mergeable,mergeStateStatus</command>
<comment>If mergeable is false or mergeStateStatus is CONFLICTING, delegate to merge-resolver</comment>
</template>
</pattern>
<pattern name="non_interactive_rebase">
<usage>Rebase operations using GIT_EDITOR to prevent interactive prompts.</usage>
<pattern name="delegating_conflict_resolution">
<usage>Delegate merge conflict resolution to the merge-resolver mode.</usage>
<template>
<command tool="git">git checkout <pr_branch></command>
<command tool="git">GIT_EDITOR=true git rebase main</command>
<comment>If conflicts occur, resolve them manually then use 'git rebase --continue'</comment>
<command tool="git">git push --force-with-lease <remote> <pr_branch></command>
<comment>When conflicts are detected, create a new task for merge-resolver</comment>
<command tool="new_task"><![CDATA[
<new_task>
<mode>merge-resolver</mode>
<message>#<pr_number></message>
</new_task>
]]></command>
<comment>Wait for merge-resolver to complete before continuing with other fixes</comment>
</template>
</pattern>
<pattern name="conflict_status_check">
<usage>Check current conflict status without interactive input.</usage>
<template>
<command tool="git">git status --porcelain</command>
<command tool="git">git diff --name-only --diff-filter=U</command>
<comment>List files with unresolved conflicts</comment>
<command tool="git">git ls-files --unmerged</command>
</template>
</pattern>
<pattern name="checking_out_pr">
<usage>Check out a pull request branch locally.</usage>
<template>

View file

@ -66,9 +66,9 @@
<step>Push to appropriate remote: 'git push --force-with-lease [remote] [branch]'</step>
</remote_handling>
<conflict_resolution>
<step>Use 'GIT_EDITOR=true git rebase main' to start rebase</step>
<step>If conflicts occur, edit files to resolve them</step>
<step>Use 'git add .' and 'git rebase --continue' to proceed</step>
<step>Delegate to merge-resolver mode using new_task</step>
<step>Provide the PR number (e.g., "#123") as the message</step>
<step>The merge-resolver mode will handle all conflict resolution automatically</step>
</conflict_resolution>
</tool>
@ -114,6 +114,26 @@ Files modified:
- webview-ui/src/components/settings/ApiSettings.tsx: Updated button text from "Save" to "Save Configuration"
Please ensure all supported languages (ca, de, es, fr, hi, id, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW) are updated with appropriate translations for these changes.</message>
</new_task>
]]></example_usage>
</tool>
<tool name="new_task (mode: merge-resolver)">
<best_practices>
<practice>Use when PR has merge conflicts that need to be resolved.</practice>
<practice>Simply provide the PR number (e.g., "#123") as the message.</practice>
<practice>The merge-resolver mode will handle checkout, rebase, conflict resolution, and pushing.</practice>
<practice>Wait for merge-resolver to complete before continuing with other PR fixes.</practice>
</best_practices>
<when_to_use>
<trigger>When gh pr view shows mergeable: false or mergeStateStatus: CONFLICTING</trigger>
<trigger>When git rebase fails with conflicts</trigger>
<trigger>When git status shows unmerged paths</trigger>
</when_to_use>
<example_usage><![CDATA[
<new_task>
<mode>merge-resolver</mode>
<message>#1234</message>
</new_task>
]]></example_usage>
</tool>

View file

@ -200,4 +200,105 @@ Ensure consistency across all language files and maintain the same context and t
<takeaway>Use gh pr view --json to get structured data about PR properties.</takeaway>
</key_takeaways>
</example>
<example name="fix_pr_with_merge_conflicts">
<scenario>
A pull request has merge conflicts that need to be resolved before other fixes can be applied.
</scenario>
<user_request>
Fix PR #5678 in RooCodeInc/Roo-Code - it has merge conflicts and failing tests.
</user_request>
<workflow>
<step number="1">
<description>Get PR details and check merge status.</description>
<tool_use>
<execute_command>
<command>gh pr view 5678 --repo RooCodeInc/Roo-Code --json number,title,author,state,body,url,headRefName,baseRefName,mergeable,mergeStateStatus</command>
</execute_command>
</tool_use>
<expected_outcome>Identify that mergeable is false and mergeStateStatus is CONFLICTING.</expected_outcome>
</step>
<step number="2">
<description>Delegate merge conflict resolution to merge-resolver mode.</description>
<tool_use>
<new_task>
<mode>merge-resolver</mode>
<message>#5678</message>
</new_task>
</tool_use>
<expected_outcome>The merge-resolver mode will handle checkout, rebase, conflict resolution, and pushing the resolved changes.</expected_outcome>
</step>
<step number="3">
<description>After merge-resolver completes, check PR status again.</description>
<tool_use>
<execute_command>
<command>gh pr view 5678 --repo RooCodeInc/Roo-Code --json mergeable,mergeStateStatus</command>
</execute_command>
</tool_use>
<analysis>Verify that the PR is now mergeable after conflict resolution.</analysis>
</step>
<step number="4">
<description>Check CI status for any remaining failures.</description>
<tool_use>
<execute_command>
<command>gh pr checks 5678 --repo RooCodeInc/Roo-Code</command>
</execute_command>
</tool_use>
<analysis>Identify any tests that are still failing after the merge conflict resolution.</analysis>
</step>
<step number="5">
<description>If tests are still failing, proceed with fixing them.</description>
<tool_use>
<execute_command>
<command>gh pr checkout 5678 --repo RooCodeInc/Roo-Code --force</command>
</execute_command>
</tool_use>
<analysis>Now that conflicts are resolved, we can focus on fixing the failing tests.</analysis>
</step>
<step number="6">
<description>Apply test fixes and push changes.</description>
<tool_use>
<execute_command>
<command>git add -u && git commit -m "fix: resolve failing tests after merge conflict resolution"</command>
</execute_command>
</tool_use>
<analysis>Commit the test fixes separately from the merge conflict resolution.</analysis>
</step>
<step number="7">
<description>Push changes and monitor CI status.</description>
<tool_use>
<execute_command>
<command>git push --force-with-lease origin [branch_name]</command>
</execute_command>
</tool_use>
<analysis>Push the test fixes to update the PR.</analysis>
</step>
<step number="8">
<description>Monitor CI checks in real-time.</description>
<tool_use>
<execute_command>
<command>gh pr checks 5678 --repo RooCodeInc/Roo-Code --watch</command>
</execute_command>
</tool_use>
<analysis>Watch CI checks continuously until all tests pass.</analysis>
</step>
</workflow>
<key_takeaways>
<takeaway>Always check for merge conflicts before attempting other fixes.</takeaway>
<takeaway>Delegate merge conflict resolution to the specialized merge-resolver mode.</takeaway>
<takeaway>The merge-resolver mode handles the entire conflict resolution workflow including pushing.</takeaway>
<takeaway>After conflict resolution, continue with other PR fixes like failing tests.</takeaway>
<takeaway>Keep conflict resolution commits separate from other fix commits for clarity.</takeaway>
</key_takeaways>
</example>
</complete_examples>

View file

@ -225,3 +225,35 @@ customModes:
- command
- mcp
source: project
- slug: merge-resolver
name: 🔀 Merge Resolver
roleDefinition: |-
You are Roo, a merge conflict resolution specialist with expertise in:
- Analyzing pull request merge conflicts using git blame and commit history
- Understanding code intent through commit messages and diffs
- Making intelligent decisions about which changes to keep, merge, or discard
- Using git commands and GitHub CLI to gather context
- Resolving conflicts based on commit metadata and code semantics
- Prioritizing changes based on intent (bugfix vs feature vs refactor)
- Combining non-conflicting changes when appropriate
You receive a PR number (e.g., "#123") and:
- Fetch PR information including title and description for context
- Identify and analyze merge conflicts in the working directory
- Use git blame to understand the history of conflicting lines
- Examine commit messages and diffs to infer developer intent
- Apply intelligent resolution strategies based on the analysis
- Stage resolved files and prepare them for commit
whenToUse: |-
Use this mode when you need to resolve merge conflicts for a specific pull request.
This mode is triggered by providing a PR number (e.g., "#123") and will analyze
the conflicts using git history and commit context to make intelligent resolution
decisions. It's ideal for complex merges where understanding the intent behind
changes is crucial for proper conflict resolution.
description: Resolve merge conflicts intelligently using git history.
groups:
- read
- edit
- command
- mcp
source: project

View file

@ -1,5 +1,27 @@
# Roo Code Changelog
## [3.23.17] - 2025-07-22
- Add: todo list tool enable checkbox to provider advanced settings
- Add: Moonshot provider (thanks @CellenLee!)
- Add: Qwen/Qwen3-235B-A22B-Instruct-2507 model to Chutes AI provider
- Fix: move context condensing prompt to Prompts section (thanks @SannidhyaSah!)
- Add: jump icon for newly created files
- Fix: add character limit to prevent terminal output context explosion
- Fix: resolve global mode export not including rules files
- Fix: enable export, share, and copy buttons during API operations (thanks @MuriloFP!)
- Add: configurable timeout for evals (5-10 min)
- Add: auto-omit MCP content when no servers are configured
- Fix: sort symlinked rules files by symlink names, not target names
- Docs: clarify when to use update_todo_list tool
- Add: Mistral embedding provider (thanks @SannidhyaSah!)
- Fix: add run parameter to vitest command in rules (thanks @KJ7LNW!)
- Update: the max_tokens fallback logic in the sliding window
- Fix: Bedrock and Vertext token counting improvements (thanks @daniel-lxs!)
- Add: llama-4-maverick model to Vertex AI provider (thanks @MuriloFP!)
- Fix: properly distinguish between user cancellations and API failures
- Fix: add case sensitivity mention to suggested fixes in apply_diff error message
## [3.23.16] - 2025-07-19
- Add global rate limiting for OpenAI-compatible embeddings (thanks @daniel-lxs!)

View file

@ -56,7 +56,7 @@ export async function createRun({ suite, exercises = [], systemPrompt, timeout,
const dockerArgs = [
`--name evals-controller-${run.id}`,
// "--rm",
"--rm",
"--network evals_default",
"-v /var/run/docker.sock:/var/run/docker.sock",
"-v /tmp/evals:/var/log/evals",

View file

@ -5,7 +5,14 @@ import * as os from "node:os"
import pWaitFor from "p-wait-for"
import { execa } from "execa"
import { type TaskEvent, TaskCommandName, RooCodeEventName, IpcMessageType, EVALS_SETTINGS } from "@roo-code/types"
import {
type TaskEvent,
type ClineSay,
TaskCommandName,
RooCodeEventName,
IpcMessageType,
EVALS_SETTINGS,
} from "@roo-code/types"
import { IpcClient } from "@roo-code/ipc"
import {
@ -203,6 +210,15 @@ export const runTask = async ({ run, task, publish, logger }: RunTaskOptions) =>
log: [RooCodeEventName.TaskTokenUsageUpdated, RooCodeEventName.TaskAskResponded],
}
const loggableSays: ClineSay[] = [
"error",
"command_output",
"rooignore_error",
"diff_error",
"condense_context",
"condense_context_error",
]
client.on(IpcMessageType.TaskEvent, async (taskEvent) => {
const { eventName, payload } = taskEvent
@ -215,7 +231,9 @@ export const runTask = async ({ run, task, publish, logger }: RunTaskOptions) =>
// For message events we only log non-partial messages.
if (
!ignoreEvents.log.includes(eventName) &&
(eventName !== RooCodeEventName.Message || payload[0].message.partial !== true)
(eventName !== RooCodeEventName.Message ||
(payload[0].message.say && loggableSays.includes(payload[0].message.say)) ||
payload[0].message.partial !== true)
) {
logger.info(`${eventName} ->`, payload)
}

View file

@ -222,7 +222,7 @@ export const EVALS_SETTINGS: RooCodeSettings = {
alwaysAllowUpdateTodoList: true,
followupAutoApproveTimeoutMs: 0,
allowedCommands: ["*"],
commandExecutionTimeout: 30_000,
commandExecutionTimeout: 20,
commandTimeoutAllowlist: [],
preventCompletionWithOpenTodos: false,
@ -266,7 +266,7 @@ export const EVALS_SETTINGS: RooCodeSettings = {
mcpEnabled: false,
mode: "code",
mode: "code", // "architect",
customModes: [],
}

View file

@ -18,6 +18,7 @@ export type ChutesModelId =
| "deepseek-ai/DeepSeek-R1-Zero"
| "deepseek-ai/DeepSeek-V3-0324"
| "Qwen/Qwen3-235B-A22B"
| "Qwen/Qwen3-235B-A22B-Instruct-2507"
| "Qwen/Qwen3-32B"
| "Qwen/Qwen3-30B-A3B"
| "Qwen/Qwen3-14B"
@ -163,6 +164,15 @@ export const chutesModels = {
outputPrice: 0,
description: "DeepSeek V3 (0324) model.",
},
"Qwen/Qwen3-235B-A22B-Instruct-2507": {
maxTokens: 32768,
contextWindow: 262144,
supportsImages: false,
supportsPromptCache: false,
inputPrice: 0,
outputPrice: 0,
description: "Qwen3 235B A22B Instruct 2507 model with 262K context window.",
},
"Qwen/Qwen3-235B-A22B": {
maxTokens: 32768,
contextWindow: 40960,

View file

@ -273,6 +273,15 @@ export const vertexModels = {
maxThinkingTokens: 24_576,
supportsReasoningBudget: true,
},
"llama-4-maverick-17b-128e-instruct-maas": {
maxTokens: 8192,
contextWindow: 131072,
supportsImages: false,
supportsPromptCache: false,
inputPrice: 0.35,
outputPrice: 1.15,
description: "Meta Llama 4 Maverick 17B Instruct model, 128K context.",
},
} as const satisfies Record<string, ModelInfo>
export const VERTEX_REGIONS = [

View file

@ -163,6 +163,28 @@ describe("ChutesHandler", () => {
expect(model.info).toEqual(expect.objectContaining(chutesModels[testModelId]))
})
it("should return Qwen3-235B-A22B-Instruct-2507 model with correct configuration", () => {
const testModelId: ChutesModelId = "Qwen/Qwen3-235B-A22B-Instruct-2507"
const handlerWithModel = new ChutesHandler({
apiModelId: testModelId,
chutesApiKey: "test-chutes-api-key",
})
const model = handlerWithModel.getModel()
expect(model.id).toBe(testModelId)
expect(model.info).toEqual(
expect.objectContaining({
maxTokens: 32768,
contextWindow: 262144,
supportsImages: false,
supportsPromptCache: false,
inputPrice: 0,
outputPrice: 0,
description: "Qwen3 235B A22B Instruct 2507 model with 262K context window.",
temperature: 0.5, // Default temperature for non-DeepSeek models
}),
)
})
it("completePrompt method should return text from Chutes API", async () => {
const expectedResponse = "This is a test response from Chutes"
mockCreate.mockResolvedValueOnce({ choices: [{ message: { content: expectedResponse } }] })

View file

@ -6,6 +6,8 @@ import { TelemetryService } from "@roo-code/telemetry"
import { Task } from "../task/Task"
import { getWorkspacePath } from "../../utils/path"
import { checkGitInstalled } from "../../utils/git"
import { t } from "../../i18n"
import { ClineApiReqInfo } from "../../shared/ExtensionMessage"
import { getApiMetrics } from "../../shared/getApiMetrics"
@ -73,6 +75,47 @@ export function getCheckpointService(cline: Task) {
cline.checkpointServiceInitializing = true
// Check if Git is installed before initializing the service
// Note: This is intentionally fire-and-forget to match the original IIFE pattern
// The service is returned immediately while Git check happens asynchronously
checkGitInstallation(cline, service, log, provider)
return service
} catch (err) {
log(`[Task#getCheckpointService] ${err.message}`)
cline.enableCheckpoints = false
return undefined
}
}
async function checkGitInstallation(
cline: Task,
service: RepoPerTaskCheckpointService,
log: (message: string) => void,
provider: any,
) {
try {
const gitInstalled = await checkGitInstalled()
if (!gitInstalled) {
log("[Task#getCheckpointService] Git is not installed, disabling checkpoints")
cline.enableCheckpoints = false
cline.checkpointServiceInitializing = false
// Show user-friendly notification
const selection = await vscode.window.showWarningMessage(
t("common:errors.git_not_installed"),
t("common:buttons.learn_more"),
)
if (selection === t("common:buttons.learn_more")) {
await vscode.env.openExternal(vscode.Uri.parse("https://git-scm.com/downloads"))
}
return
}
// Git is installed, proceed with initialization
service.on("initialize", () => {
log("[Task#getCheckpointService] service initialized")
@ -118,12 +161,11 @@ export function getCheckpointService(cline: Task) {
log(`[Task#getCheckpointService] initShadowGit -> ${err.message}`)
cline.enableCheckpoints = false
})
return service
} catch (err) {
log(`[Task#getCheckpointService] ${err.message}`)
log(`[Task#getCheckpointService] Unexpected error during Git check: ${err.message}`)
console.error("Git check error:", err)
cline.enableCheckpoints = false
return undefined
cline.checkpointServiceInitializing = false
}
}

View file

@ -420,7 +420,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -317,7 +317,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -420,7 +420,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -469,7 +469,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -425,7 +425,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -420,7 +420,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -473,7 +473,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -420,7 +420,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -508,7 +508,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -420,7 +420,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -473,7 +473,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -469,7 +469,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -420,7 +420,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -56,7 +56,7 @@ Replace the entire TODO list with an updated checklist reflecting the current st
</update_todo_list>
**When to Use:**
- The task involves multiple steps or requires ongoing tracking.
- The task is complicated or involves multiple steps or requires ongoing tracking.
- You need to update the status of several todos at once.
- New actionable items are discovered during task execution.
- The user requests a todo list or provides multiple tasks.

View file

@ -21,7 +21,7 @@ import { t } from "../../i18n"
class ShellIntegrationError extends Error {}
export async function executeCommandTool(
cline: Task,
task: Task,
block: ToolUse,
askApproval: AskApproval,
handleError: HandleError,
@ -33,25 +33,25 @@ export async function executeCommandTool(
try {
if (block.partial) {
await cline.ask("command", removeClosingTag("command", command), block.partial).catch(() => {})
await task.ask("command", removeClosingTag("command", command), block.partial).catch(() => {})
return
} else {
if (!command) {
cline.consecutiveMistakeCount++
cline.recordToolError("execute_command")
pushToolResult(await cline.sayAndCreateMissingParamError("execute_command", "command"))
task.consecutiveMistakeCount++
task.recordToolError("execute_command")
pushToolResult(await task.sayAndCreateMissingParamError("execute_command", "command"))
return
}
const ignoredFileAttemptedToAccess = cline.rooIgnoreController?.validateCommand(command)
const ignoredFileAttemptedToAccess = task.rooIgnoreController?.validateCommand(command)
if (ignoredFileAttemptedToAccess) {
await cline.say("rooignore_error", ignoredFileAttemptedToAccess)
await task.say("rooignore_error", ignoredFileAttemptedToAccess)
pushToolResult(formatResponse.toolError(formatResponse.rooIgnoreError(ignoredFileAttemptedToAccess)))
return
}
cline.consecutiveMistakeCount = 0
task.consecutiveMistakeCount = 0
command = unescapeHtmlEntities(command) // Unescape HTML entities.
const didApprove = await askApproval("command", command)
@ -60,14 +60,15 @@ export async function executeCommandTool(
return
}
const executionId = cline.lastMessageTs?.toString() ?? Date.now().toString()
const clineProvider = await cline.providerRef.deref()
const clineProviderState = await clineProvider?.getState()
const executionId = task.lastMessageTs?.toString() ?? Date.now().toString()
const provider = await task.providerRef.deref()
const providerState = await provider?.getState()
const {
terminalOutputLineLimit = 500,
terminalOutputCharacterLimit = DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT,
terminalShellIntegrationDisabled = false,
} = clineProviderState ?? {}
} = providerState ?? {}
// Get command execution timeout from VSCode configuration (in seconds)
const commandExecutionTimeoutSeconds = vscode.workspace
@ -96,26 +97,26 @@ export async function executeCommandTool(
}
try {
const [rejected, result] = await executeCommand(cline, options)
const [rejected, result] = await executeCommand(task, options)
if (rejected) {
cline.didRejectTool = true
task.didRejectTool = true
}
pushToolResult(result)
} catch (error: unknown) {
const status: CommandExecutionStatus = { executionId, status: "fallback" }
clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
await cline.say("shell_integration_warning")
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
await task.say("shell_integration_warning")
if (error instanceof ShellIntegrationError) {
const [rejected, result] = await executeCommand(cline, {
const [rejected, result] = await executeCommand(task, {
...options,
terminalShellIntegrationDisabled: true,
})
if (rejected) {
cline.didRejectTool = true
task.didRejectTool = true
}
pushToolResult(result)
@ -143,7 +144,7 @@ export type ExecuteCommandOptions = {
}
export async function executeCommand(
cline: Task,
task: Task,
{
executionId,
command,
@ -154,16 +155,16 @@ export async function executeCommand(
commandExecutionTimeout = 0,
}: ExecuteCommandOptions,
): Promise<[boolean, ToolResponse]> {
// Convert milliseconds back to seconds for display purposes
// Convert milliseconds back to seconds for display purposes.
const commandExecutionTimeoutSeconds = commandExecutionTimeout / 1000
let workingDir: string
if (!customCwd) {
workingDir = cline.cwd
workingDir = task.cwd
} else if (path.isAbsolute(customCwd)) {
workingDir = customCwd
} else {
workingDir = path.resolve(cline.cwd, customCwd)
workingDir = path.resolve(task.cwd, customCwd)
}
try {
@ -180,7 +181,7 @@ export async function executeCommand(
let shellIntegrationError: string | undefined
const terminalProvider = terminalShellIntegrationDisabled ? "execa" : "vscode"
const clineProvider = await cline.providerRef.deref()
const provider = await task.providerRef.deref()
let accumulatedOutput = ""
const callbacks: RooTerminalCallbacks = {
@ -192,14 +193,14 @@ export async function executeCommand(
terminalOutputCharacterLimit,
)
const status: CommandExecutionStatus = { executionId, status: "output", output: compressedOutput }
clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
if (runInBackground) {
return
}
try {
const { response, text, images } = await cline.ask("command_output", "")
const { response, text, images } = await task.ask("command_output", "")
runInBackground = true
if (response === "messageResponse") {
@ -214,29 +215,30 @@ export async function executeCommand(
terminalOutputLineLimit,
terminalOutputCharacterLimit,
)
cline.say("command_output", result)
task.say("command_output", result)
completed = true
},
onShellExecutionStarted: (pid: number | undefined) => {
console.log(`[executeCommand] onShellExecutionStarted: ${pid}`)
const status: CommandExecutionStatus = { executionId, status: "started", pid, command }
clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
},
onShellExecutionComplete: (details: ExitCodeDetails) => {
const status: CommandExecutionStatus = { executionId, status: "exited", exitCode: details.exitCode }
clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
exitDetails = details
},
}
if (terminalProvider === "vscode") {
callbacks.onNoShellIntegration = async (error: string) => {
TelemetryService.instance.captureShellIntegrationError(cline.taskId)
TelemetryService.instance.captureShellIntegrationError(task.taskId)
shellIntegrationError = error
}
}
const terminal = await TerminalRegistry.getOrCreateTerminal(workingDir, !!customCwd, cline.taskId, terminalProvider)
const terminal = await TerminalRegistry.getOrCreateTerminal(workingDir, !!customCwd, task.taskId, terminalProvider)
if (terminal instanceof Terminal) {
terminal.terminal.show(true)
@ -248,9 +250,9 @@ export async function executeCommand(
}
const process = terminal.runCommand(command, callbacks)
cline.terminalProcess = process
task.terminalProcess = process
// Implement command execution timeout (skip if timeout is 0)
// Implement command execution timeout (skip if timeout is 0).
if (commandExecutionTimeout > 0) {
let timeoutId: NodeJS.Timeout | undefined
let isTimedOut = false
@ -258,10 +260,7 @@ export async function executeCommand(
const timeoutPromise = new Promise<void>((_, reject) => {
timeoutId = setTimeout(() => {
isTimedOut = true
// Try to abort the process
if (cline.terminalProcess) {
cline.terminalProcess.abort()
}
task.terminalProcess?.abort()
reject(new Error(`Command execution timed out after ${commandExecutionTimeout}ms`))
}, commandExecutionTimeout)
})
@ -270,17 +269,10 @@ export async function executeCommand(
await Promise.race([process, timeoutPromise])
} catch (error) {
if (isTimedOut) {
// Handle timeout case
const status: CommandExecutionStatus = { executionId, status: "timeout" }
clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
// Add visual feedback for timeout
await cline.say(
"error",
t("common:errors:command_timeout", { seconds: commandExecutionTimeoutSeconds }),
)
cline.terminalProcess = undefined
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
await task.say("error", t("common:errors:command_timeout", { seconds: commandExecutionTimeoutSeconds }))
task.terminalProcess = undefined
return [
false,
@ -292,14 +284,15 @@ export async function executeCommand(
if (timeoutId) {
clearTimeout(timeoutId)
}
cline.terminalProcess = undefined
task.terminalProcess = undefined
}
} else {
// No timeout - just wait for the process to complete
// No timeout - just wait for the process to complete.
try {
await process
} finally {
cline.terminalProcess = undefined
task.terminalProcess = undefined
}
}
@ -316,7 +309,7 @@ export async function executeCommand(
if (message) {
const { text, images } = message
await cline.say("user_feedback", text, images)
await task.say("user_feedback", text, images)
return [
true,

View file

@ -451,7 +451,7 @@ Diff ${i + 1} failed for file: ${relPath}
Error: ${failPart.error}
Suggested fixes:
1. Verify the search content exactly matches the file content (including whitespace)
1. Verify the search content exactly matches the file content (including whitespace and case)
2. Check for correct indentation and line endings
3. Use <read_file> to see the current file content
4. Consider breaking complex changes into smaller diffs

View file

@ -32,6 +32,7 @@
"could_not_open_file_generic": "No s'ha pogut obrir el fitxer!",
"checkpoint_timeout": "S'ha esgotat el temps en intentar restaurar el punt de control.",
"checkpoint_failed": "Ha fallat la restauració del punt de control.",
"git_not_installed": "Git és necessari per a la funció de punts de control. Si us plau, instal·la Git per activar els punts de control.",
"no_workspace": "Si us plau, obre primer una carpeta de projecte",
"update_support_prompt": "Ha fallat l'actualització del missatge de suport",
"reset_support_prompt": "Ha fallat el restabliment del missatge de suport",
@ -111,7 +112,8 @@
},
"buttons": {
"save": "Desar",
"edit": "Editar"
"edit": "Editar",
"learn_more": "Més informació"
},
"tasks": {
"canceled": "Error de tasca: Ha estat aturada i cancel·lada per l'usuari.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Datei konnte nicht geöffnet werden!",
"checkpoint_timeout": "Zeitüberschreitung beim Versuch, den Checkpoint wiederherzustellen.",
"checkpoint_failed": "Fehler beim Wiederherstellen des Checkpoints.",
"git_not_installed": "Git ist für die Checkpoint-Funktion erforderlich. Bitte installiere Git, um Checkpoints zu aktivieren.",
"no_workspace": "Bitte öffne zuerst einen Projektordner",
"update_support_prompt": "Fehler beim Aktualisieren der Support-Nachricht",
"reset_support_prompt": "Fehler beim Zurücksetzen der Support-Nachricht",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Speichern",
"edit": "Bearbeiten"
"edit": "Bearbeiten",
"learn_more": "Mehr erfahren"
},
"tasks": {
"canceled": "Aufgabenfehler: Die Aufgabe wurde vom Benutzer gestoppt und abgebrochen.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Could not open file!",
"checkpoint_timeout": "Timed out when attempting to restore checkpoint.",
"checkpoint_failed": "Failed to restore checkpoint.",
"git_not_installed": "Git is required for the checkpoints feature. Please install Git to enable checkpoints.",
"no_workspace": "Please open a project folder first",
"update_support_prompt": "Failed to update support prompt",
"reset_support_prompt": "Failed to reset support prompt",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Save",
"edit": "Edit"
"edit": "Edit",
"learn_more": "Learn More"
},
"tasks": {
"canceled": "Task error: It was stopped and canceled by the user.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "¡No se pudo abrir el archivo!",
"checkpoint_timeout": "Se agotó el tiempo al intentar restaurar el punto de control.",
"checkpoint_failed": "Error al restaurar el punto de control.",
"git_not_installed": "Git es necesario para la función de puntos de control. Por favor, instala Git para activar los puntos de control.",
"no_workspace": "Por favor, abre primero una carpeta de proyecto",
"update_support_prompt": "Error al actualizar el mensaje de soporte",
"reset_support_prompt": "Error al restablecer el mensaje de soporte",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Guardar",
"edit": "Editar"
"edit": "Editar",
"learn_more": "Más información"
},
"tasks": {
"canceled": "Error de tarea: Fue detenida y cancelada por el usuario.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Impossible d'ouvrir le fichier !",
"checkpoint_timeout": "Expiration du délai lors de la tentative de rétablissement du checkpoint.",
"checkpoint_failed": "Échec du rétablissement du checkpoint.",
"git_not_installed": "Git est requis pour la fonctionnalité des points de contrôle. Veuillez installer Git pour activer les points de contrôle.",
"no_workspace": "Veuillez d'abord ouvrir un espace de travail",
"update_support_prompt": "Erreur lors de la mise à jour du prompt de support",
"reset_support_prompt": "Erreur lors de la réinitialisation du prompt de support",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Enregistrer",
"edit": "Modifier"
"edit": "Modifier",
"learn_more": "En savoir plus"
},
"tasks": {
"canceled": "Erreur de tâche : Elle a été arrêtée et annulée par l'utilisateur.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "फ़ाइल नहीं खोली जा सकी!",
"checkpoint_timeout": "चेकपॉइंट को पुनर्स्थापित करने का प्रयास करते समय टाइमआउट हो गया।",
"checkpoint_failed": "चेकपॉइंट पुनर्स्थापित करने में विफल।",
"git_not_installed": "चेकपॉइंट सुविधा के लिए Git आवश्यक है। कृपया चेकपॉइंट সক্ষম करने के लिए Git इंस्टॉल करें।",
"no_workspace": "कृपया पहले प्रोजेक्ट फ़ोल्डर खोलें",
"update_support_prompt": "सपोर्ट प्रॉम्प्ट अपडेट करने में विफल",
"reset_support_prompt": "सपोर्ट प्रॉम्प्ट रीसेट करने में विफल",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "सहेजें",
"edit": "संपादित करें"
"edit": "संपादित करें",
"learn_more": "और अधिक जानें"
},
"tasks": {
"canceled": "टास्क त्रुटि: इसे उपयोगकर्ता द्वारा रोका और रद्द किया गया था।",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Tidak dapat membuka file!",
"checkpoint_timeout": "Timeout saat mencoba memulihkan checkpoint.",
"checkpoint_failed": "Gagal memulihkan checkpoint.",
"git_not_installed": "Git diperlukan untuk fitur checkpoint. Silakan instal Git untuk mengaktifkan checkpoint.",
"no_workspace": "Silakan buka folder proyek terlebih dahulu",
"update_support_prompt": "Gagal memperbarui support prompt",
"reset_support_prompt": "Gagal mereset support prompt",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Simpan",
"edit": "Edit"
"edit": "Edit",
"learn_more": "Pelajari Lebih Lanjut"
},
"tasks": {
"canceled": "Error tugas: Dihentikan dan dibatalkan oleh pengguna.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Impossibile aprire il file!",
"checkpoint_timeout": "Timeout durante il tentativo di ripristinare il checkpoint.",
"checkpoint_failed": "Impossibile ripristinare il checkpoint.",
"git_not_installed": "Git è richiesto per la funzione di checkpoint. Per favore, installa Git per abilitare i checkpoint.",
"no_workspace": "Per favore, apri prima una cartella di progetto",
"update_support_prompt": "Errore durante l'aggiornamento del messaggio di supporto",
"reset_support_prompt": "Errore durante il ripristino del messaggio di supporto",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Salva",
"edit": "Modifica"
"edit": "Modifica",
"learn_more": "Scopri di più"
},
"tasks": {
"canceled": "Errore attività: È stata interrotta e annullata dall'utente.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "ファイルを開けませんでした!",
"checkpoint_timeout": "チェックポイントの復元を試みる際にタイムアウトしました。",
"checkpoint_failed": "チェックポイントの復元に失敗しました。",
"git_not_installed": "チェックポイント機能にはGitが必要です。チェックポイントを有効にするにはGitをインストールしてください。",
"no_workspace": "まずプロジェクトフォルダを開いてください",
"update_support_prompt": "サポートメッセージの更新に失敗しました",
"reset_support_prompt": "サポートメッセージのリセットに失敗しました",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "保存",
"edit": "編集"
"edit": "編集",
"learn_more": "詳細"
},
"tasks": {
"canceled": "タスクエラー:ユーザーによって停止およびキャンセルされました。",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "파일을 열 수 없습니다!",
"checkpoint_timeout": "체크포인트 복원을 시도하는 중 시간 초과되었습니다.",
"checkpoint_failed": "체크포인트 복원에 실패했습니다.",
"git_not_installed": "체크포인트 기능을 사용하려면 Git이 필요합니다. 체크포인트를 활성화하려면 Git을 설치하세요.",
"no_workspace": "먼저 프로젝트 폴더를 열어주세요",
"update_support_prompt": "지원 프롬프트 업데이트에 실패했습니다",
"reset_support_prompt": "지원 프롬프트 재설정에 실패했습니다",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "저장",
"edit": "편집"
"edit": "편집",
"learn_more": "더 알아보기"
},
"tasks": {
"canceled": "작업 오류: 사용자에 의해 중지 및 취소되었습니다.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Kon bestand niet openen!",
"checkpoint_timeout": "Time-out bij het herstellen van checkpoint.",
"checkpoint_failed": "Herstellen van checkpoint mislukt.",
"git_not_installed": "Git is vereist voor de checkpoint-functie. Installeer Git om checkpoints in te schakelen.",
"no_workspace": "Open eerst een projectmap",
"update_support_prompt": "Bijwerken van ondersteuningsprompt mislukt",
"reset_support_prompt": "Resetten van ondersteuningsprompt mislukt",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Opslaan",
"edit": "Bewerken"
"edit": "Bewerken",
"learn_more": "Meer informatie"
},
"tasks": {
"canceled": "Taakfout: gestopt en geannuleerd door gebruiker.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Nie można otworzyć pliku!",
"checkpoint_timeout": "Upłynął limit czasu podczas próby przywrócenia punktu kontrolnego.",
"checkpoint_failed": "Nie udało się przywrócić punktu kontrolnego.",
"git_not_installed": "Funkcja punktów kontrolnych wymaga oprogramowania Git. Zainstaluj Git, aby włączyć punkty kontrolne.",
"no_workspace": "Najpierw otwórz folder projektu",
"update_support_prompt": "Nie udało się zaktualizować komunikatu wsparcia",
"reset_support_prompt": "Nie udało się zresetować komunikatu wsparcia",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Zapisz",
"edit": "Edytuj"
"edit": "Edytuj",
"learn_more": "Dowiedz się więcej"
},
"tasks": {
"canceled": "Błąd zadania: Zostało zatrzymane i anulowane przez użytkownika.",

View file

@ -32,6 +32,7 @@
"could_not_open_file_generic": "Não foi possível abrir o arquivo!",
"checkpoint_timeout": "Tempo esgotado ao tentar restaurar o ponto de verificação.",
"checkpoint_failed": "Falha ao restaurar o ponto de verificação.",
"git_not_installed": "O Git é necessário para o recurso de checkpoints. Por favor, instale o Git para habilitar os checkpoints.",
"no_workspace": "Por favor, abra primeiro uma pasta de projeto",
"update_support_prompt": "Falha ao atualizar o prompt de suporte",
"reset_support_prompt": "Falha ao redefinir o prompt de suporte",
@ -111,7 +112,8 @@
},
"buttons": {
"save": "Salvar",
"edit": "Editar"
"edit": "Editar",
"learn_more": "Saiba Mais"
},
"tasks": {
"canceled": "Erro na tarefa: Foi interrompida e cancelada pelo usuário.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Не удалось открыть файл!",
"checkpoint_timeout": "Превышено время ожидания при попытке восстановления контрольной точки.",
"checkpoint_failed": "Не удалось восстановить контрольную точку.",
"git_not_installed": "Для функции контрольных точек требуется Git. Пожалуйста, установите Git, чтобы включить контрольные точки.",
"no_workspace": "Пожалуйста, сначала откройте папку проекта",
"update_support_prompt": "Не удалось обновить промпт поддержки",
"reset_support_prompt": "Не удалось сбросить промпт поддержки",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Сохранить",
"edit": "Редактировать"
"edit": "Редактировать",
"learn_more": "Узнать больше"
},
"tasks": {
"canceled": "Ошибка задачи: Она была остановлена и отменена пользователем.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Dosya açılamadı!",
"checkpoint_timeout": "Kontrol noktasını geri yüklemeye çalışırken zaman aşımına uğradı.",
"checkpoint_failed": "Kontrol noktası geri yüklenemedi.",
"git_not_installed": "Kontrol noktaları özelliği için Git gereklidir. Kontrol noktalarını etkinleştirmek için lütfen Git'i yükleyin.",
"no_workspace": "Lütfen önce bir proje klasörü açın",
"update_support_prompt": "Destek istemi güncellenemedi",
"reset_support_prompt": "Destek istemi sıfırlanamadı",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Kaydet",
"edit": "Düzenle"
"edit": "Düzenle",
"learn_more": "Daha Fazla Bilgi"
},
"tasks": {
"canceled": "Görev hatası: Kullanıcı tarafından durduruldu ve iptal edildi.",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "Không thể mở tệp!",
"checkpoint_timeout": "Đã hết thời gian khi cố gắng khôi phục điểm kiểm tra.",
"checkpoint_failed": "Không thể khôi phục điểm kiểm tra.",
"git_not_installed": "Yêu cầu Git cho tính năng điểm kiểm tra. Vui lòng cài đặt Git để bật điểm kiểm tra.",
"no_workspace": "Vui lòng mở thư mục dự án trước",
"update_support_prompt": "Không thể cập nhật lời nhắc hỗ trợ",
"reset_support_prompt": "Không thể đặt lại lời nhắc hỗ trợ",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "Lưu",
"edit": "Chỉnh sửa"
"edit": "Chỉnh sửa",
"learn_more": "Tìm hiểu thêm"
},
"tasks": {
"canceled": "Lỗi nhiệm vụ: Nó đã bị dừng và hủy bởi người dùng.",

View file

@ -33,6 +33,7 @@
"could_not_open_file_generic": "无法打开文件!",
"checkpoint_timeout": "尝试恢复检查点时超时。",
"checkpoint_failed": "恢复检查点失败。",
"git_not_installed": "存档点功能需要 Git。请安装 Git 以启用存档点。",
"no_workspace": "请先打开项目文件夹",
"update_support_prompt": "更新支持消息失败",
"reset_support_prompt": "重置支持消息失败",
@ -112,7 +113,8 @@
},
"buttons": {
"save": "保存",
"edit": "编辑"
"edit": "编辑",
"learn_more": "了解更多"
},
"tasks": {
"canceled": "任务错误:它已被用户停止并取消。",

View file

@ -28,6 +28,7 @@
"could_not_open_file_generic": "無法開啟檔案!",
"checkpoint_timeout": "嘗試恢復檢查點時超時。",
"checkpoint_failed": "恢復檢查點失敗。",
"git_not_installed": "存檔點功能需要 Git。請安裝 Git 以啟用存檔點。",
"no_workspace": "請先開啟專案資料夾",
"update_support_prompt": "更新支援訊息失敗",
"reset_support_prompt": "重設支援訊息失敗",
@ -107,7 +108,8 @@
},
"buttons": {
"save": "儲存",
"edit": "編輯"
"edit": "編輯",
"learn_more": "了解更多"
},
"tasks": {
"canceled": "工作錯誤:它已被使用者停止並取消。",

View file

@ -73,6 +73,8 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
let timeoutId: NodeJS.Timeout | undefined
const kill = new Promise<void>((resolve) => {
console.log(`[ExecaTerminalProcess#run] SIGKILL -> ${this.pid}`)
timeoutId = setTimeout(() => {
try {
subprocess.kill("SIGKILL")
@ -86,7 +88,7 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
await Promise.race([subprocess, kill])
} catch (error) {
console.log(
`[ExecaTerminalProcess] subprocess termination error: ${error instanceof Error ? error.message : String(error)}`,
`[ExecaTerminalProcess#run] subprocess termination error: ${error instanceof Error ? error.message : String(error)}`,
)
}
@ -98,12 +100,13 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
this.emit("shell_execution_complete", { exitCode: 0 })
} catch (error) {
if (error instanceof ExecaError) {
console.error(`[ExecaTerminalProcess] shell execution error: ${error.message}`)
console.error(`[ExecaTerminalProcess#run] shell execution error: ${error.message}`)
this.emit("shell_execution_complete", { exitCode: error.exitCode ?? 0, signalName: error.signal })
} else {
console.error(
`[ExecaTerminalProcess] shell execution error: ${error instanceof Error ? error.message : String(error)}`,
`[ExecaTerminalProcess#run] shell execution error: ${error instanceof Error ? error.message : String(error)}`,
)
this.emit("shell_execution_complete", { exitCode: 1 })
}
}
@ -128,29 +131,30 @@ export class ExecaTerminalProcess extends BaseTerminalProcess {
psTree(this.pid, async (err, children) => {
if (!err) {
const pids = children.map((p) => parseInt(p.PID))
console.error(`[ExecaTerminalProcess#abort] SIGKILL children -> ${pids.join(", ")}`)
for (const pid of pids) {
try {
process.kill(pid, "SIGINT")
process.kill(pid, "SIGKILL")
} catch (e) {
console.warn(
`[ExecaTerminalProcess] Failed to send SIGINT to child PID ${pid}: ${e instanceof Error ? e.message : String(e)}`,
`[ExecaTerminalProcess#abort] Failed to send SIGKILL to child PID ${pid}: ${e instanceof Error ? e.message : String(e)}`,
)
// Optionally try SIGTERM or SIGKILL on failure, depending on desired behavior.
}
}
} else {
console.error(
`[ExecaTerminalProcess] Failed to get process tree for PID ${this.pid}: ${err.message}`,
`[ExecaTerminalProcess#abort] Failed to get process tree for PID ${this.pid}: ${err.message}`,
)
}
})
try {
process.kill(this.pid, "SIGINT")
console.error(`[ExecaTerminalProcess#abort] SIGKILL parent -> ${this.pid}`)
process.kill(this.pid, "SIGKILL")
} catch (e) {
console.warn(
`[ExecaTerminalProcess] Failed to send SIGINT to main PID ${this.pid}: ${e instanceof Error ? e.message : String(e)}`,
`[ExecaTerminalProcess#abort] Failed to send SIGKILL to main PID ${this.pid}: ${e instanceof Error ? e.message : String(e)}`,
)
}
}

View file

@ -3,7 +3,7 @@
"displayName": "%extension.displayName%",
"description": "%extension.description%",
"publisher": "RooVeterinaryInc",
"version": "3.23.16",
"version": "3.23.17",
"icon": "assets/icons/icon.png",
"galleryBanner": {
"color": "#617A91",

View file

@ -4,6 +4,7 @@ import * as fs from "fs"
import * as path from "path"
import {
checkGitInstalled,
searchCommits,
getCommitInfo,
getWorkingState,
@ -83,6 +84,54 @@ describe("git utils", () => {
vitest.clearAllMocks()
})
describe("checkGitInstalled", () => {
it("should return true when git --version succeeds", async () => {
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
if (command === "git --version") {
callback(null, { stdout: "git version 2.39.2", stderr: "" })
return {} as any
}
callback(new Error("Unexpected command"))
return {} as any
})
const result = await checkGitInstalled()
expect(result).toBe(true)
expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function))
})
it("should return false when git --version fails", async () => {
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
if (command === "git --version") {
callback(new Error("git not found"))
return {} as any
}
callback(new Error("Unexpected command"))
return {} as any
})
const result = await checkGitInstalled()
expect(result).toBe(false)
expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function))
})
it("should handle unexpected errors gracefully", async () => {
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
if (command === "git --version") {
// Simulate an unexpected error
callback(new Error("Unexpected system error"))
return {} as any
}
callback(new Error("Unexpected command"))
return {} as any
})
const result = await checkGitInstalled()
expect(result).toBe(false)
expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function))
})
})
describe("searchCommits", () => {
const mockCommitData = [
"abc123def456",

View file

@ -210,7 +210,16 @@ async function checkGitRepo(cwd: string): Promise<boolean> {
}
}
async function checkGitInstalled(): Promise<boolean> {
/**
* Checks if Git is installed on the system by attempting to run git --version
* @returns {Promise<boolean>} True if Git is installed and accessible, false otherwise
* @example
* const isGitInstalled = await checkGitInstalled();
* if (!isGitInstalled) {
* console.log("Git is not installed");
* }
*/
export async function checkGitInstalled(): Promise<boolean> {
try {
await execAsync("git --version")
return true