This commit is contained in:
Kidist Demessie 2026-04-23 16:18:58 -06:00 committed by GitHub
commit 9b91fbbb00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 1911 additions and 1321 deletions

12
.clinerules Normal file
View file

@ -0,0 +1,12 @@
# 2026 DETERMINISTIC PROTOCOL
## RULE 1: THE HANDSHAKE
Before you perform ANY coding task, you MUST initialize your session by calling:
selectActiveIntent(intentId: 'INT-002')
- You must strictly follow the <owned_scope> provided in the XML response.
- Do NOT touch files outside that scope.
## RULE 2: THE GOLDEN THREAD (LOGGING)
Every time you create, modify, or delete a file, you MUST immediately run the following hook to log the hash to the ledger:
python .orchestration/hooks/tmd.py '<INTENT_ID>' '<FILE_PATH>' '<NEW_CONTENT>'
- This is mandatory for traceability.

View file

@ -3,117 +3,117 @@ description: Report a broken behavior in plain language with a minimal reproduct
labels: ["bug"]
title: "[BUG] "
body:
- type: markdown
attributes:
value: |
Thank you for your report! Please search existing issues first:
https://github.com/RooCodeInc/Roo-Code/issues
- type: markdown
attributes:
value: |
Thank you for your report! Please search existing issues first:
https://github.com/RooCodeInc/Roo-Code/issues
- type: textarea
id: problem
attributes:
label: Problem (one or two sentences)
description: Describe what went wrong in plain language.
placeholder: 'Example: "Expected the task to start, but nothing happened and no message appeared."'
validations:
required: true
- type: textarea
id: problem
attributes:
label: Problem (one or two sentences)
description: Describe what went wrong in plain language.
placeholder: 'Example: "Expected the task to start, but nothing happened and no message appeared."'
validations:
required: true
- type: textarea
id: context
attributes:
label: Context (who is affected and when)
description: Who sees this and in what situation? Keep it non-technical.
placeholder: 'Example: "Happens to new users when starting a run from the New Run page with dark theme enabled."'
validations:
required: true
- type: textarea
id: context
attributes:
label: Context (who is affected and when)
description: Who sees this and in what situation? Keep it non-technical.
placeholder: 'Example: "Happens to new users when starting a run from the New Run page with dark theme enabled."'
validations:
required: true
- type: textarea
id: steps
attributes:
label: Reproduction steps
description: Provide clear, numbered steps so we can reproduce.
placeholder: |
1) Environment/setup (OS, extension version, relevant settings)
2) Exact actions (clicks, inputs, commands)
3) What you observed after each step
validations:
required: true
- type: textarea
id: steps
attributes:
label: Reproduction steps
description: Provide clear, numbered steps so we can reproduce.
placeholder: |
1) Environment/setup (OS, extension version, relevant settings)
2) Exact actions (clicks, inputs, commands)
3) What you observed after each step
validations:
required: true
- type: input
id: expected
attributes:
label: Expected result
placeholder: e.g., "The task starts and shows progress."
validations:
required: true
- type: input
id: expected
attributes:
label: Expected result
placeholder: e.g., "The task starts and shows progress."
validations:
required: true
- type: input
id: actual
attributes:
label: Actual result
placeholder: e.g., "The button appears disabled and no progress is shown."
validations:
required: true
- type: input
id: actual
attributes:
label: Actual result
placeholder: e.g., "The button appears disabled and no progress is shown."
validations:
required: true
- type: textarea
id: variations
attributes:
label: Variations tried (optional)
description: Different browsers, devices, providers, or settings you tried.
placeholder: e.g., "Tried Chrome/Firefox, disabling dark theme, switching providers."
- type: textarea
id: variations
attributes:
label: Variations tried (optional)
description: Different browsers, devices, providers, or settings you tried.
placeholder: e.g., "Tried Chrome/Firefox, disabling dark theme, switching providers."
- type: input
id: version
attributes:
label: App Version
description: What version of Roo Code are you using? (e.g., v3.3.1)
validations:
required: true
- type: input
id: version
attributes:
label: App Version
description: What version of Roo Code are you using? (e.g., v3.3.1)
validations:
required: true
- type: dropdown
id: provider
attributes:
label: API Provider (optional)
options:
- Anthropic
- Amazon Bedrock
- Chutes AI
- DeepSeek
- Featherless AI
- Fireworks AI
- Google Gemini
- Google Vertex AI
- Groq
- LiteLLM
- LM Studio
- Mistral AI
- Ollama
- OpenAI
- OpenAI Compatible
- OpenRouter
- Requesty
- SambaNova
- Unbound
- VS Code Language Model API
- xAI (Grok)
- Not Applicable / Other
- type: dropdown
id: provider
attributes:
label: API Provider (optional)
options:
- Anthropic
- Amazon Bedrock
- Chutes AI
- DeepSeek
- Featherless AI
- Fireworks AI
- Google Gemini
- Google Vertex AI
- Groq
- LiteLLM
- LM Studio
- Mistral AI
- Ollama
- OpenAI
- OpenAI Compatible
- OpenRouter
- Requesty
- SambaNova
- Unbound
- VS Code Language Model API
- xAI (Grok)
- Not Applicable / Other
- type: input
id: model
attributes:
label: Model Used (optional)
description: Exact model name (e.g., Claude 3.7 Sonnet). Use N/A if irrelevant.
- type: input
id: model
attributes:
label: Model Used (optional)
description: Exact model name (e.g., Claude 3.7 Sonnet). Use N/A if irrelevant.
- type: textarea
id: roo-code-tasks
attributes:
label: Roo Code Task Links (optional)
description: If you have any publicly shared Roo Code task links that demonstrate the issue, paste them here.
placeholder: Paste your Roo Code share links here, one per line
- type: textarea
id: roo-code-tasks
attributes:
label: Roo Code Task Links (optional)
description: If you have any publicly shared Roo Code task links that demonstrate the issue, paste them here.
placeholder: Paste your Roo Code share links here, one per line
- type: textarea
id: logs
attributes:
label: Relevant logs or errors (optional)
description: Paste relevant output or errors. Use triple backticks (```) for formatting.
render: shell
- type: textarea
id: logs
attributes:
label: Relevant logs or errors (optional)
description: Paste relevant output or errors. Use triple backticks (```) for formatting.
render: shell

View file

@ -3,89 +3,89 @@ description: Propose an improvement in plain language focused on user benefit
labels: ["enhancement"]
title: "[ENHANCEMENT] "
body:
- type: markdown
attributes:
value: |
Thank you for helping improve Roo Code!
Please focus on the problem and the desired behavior in plain language.
- type: markdown
attributes:
value: |
Thank you for helping improve Roo Code!
Please focus on the problem and the desired behavior in plain language.
- type: textarea
id: problem
attributes:
label: Problem (one or two sentences)
description: What problem are users facing?
placeholder: e.g., "Users often click Copy Run by mistake and duplicate runs unintentionally."
validations:
required: true
- type: textarea
id: context
attributes:
label: Context (who is affected and when)
description: Who encounters this and in what situation?
placeholder: e.g., "Happens when browsing the Runs list; most visible for new users."
validations:
required: true
- type: textarea
id: desired
attributes:
label: Desired behavior (conceptual, not technical)
description: Describe what should happen in simple terms.
placeholder: e.g., "Ask for confirmation before copying a run."
validations:
required: true
- type: textarea
id: constraints
attributes:
label: Constraints / preferences (optional)
description: Any considerations like performance, accessibility, or UX expectations.
placeholder: e.g., "Keep it quick and unobtrusive; keyboard accessible."
- type: checkboxes
id: checklist
attributes:
label: Request checklist
options:
- label: I've searched existing Issues and Discussions for duplicates
required: true
- label: This describes a specific problem with clear context and impact
- type: textarea
id: problem
attributes:
label: Problem (one or two sentences)
description: What problem are users facing?
placeholder: e.g., "Users often click Copy Run by mistake and duplicate runs unintentionally."
validations:
required: true
- type: textarea
id: roo-code-tasks
attributes:
label: Roo Code Task Links (optional)
description: If you explored this with Roo Code, share public task links for context.
placeholder: Paste your Roo Code share links here, one per line
- type: textarea
id: context
attributes:
label: Context (who is affected and when)
description: Who encounters this and in what situation?
placeholder: e.g., "Happens when browsing the Runs list; most visible for new users."
validations:
required: true
- type: markdown
attributes:
value: |
---
Optional (for contributors): You can stop here if you're just proposing the improvement.
- type: textarea
id: desired
attributes:
label: Desired behavior (conceptual, not technical)
description: Describe what should happen in simple terms.
placeholder: e.g., "Ask for confirmation before copying a run."
validations:
required: true
- type: textarea
id: acceptance-criteria
attributes:
label: Acceptance criteria (optional)
description: Define what “working” looks like with specific, testable outcomes.
placeholder: |
Given [context]
When [user action]
Then [expected result]
And [additional expectations]
But [what should NOT happen]
- type: textarea
id: constraints
attributes:
label: Constraints / preferences (optional)
description: Any considerations like performance, accessibility, or UX expectations.
placeholder: e.g., "Keep it quick and unobtrusive; keyboard accessible."
- type: textarea
id: proposed-solution
attributes:
label: Proposed approach (optional)
description: If you have an idea, describe it briefly in plain language.
- type: checkboxes
id: checklist
attributes:
label: Request checklist
options:
- label: I've searched existing Issues and Discussions for duplicates
required: true
- label: This describes a specific problem with clear context and impact
required: true
- type: textarea
id: risks
attributes:
label: Trade-offs / risks (optional)
description: Potential downsides or alternatives considered.
- type: textarea
id: roo-code-tasks
attributes:
label: Roo Code Task Links (optional)
description: If you explored this with Roo Code, share public task links for context.
placeholder: Paste your Roo Code share links here, one per line
- type: markdown
attributes:
value: |
---
Optional (for contributors): You can stop here if you're just proposing the improvement.
- type: textarea
id: acceptance-criteria
attributes:
label: Acceptance criteria (optional)
description: Define what “working” looks like with specific, testable outcomes.
placeholder: |
Given [context]
When [user action]
Then [expected result]
And [additional expectations]
But [what should NOT happen]
- type: textarea
id: proposed-solution
attributes:
label: Proposed approach (optional)
description: If you have an idea, describe it briefly in plain language.
- type: textarea
id: risks
attributes:
label: Trade-offs / risks (optional)
description: Potential downsides or alternatives considered.

View file

@ -2,61 +2,61 @@ name: Marketplace Feedback
description: Report issues or suggest improvements for marketplace items (custom modes and MCP servers)
labels: ["marketplace"]
body:
- type: markdown
attributes:
value: |
**Thanks for your feedback!** Please check existing issues first: https://github.com/RooCodeInc/Roo-Code/issues
- type: markdown
attributes:
value: |
**Thanks for your feedback!** Please check existing issues first: https://github.com/RooCodeInc/Roo-Code/issues
- type: dropdown
id: feedback-type
attributes:
label: What kind of feedback?
options:
- Problem with existing marketplace item
- Suggestion for new custom mode
- Suggestion for new MCP server
- General marketplace issue
validations:
required: true
- type: dropdown
id: feedback-type
attributes:
label: What kind of feedback?
options:
- Problem with existing marketplace item
- Suggestion for new custom mode
- Suggestion for new MCP server
- General marketplace issue
validations:
required: true
- type: dropdown
id: item-type
attributes:
label: Item Type (if applicable)
options:
- Custom Mode
- MCP Server
- Marketplace UI/Functionality
- Not Applicable
validations:
required: false
- type: dropdown
id: item-type
attributes:
label: Item Type (if applicable)
options:
- Custom Mode
- MCP Server
- Marketplace UI/Functionality
- Not Applicable
validations:
required: false
- type: input
id: item-name
attributes:
label: Item Name (if applicable)
placeholder: e.g., "Debug Mode", "Weather API Server", "Code Formatter"
- type: input
id: item-name
attributes:
label: Item Name (if applicable)
placeholder: e.g., "Debug Mode", "Weather API Server", "Code Formatter"
- type: textarea
id: description
attributes:
label: Description
description: What's the issue or what would you like to see?
placeholder: Clear description of the problem or suggestion
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: What's the issue or what would you like to see?
placeholder: Clear description of the problem or suggestion
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional Details (optional)
description: Steps to reproduce, expected behavior, screenshots, etc.
placeholder: Any other helpful information
- type: textarea
id: additional-info
attributes:
label: Additional Details (optional)
description: Steps to reproduce, expected behavior, screenshots, etc.
placeholder: Any other helpful information
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I've searched existing issues for duplicates
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I've searched existing issues for duplicates
required: true

View file

@ -1,58 +1,58 @@
name: 'Slack Notification'
description: 'Send Slack notification for workflow failures'
name: "Slack Notification"
description: "Send Slack notification for workflow failures"
inputs:
webhook-url:
description: 'Slack webhook URL'
required: true
channel:
description: 'Slack channel to notify'
required: true
workflow-name:
description: 'Name of the workflow'
required: true
failed-jobs:
description: 'JSON object containing job results'
required: true
webhook-url:
description: "Slack webhook URL"
required: true
channel:
description: "Slack channel to notify"
required: true
workflow-name:
description: "Name of the workflow"
required: true
failed-jobs:
description: "JSON object containing job results"
required: true
runs:
using: 'composite'
steps:
- name: Parse failed jobs
id: parse-jobs
shell: bash
run: |
echo "Parsing job results..."
failed_list=""
using: "composite"
steps:
- name: Parse failed jobs
id: parse-jobs
shell: bash
run: |
echo "Parsing job results..."
failed_list=""
echo '${{ inputs.failed-jobs }}' | jq -r 'to_entries[] | select(.value.result == "failure") | .key' | while read job; do
case $job in
"check-translations") failed_list="${failed_list}❌ Translation check\n" ;;
"knip") failed_list="${failed_list}❌ Knip analysis\n" ;;
"compile") failed_list="${failed_list}❌ Compile & lint\n" ;;
"unit-test") failed_list="${failed_list}❌ Unit tests\n" ;;
"integration-test") failed_list="${failed_list}❌ Integration tests\n" ;;
esac
done
echo '${{ inputs.failed-jobs }}' | jq -r 'to_entries[] | select(.value.result == "failure") | .key' | while read job; do
case $job in
"check-translations") failed_list="${failed_list}❌ Translation check\n" ;;
"knip") failed_list="${failed_list}❌ Knip analysis\n" ;;
"compile") failed_list="${failed_list}❌ Compile & lint\n" ;;
"unit-test") failed_list="${failed_list}❌ Unit tests\n" ;;
"integration-test") failed_list="${failed_list}❌ Integration tests\n" ;;
esac
done
echo "failed_jobs<<EOF" >> $GITHUB_OUTPUT
echo -e "$failed_list" | sed '/^$/d' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "failed_jobs<<EOF" >> $GITHUB_OUTPUT
echo -e "$failed_list" | sed '/^$/d' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Send Slack notification
uses: 8398a7/action-slack@v3
with:
status: failure
channel: ${{ inputs.channel }}
text: |
🚨 ${{ inputs.workflow-name }} workflow failed on main branch!
- name: Send Slack notification
uses: 8398a7/action-slack@v3
with:
status: failure
channel: ${{ inputs.channel }}
text: |
🚨 ${{ inputs.workflow-name }} workflow failed on main branch!
Repository: ${{ github.repository }}
Commit: ${{ github.sha }}
Author: ${{ github.actor }}
Repository: ${{ github.repository }}
Commit: ${{ github.sha }}
Author: ${{ github.actor }}
Failed jobs:
${{ steps.parse-jobs.outputs.failed_jobs }}
Failed jobs:
${{ steps.parse-jobs.outputs.failed_jobs }}
View details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
env:
SLACK_WEBHOOK_URL: ${{ inputs.webhook-url }}
View details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
env:
SLACK_WEBHOOK_URL: ${{ inputs.webhook-url }}

0
.github/copilot-instructions.md vendored Normal file
View file

View file

@ -5,7 +5,7 @@
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

View file

@ -2,131 +2,131 @@ name: Changeset Release
run-name: Changeset Release ${{ github.actor != 'R00-B0T' && '- Create PR' || '- Update Changelog' }}
on:
workflow_dispatch:
pull_request:
types: [closed, opened, labeled]
workflow_dispatch:
pull_request:
types: [closed, opened, labeled]
env:
REPO_PATH: ${{ github.repository }}
GIT_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
REPO_PATH: ${{ github.repository }}
GIT_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
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' ) ||
github.event_name == 'workflow_dispatch'
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 and pnpm
uses: ./.github/actions/setup-node-pnpm
# 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' ) ||
github.event_name == 'workflow_dispatch'
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 and pnpm
uses: ./.github/actions/setup-node-pnpm
# 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
# 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: pnpm changeset:version # This performs the changeset version bump
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 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: pnpm changeset:version # This performs the changeset version bump
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Job 2: Process version bump PR created by R00-B0T
changeset-pr-edit-approve:
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: Determine checkout ref
id: checkout-ref
run: |
echo "Event action: ${{ github.event.action }}"
echo "Actor: ${{ github.actor }}"
echo "Head ref: ${{ github.head_ref }}"
echo "PR SHA: ${{ github.event.pull_request.head.sha }}"
# Job 2: Process version bump PR created by R00-B0T
changeset-pr-edit-approve:
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: Determine checkout ref
id: checkout-ref
run: |
echo "Event action: ${{ github.event.action }}"
echo "Actor: ${{ github.actor }}"
echo "Head ref: ${{ github.head_ref }}"
echo "PR SHA: ${{ github.event.pull_request.head.sha }}"
if [[ "${{ github.event.action }}" == "opened" && "${{ github.actor }}" == "R00-B0T" ]]; then
echo "Using branch ref: ${{ github.head_ref }}"
echo "git_ref=${{ github.head_ref }}" >> $GITHUB_OUTPUT
else
echo "Using SHA ref: ${{ github.event.pull_request.head.sha }}"
echo "git_ref=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
fi
if [[ "${{ github.event.action }}" == "opened" && "${{ github.actor }}" == "R00-B0T" ]]; then
echo "Using branch ref: ${{ github.head_ref }}"
echo "git_ref=${{ github.head_ref }}" >> $GITHUB_OUTPUT
else
echo "Using SHA ref: ${{ github.event.pull_request.head.sha }}"
echo "git_ref=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
fi
- name: Checkout Repo
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
ref: ${{ steps.checkout-ref.outputs.git_ref }}
- name: Checkout Repo
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
ref: ${{ steps.checkout-ref.outputs.git_ref }}
# Commit and push changelog updates
- name: Push Changelog updates
if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-ready') }}
run: |
git config user.name "R00-B0T"
git config user.email github-actions@github.com
echo "Running git add and commit..."
git add CHANGELOG.md
git commit -m "Updating CHANGELOG.md format"
git status
echo "--------------------------------------------------------------------------------"
echo "Pushing to remote..."
echo "--------------------------------------------------------------------------------"
git push
# Commit and push changelog updates
- name: Push Changelog updates
if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-ready') }}
run: |
git config user.name "R00-B0T"
git config user.email github-actions@github.com
echo "Running git add and commit..."
git add CHANGELOG.md
git commit -m "Updating CHANGELOG.md format"
git status
echo "--------------------------------------------------------------------------------"
echo "Pushing to remote..."
echo "--------------------------------------------------------------------------------"
git push
# Add label to indicate changelog has been formatted
- name: Add changelog-ready label
if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-ready') }}
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: ['changelog-ready']
});
# Add label to indicate changelog has been formatted
- name: Add changelog-ready label
if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-ready') }}
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: ['changelog-ready']
});
# Auto-approve PR only after it has been labeled
- name: Auto approve PR
if: contains(github.event.pull_request.labels.*.name, 'changelog-ready')
uses: hmarr/auto-approve-action@v4
with:
review-message: "I'm approving since it's a bump version PR"
# Auto-merge PR
- name: Automerge on PR
if: false # Needs enablePullRequestAutoMerge in repo settings to work contains(github.event.pull_request.labels.*.name, 'changelog-ready')
run: gh pr merge --auto --merge ${{ github.event.pull_request.number }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Auto-approve PR only after it has been labeled
- name: Auto approve PR
if: contains(github.event.pull_request.labels.*.name, 'changelog-ready')
uses: hmarr/auto-approve-action@v4
with:
review-message: "I'm approving since it's a bump version PR"
# Auto-merge PR
- name: Automerge on PR
if: false # Needs enablePullRequestAutoMerge in repo settings to work contains(github.event.pull_request.labels.*.name, 'changelog-ready')
run: gh pr merge --auto --merge ${{ github.event.pull_request.number }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,394 +1,394 @@
name: CLI Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., 0.1.0). Leave empty to use package.json version.'
required: false
type: string
dry_run:
description: 'Dry run (build and test but do not create release).'
required: false
type: boolean
default: false
workflow_dispatch:
inputs:
version:
description: "Version to release (e.g., 0.1.0). Leave empty to use package.json version."
required: false
type: string
dry_run:
description: "Dry run (build and test but do not create release)."
required: false
type: boolean
default: false
jobs:
# Build CLI for each platform.
build:
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
platform: darwin-arm64
runs-on: macos-latest
- os: ubuntu-latest
platform: linux-x64
runs-on: ubuntu-latest
- os: ubuntu-24.04-arm
platform: linux-arm64
runs-on: ubuntu-24.04-arm
# Build CLI for each platform.
build:
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
platform: darwin-arm64
runs-on: macos-latest
- os: ubuntu-latest
platform: linux-x64
runs-on: ubuntu-latest
- os: ubuntu-24.04-arm
platform: linux-arm64
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on }}
runs-on: ${{ matrix.runs-on }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
- name: Get version
id: version
run: |
if [ -n "${{ inputs.version }}" ]; then
VERSION="${{ inputs.version }}"
else
VERSION=$(node -p "require('./apps/cli/package.json').version")
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=cli-v$VERSION" >> $GITHUB_OUTPUT
echo "Using version: $VERSION"
- name: Get version
id: version
run: |
if [ -n "${{ inputs.version }}" ]; then
VERSION="${{ inputs.version }}"
else
VERSION=$(node -p "require('./apps/cli/package.json').version")
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=cli-v$VERSION" >> $GITHUB_OUTPUT
echo "Using version: $VERSION"
- name: Build extension bundle
run: pnpm bundle
- name: Build extension bundle
run: pnpm bundle
- name: Build CLI
run: pnpm --filter @roo-code/cli build
- name: Build CLI
run: pnpm --filter @roo-code/cli build
- name: Create release tarball
id: tarball
env:
VERSION: ${{ steps.version.outputs.version }}
PLATFORM: ${{ matrix.platform }}
run: |
RELEASE_DIR="roo-cli-${PLATFORM}"
TARBALL="roo-cli-${PLATFORM}.tar.gz"
- name: Create release tarball
id: tarball
env:
VERSION: ${{ steps.version.outputs.version }}
PLATFORM: ${{ matrix.platform }}
run: |
RELEASE_DIR="roo-cli-${PLATFORM}"
TARBALL="roo-cli-${PLATFORM}.tar.gz"
# Clean up any previous build.
rm -rf "$RELEASE_DIR"
rm -f "$TARBALL"
# Clean up any previous build.
rm -rf "$RELEASE_DIR"
rm -f "$TARBALL"
# Create directory structure.
mkdir -p "$RELEASE_DIR/bin"
mkdir -p "$RELEASE_DIR/lib"
mkdir -p "$RELEASE_DIR/extension"
# Create directory structure.
mkdir -p "$RELEASE_DIR/bin"
mkdir -p "$RELEASE_DIR/lib"
mkdir -p "$RELEASE_DIR/extension"
# Copy CLI dist files.
echo "Copying CLI files..."
cp -r apps/cli/dist/* "$RELEASE_DIR/lib/"
# Copy CLI dist files.
echo "Copying CLI files..."
cp -r apps/cli/dist/* "$RELEASE_DIR/lib/"
# Create package.json for npm install.
echo "Creating package.json..."
node -e "
const pkg = require('./apps/cli/package.json');
const newPkg = {
name: '@roo-code/cli',
version: '$VERSION',
type: 'module',
dependencies: {
'@inkjs/ui': pkg.dependencies['@inkjs/ui'],
'@trpc/client': pkg.dependencies['@trpc/client'],
'commander': pkg.dependencies.commander,
'fuzzysort': pkg.dependencies.fuzzysort,
'ink': pkg.dependencies.ink,
'p-wait-for': pkg.dependencies['p-wait-for'],
'react': pkg.dependencies.react,
'superjson': pkg.dependencies.superjson,
'zustand': pkg.dependencies.zustand
}
};
console.log(JSON.stringify(newPkg, null, 2));
" > "$RELEASE_DIR/package.json"
# Create package.json for npm install.
echo "Creating package.json..."
node -e "
const pkg = require('./apps/cli/package.json');
const newPkg = {
name: '@roo-code/cli',
version: '$VERSION',
type: 'module',
dependencies: {
'@inkjs/ui': pkg.dependencies['@inkjs/ui'],
'@trpc/client': pkg.dependencies['@trpc/client'],
'commander': pkg.dependencies.commander,
'fuzzysort': pkg.dependencies.fuzzysort,
'ink': pkg.dependencies.ink,
'p-wait-for': pkg.dependencies['p-wait-for'],
'react': pkg.dependencies.react,
'superjson': pkg.dependencies.superjson,
'zustand': pkg.dependencies.zustand
}
};
console.log(JSON.stringify(newPkg, null, 2));
" > "$RELEASE_DIR/package.json"
# Copy extension bundle.
echo "Copying extension bundle..."
cp -r src/dist/* "$RELEASE_DIR/extension/"
# Copy extension bundle.
echo "Copying extension bundle..."
cp -r src/dist/* "$RELEASE_DIR/extension/"
# Add package.json to extension directory for CommonJS.
echo '{"type": "commonjs"}' > "$RELEASE_DIR/extension/package.json"
# Add package.json to extension directory for CommonJS.
echo '{"type": "commonjs"}' > "$RELEASE_DIR/extension/package.json"
# Find and copy ripgrep binary.
echo "Looking for ripgrep binary..."
RIPGREP_PATH=$(find node_modules -path "*/@vscode/ripgrep/bin/rg" -type f 2>/dev/null | head -1)
if [ -n "$RIPGREP_PATH" ] && [ -f "$RIPGREP_PATH" ]; then
echo "Found ripgrep at: $RIPGREP_PATH"
mkdir -p "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin"
cp "$RIPGREP_PATH" "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin/"
chmod +x "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin/rg"
mkdir -p "$RELEASE_DIR/bin"
cp "$RIPGREP_PATH" "$RELEASE_DIR/bin/"
chmod +x "$RELEASE_DIR/bin/rg"
else
echo "Warning: ripgrep binary not found"
fi
# Find and copy ripgrep binary.
echo "Looking for ripgrep binary..."
RIPGREP_PATH=$(find node_modules -path "*/@vscode/ripgrep/bin/rg" -type f 2>/dev/null | head -1)
if [ -n "$RIPGREP_PATH" ] && [ -f "$RIPGREP_PATH" ]; then
echo "Found ripgrep at: $RIPGREP_PATH"
mkdir -p "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin"
cp "$RIPGREP_PATH" "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin/"
chmod +x "$RELEASE_DIR/node_modules/@vscode/ripgrep/bin/rg"
mkdir -p "$RELEASE_DIR/bin"
cp "$RIPGREP_PATH" "$RELEASE_DIR/bin/"
chmod +x "$RELEASE_DIR/bin/rg"
else
echo "Warning: ripgrep binary not found"
fi
# Create the wrapper script
echo "Creating wrapper script..."
printf '%s\n' '#!/usr/bin/env node' \
'' \
"import { fileURLToPath } from 'url';" \
"import { dirname, join } from 'path';" \
'' \
'const __filename = fileURLToPath(import.meta.url);' \
'const __dirname = dirname(__filename);' \
'' \
'// Set environment variables for the CLI' \
"process.env.ROO_CLI_ROOT = join(__dirname, '..');" \
"process.env.ROO_EXTENSION_PATH = join(__dirname, '..', 'extension');" \
"process.env.ROO_RIPGREP_PATH = join(__dirname, 'rg');" \
'' \
'// Import and run the actual CLI' \
"await import(join(__dirname, '..', 'lib', 'index.js'));" \
> "$RELEASE_DIR/bin/roo"
# Create the wrapper script
echo "Creating wrapper script..."
printf '%s\n' '#!/usr/bin/env node' \
'' \
"import { fileURLToPath } from 'url';" \
"import { dirname, join } from 'path';" \
'' \
'const __filename = fileURLToPath(import.meta.url);' \
'const __dirname = dirname(__filename);' \
'' \
'// Set environment variables for the CLI' \
"process.env.ROO_CLI_ROOT = join(__dirname, '..');" \
"process.env.ROO_EXTENSION_PATH = join(__dirname, '..', 'extension');" \
"process.env.ROO_RIPGREP_PATH = join(__dirname, 'rg');" \
'' \
'// Import and run the actual CLI' \
"await import(join(__dirname, '..', 'lib', 'index.js'));" \
> "$RELEASE_DIR/bin/roo"
chmod +x "$RELEASE_DIR/bin/roo"
chmod +x "$RELEASE_DIR/bin/roo"
# Create empty .env file.
touch "$RELEASE_DIR/.env"
# Create empty .env file.
touch "$RELEASE_DIR/.env"
# Create tarball.
echo "Creating tarball..."
tar -czvf "$TARBALL" "$RELEASE_DIR"
# Create tarball.
echo "Creating tarball..."
tar -czvf "$TARBALL" "$RELEASE_DIR"
# Clean up release directory.
rm -rf "$RELEASE_DIR"
# Clean up release directory.
rm -rf "$RELEASE_DIR"
# Create checksum.
if command -v sha256sum &> /dev/null; then
sha256sum "$TARBALL" > "${TARBALL}.sha256"
elif command -v shasum &> /dev/null; then
shasum -a 256 "$TARBALL" > "${TARBALL}.sha256"
fi
# Create checksum.
if command -v sha256sum &> /dev/null; then
sha256sum "$TARBALL" > "${TARBALL}.sha256"
elif command -v shasum &> /dev/null; then
shasum -a 256 "$TARBALL" > "${TARBALL}.sha256"
fi
echo "tarball=$TARBALL" >> $GITHUB_OUTPUT
echo "Created: $TARBALL"
ls -la "$TARBALL"
echo "tarball=$TARBALL" >> $GITHUB_OUTPUT
echo "Created: $TARBALL"
ls -la "$TARBALL"
- name: Verify tarball
env:
PLATFORM: ${{ matrix.platform }}
run: |
TARBALL="roo-cli-${PLATFORM}.tar.gz"
- name: Verify tarball
env:
PLATFORM: ${{ matrix.platform }}
run: |
TARBALL="roo-cli-${PLATFORM}.tar.gz"
# Create temp directory for verification.
VERIFY_DIR=$(mktemp -d)
# Create temp directory for verification.
VERIFY_DIR=$(mktemp -d)
# Extract and verify structure.
tar -xzf "$TARBALL" -C "$VERIFY_DIR"
# Extract and verify structure.
tar -xzf "$TARBALL" -C "$VERIFY_DIR"
echo "Verifying tarball contents..."
ls -la "$VERIFY_DIR/roo-cli-${PLATFORM}/"
echo "Verifying tarball contents..."
ls -la "$VERIFY_DIR/roo-cli-${PLATFORM}/"
# Check required files exist.
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/bin/roo" || { echo "Missing bin/roo"; exit 1; }
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/lib/index.js" || { echo "Missing lib/index.js"; exit 1; }
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/package.json" || { echo "Missing package.json"; exit 1; }
test -d "$VERIFY_DIR/roo-cli-${PLATFORM}/extension" || { echo "Missing extension directory"; exit 1; }
# Check required files exist.
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/bin/roo" || { echo "Missing bin/roo"; exit 1; }
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/lib/index.js" || { echo "Missing lib/index.js"; exit 1; }
test -f "$VERIFY_DIR/roo-cli-${PLATFORM}/package.json" || { echo "Missing package.json"; exit 1; }
test -d "$VERIFY_DIR/roo-cli-${PLATFORM}/extension" || { echo "Missing extension directory"; exit 1; }
echo "Tarball verification passed!"
echo "Tarball verification passed!"
# Cleanup.
rm -rf "$VERIFY_DIR"
# Cleanup.
rm -rf "$VERIFY_DIR"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: cli-${{ matrix.platform }}
path: |
roo-cli-${{ matrix.platform }}.tar.gz
roo-cli-${{ matrix.platform }}.tar.gz.sha256
retention-days: 7
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: cli-${{ matrix.platform }}
path: |
roo-cli-${{ matrix.platform }}.tar.gz
roo-cli-${{ matrix.platform }}.tar.gz.sha256
retention-days: 7
# Create GitHub release with all platform artifacts.
release:
needs: build
runs-on: ubuntu-latest
if: ${{ !inputs.dry_run }}
permissions:
contents: write
# Create GitHub release with all platform artifacts.
release:
needs: build
runs-on: ubuntu-latest
if: ${{ !inputs.dry_run }}
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get version
id: version
run: |
if [ -n "${{ inputs.version }}" ]; then
VERSION="${{ inputs.version }}"
else
VERSION=$(node -p "require('./apps/cli/package.json').version")
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=cli-v$VERSION" >> $GITHUB_OUTPUT
- name: Get version
id: version
run: |
if [ -n "${{ inputs.version }}" ]; then
VERSION="${{ inputs.version }}"
else
VERSION=$(node -p "require('./apps/cli/package.json').version")
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=cli-v$VERSION" >> $GITHUB_OUTPUT
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare release files
run: |
mkdir -p release
find artifacts -name "*.tar.gz" -exec cp {} release/ \;
find artifacts -name "*.sha256" -exec cp {} release/ \;
ls -la release/
- name: Prepare release files
run: |
mkdir -p release
find artifacts -name "*.tar.gz" -exec cp {} release/ \;
find artifacts -name "*.sha256" -exec cp {} release/ \;
ls -la release/
- name: Extract changelog
id: changelog
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
CHANGELOG_FILE="apps/cli/CHANGELOG.md"
- name: Extract changelog
id: changelog
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
CHANGELOG_FILE="apps/cli/CHANGELOG.md"
if [ -f "$CHANGELOG_FILE" ]; then
# Extract content between version headers.
CONTENT=$(awk -v version="$VERSION" '
BEGIN { found = 0; content = ""; target = "[" version "]" }
/^## \[/ {
if (found) { exit }
if (index($0, target) > 0) { found = 1; next }
}
found { content = content $0 "\n" }
END { print content }
' "$CHANGELOG_FILE")
if [ -f "$CHANGELOG_FILE" ]; then
# Extract content between version headers.
CONTENT=$(awk -v version="$VERSION" '
BEGIN { found = 0; content = ""; target = "[" version "]" }
/^## \[/ {
if (found) { exit }
if (index($0, target) > 0) { found = 1; next }
}
found { content = content $0 "\n" }
END { print content }
' "$CHANGELOG_FILE")
if [ -n "$CONTENT" ]; then
echo "Found changelog content"
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "No changelog content found for version $VERSION"
echo "content=" >> $GITHUB_OUTPUT
fi
else
echo "No changelog file found"
echo "content=" >> $GITHUB_OUTPUT
fi
if [ -n "$CONTENT" ]; then
echo "Found changelog content"
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "$CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "No changelog content found for version $VERSION"
echo "content=" >> $GITHUB_OUTPUT
fi
else
echo "No changelog file found"
echo "content=" >> $GITHUB_OUTPUT
fi
- name: Generate checksums summary
id: checksums
run: |
echo "checksums<<EOF" >> $GITHUB_OUTPUT
cat release/*.sha256 >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Generate checksums summary
id: checksums
run: |
echo "checksums<<EOF" >> $GITHUB_OUTPUT
cat release/*.sha256 >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Check for existing release
id: check_release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
if gh release view "$TAG" &> /dev/null; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Check for existing release
id: check_release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
if gh release view "$TAG" &> /dev/null; then
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Delete existing release
if: steps.check_release.outputs.exists == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
echo "Deleting existing release $TAG..."
gh release delete "$TAG" --yes || true
git push origin ":refs/tags/$TAG" || true
- name: Delete existing release
if: steps.check_release.outputs.exists == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
run: |
echo "Deleting existing release $TAG..."
gh release delete "$TAG" --yes || true
git push origin ":refs/tags/$TAG" || true
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ steps.version.outputs.tag }}
CHANGELOG_CONTENT: ${{ steps.changelog.outputs.content }}
CHECKSUMS: ${{ steps.checksums.outputs.checksums }}
run: |
NOTES_FILE=$(mktemp)
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
TAG: ${{ steps.version.outputs.tag }}
CHANGELOG_CONTENT: ${{ steps.changelog.outputs.content }}
CHECKSUMS: ${{ steps.checksums.outputs.checksums }}
run: |
NOTES_FILE=$(mktemp)
if [ -n "$CHANGELOG_CONTENT" ]; then
echo "## What's New" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "$CHANGELOG_CONTENT" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
fi
if [ -n "$CHANGELOG_CONTENT" ]; then
echo "## What's New" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "$CHANGELOG_CONTENT" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
fi
echo "## Installation" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo '```bash' >> "$NOTES_FILE"
echo "curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "Or install a specific version:" >> "$NOTES_FILE"
echo '```bash' >> "$NOTES_FILE"
echo "ROO_VERSION=$VERSION curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "## Requirements" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "- Node.js 20 or higher" >> "$NOTES_FILE"
echo "- macOS Apple Silicon (M1/M2/M3/M4), Linux x64, or Linux ARM64" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "## Usage" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo '```bash' >> "$NOTES_FILE"
echo "# Run a task" >> "$NOTES_FILE"
echo 'roo "What is this project?"' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "# See all options" >> "$NOTES_FILE"
echo "roo --help" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "## Platform Support" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "This release includes binaries for:" >> "$NOTES_FILE"
echo '- `roo-cli-darwin-arm64.tar.gz` - macOS Apple Silicon (M1/M2/M3)' >> "$NOTES_FILE"
echo '- `roo-cli-linux-x64.tar.gz` - Linux x64' >> "$NOTES_FILE"
echo '- `roo-cli-linux-arm64.tar.gz` - Linux ARM64' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "## Checksums" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "$CHECKSUMS" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "## Installation" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo '```bash' >> "$NOTES_FILE"
echo "curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "Or install a specific version:" >> "$NOTES_FILE"
echo '```bash' >> "$NOTES_FILE"
echo "ROO_VERSION=$VERSION curl -fsSL https://raw.githubusercontent.com/RooCodeInc/Roo-Code/main/apps/cli/install.sh | sh" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "## Requirements" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "- Node.js 20 or higher" >> "$NOTES_FILE"
echo "- macOS Apple Silicon (M1/M2/M3/M4), Linux x64, or Linux ARM64" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "## Usage" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo '```bash' >> "$NOTES_FILE"
echo "# Run a task" >> "$NOTES_FILE"
echo 'roo "What is this project?"' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "# See all options" >> "$NOTES_FILE"
echo "roo --help" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "## Platform Support" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "This release includes binaries for:" >> "$NOTES_FILE"
echo '- `roo-cli-darwin-arm64.tar.gz` - macOS Apple Silicon (M1/M2/M3)' >> "$NOTES_FILE"
echo '- `roo-cli-linux-x64.tar.gz` - Linux x64' >> "$NOTES_FILE"
echo '- `roo-cli-linux-arm64.tar.gz` - Linux ARM64' >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "## Checksums" >> "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
echo "$CHECKSUMS" >> "$NOTES_FILE"
echo '```' >> "$NOTES_FILE"
gh release create "$TAG" \
--title "Roo Code CLI v$VERSION" \
--notes-file "$NOTES_FILE" \
--prerelease \
release/*
gh release create "$TAG" \
--title "Roo Code CLI v$VERSION" \
--notes-file "$NOTES_FILE" \
--prerelease \
release/*
rm -f "$NOTES_FILE"
echo "Release created: https://github.com/${{ github.repository }}/releases/tag/$TAG"
rm -f "$NOTES_FILE"
echo "Release created: https://github.com/${{ github.repository }}/releases/tag/$TAG"
# Summary job for dry runs
summary:
needs: build
runs-on: ubuntu-latest
if: ${{ inputs.dry_run }}
# Summary job for dry runs
summary:
needs: build
runs-on: ubuntu-latest
if: ${{ inputs.dry_run }}
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Show build summary
run: |
echo "## Dry Run Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The following artifacts were built:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
find artifacts -name "*.tar.gz" | while read f; do
SIZE=$(ls -lh "$f" | awk '{print $5}')
echo "- $(basename $f) ($SIZE)" >> $GITHUB_STEP_SUMMARY
done
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Checksums" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
cat artifacts/*/*.sha256 >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
- name: Show build summary
run: |
echo "## Dry Run Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The following artifacts were built:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
find artifacts -name "*.tar.gz" | while read f; do
SIZE=$(ls -lh "$f" | awk '{print $5}')
echo "- $(basename $f) ($SIZE)" >> $GITHUB_STEP_SUMMARY
done
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Checksums" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
cat artifacts/*/*.sha256 >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY

View file

@ -1,81 +1,81 @@
name: CodeQL Advanced
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '24 19 * * 3'
push:
branches: ["main"]
pull_request:
branches: ["main"]
schedule:
- cron: "24 19 * * 3"
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: javascript-typescript
build-mode: none
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
strategy:
fail-fast: false
matrix:
include:
- language: javascript-typescript
build-mode: none
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View file

@ -1,92 +1,92 @@
name: Publish Extension
on:
pull_request:
types: [closed]
workflow_dispatch:
pull_request:
types: [closed]
workflow_dispatch:
env:
GIT_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
GIT_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
jobs:
publish-extension:
runs-on: ubuntu-latest
permissions:
contents: write # Required for pushing tags.
if: >
( github.event_name == 'pull_request' &&
github.event.pull_request.base.ref == 'main' &&
contains(github.event.pull_request.title, 'Changeset version bump') ) ||
github.event_name == 'workflow_dispatch'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ env.GIT_REF }}
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Create .env file
run: echo "POSTHOG_API_KEY=${{ secrets.POSTHOG_API_KEY }}" >> .env
- name: Package Extension
run: |
current_package_version=$(node -p "require('./src/package.json').version")
pnpm vsix
publish-extension:
runs-on: ubuntu-latest
permissions:
contents: write # Required for pushing tags.
if: >
( github.event_name == 'pull_request' &&
github.event.pull_request.base.ref == 'main' &&
contains(github.event.pull_request.title, 'Changeset version bump') ) ||
github.event_name == 'workflow_dispatch'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ env.GIT_REF }}
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Create .env file
run: echo "POSTHOG_API_KEY=${{ secrets.POSTHOG_API_KEY }}" >> .env
- name: Package Extension
run: |
current_package_version=$(node -p "require('./src/package.json').version")
pnpm vsix
# Save VSIX contents to a temporary file to avoid broken pipe issues.
unzip -l bin/roo-cline-${current_package_version}.vsix > /tmp/roo-code-vsix-contents.txt
# Save VSIX contents to a temporary file to avoid broken pipe issues.
unzip -l bin/roo-cline-${current_package_version}.vsix > /tmp/roo-code-vsix-contents.txt
# Check for required files.
grep -q "extension/package.json" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/package.nls.json" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/dist/extension.js" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/webview-ui/audio/celebration.wav" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/webview-ui/build/assets/index.js" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/assets/codicons/codicon.ttf" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/assets/vscode-material-icons/icons/3d.svg" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q ".env" /tmp/roo-code-vsix-contents.txt || exit 1
# Clean up temporary file.
rm /tmp/roo-code-vsix-contents.txt
- name: Create and Push Git Tag
run: |
current_package_version=$(node -p "require('./src/package.json').version")
git tag -a "v${current_package_version}" -m "Release v${current_package_version}"
git push origin "v${current_package_version}" --no-verify
echo "Successfully created and pushed git tag v${current_package_version}"
- name: Publish Extension
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: |
current_package_version=$(node -p "require('./src/package.json').version")
pnpm --filter roo-cline publish:marketplace
echo "Successfully published version $current_package_version to VS Code Marketplace"
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
current_package_version=$(node -p "require('./src/package.json').version")
# Check for required files.
grep -q "extension/package.json" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/package.nls.json" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/dist/extension.js" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/webview-ui/audio/celebration.wav" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/webview-ui/build/assets/index.js" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/assets/codicons/codicon.ttf" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q "extension/assets/vscode-material-icons/icons/3d.svg" /tmp/roo-code-vsix-contents.txt || exit 1
grep -q ".env" /tmp/roo-code-vsix-contents.txt || exit 1
# Extract changelog for current version
echo "Extracting changelog for version ${current_package_version}"
changelog_content=$(sed -n "/## \\[${current_package_version}\\]/,/## \\[/p" CHANGELOG.md | sed '$d')
# Clean up temporary file.
rm /tmp/roo-code-vsix-contents.txt
- name: Create and Push Git Tag
run: |
current_package_version=$(node -p "require('./src/package.json').version")
git tag -a "v${current_package_version}" -m "Release v${current_package_version}"
git push origin "v${current_package_version}" --no-verify
echo "Successfully created and pushed git tag v${current_package_version}"
- name: Publish Extension
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: |
current_package_version=$(node -p "require('./src/package.json').version")
pnpm --filter roo-cline publish:marketplace
echo "Successfully published version $current_package_version to VS Code Marketplace"
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
current_package_version=$(node -p "require('./src/package.json').version")
# If changelog extraction failed, use a default message
if [ -z "$changelog_content" ]; then
echo "Warning: No changelog section found for version ${current_package_version}"
changelog_content="Release v${current_package_version}"
else
echo "Found changelog section for version ${current_package_version}"
fi
# Extract changelog for current version
echo "Extracting changelog for version ${current_package_version}"
changelog_content=$(sed -n "/## \\[${current_package_version}\\]/,/## \\[/p" CHANGELOG.md | sed '$d')
# Create release with changelog content
gh release create "v${current_package_version}" \
--title "Release v${current_package_version}" \
--notes "$changelog_content" \
--target ${{ env.GIT_REF }} \
bin/roo-cline-${current_package_version}.vsix
echo "Successfully created GitHub Release v${current_package_version}"
# If changelog extraction failed, use a default message
if [ -z "$changelog_content" ]; then
echo "Warning: No changelog section found for version ${current_package_version}"
changelog_content="Release v${current_package_version}"
else
echo "Found changelog section for version ${current_package_version}"
fi
# Create release with changelog content
gh release create "v${current_package_version}" \
--title "Release v${current_package_version}" \
--notes "$changelog_content" \
--target ${{ env.GIT_REF }} \
bin/roo-cline-${current_package_version}.vsix
echo "Successfully created GitHub Release v${current_package_version}"

View file

@ -1,52 +1,52 @@
name: Nightly Publish
on:
push:
branches: [main]
workflow_dispatch: # Allows manual triggering.
push:
branches: [main]
workflow_dispatch: # Allows manual triggering.
jobs:
publish-nightly:
runs-on: ubuntu-latest
publish-nightly:
runs-on: ubuntu-latest
permissions:
contents: read # No tags pushed → read is enough.
permissions:
contents: read # No tags pushed → read is enough.
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
with:
install-args: '--frozen-lockfile'
- name: Forge numeric Nightly version
id: version
env:
RUN_NUMBER: ${{ github.run_number }}
run: echo "number=$(( 5500 + ${RUN_NUMBER} ))" >> $GITHUB_OUTPUT
- name: Patch package.json version
env:
VERSION_NUMBER: ${{ steps.version.outputs.number }}
run: |
node <<'EOF'
const fs = require('fs');
const path = require('path');
const pkgPath = path.join(__dirname, 'apps', 'vscode-nightly', 'package.nightly.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath,'utf8'));
const [maj, min] = pkg.version.split('.');
pkg.version = `${maj}.${min}.${process.env.VERSION_NUMBER}`;
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
console.log(`🔖 Nightly version set to ${pkg.version}`);
EOF
- name: Build VSIX
run: pnpm vsix:nightly # Produces bin/roo-code-nightly-0.0.[count].vsix
- name: Publish to VS Code Marketplace
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: npx vsce publish --packagePath "bin/$(/bin/ls bin | head -n1)"
- name: Publish to Open VSX Registry
env:
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: npx ovsx publish "bin/$(ls bin | head -n1)"
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
with:
install-args: "--frozen-lockfile"
- name: Forge numeric Nightly version
id: version
env:
RUN_NUMBER: ${{ github.run_number }}
run: echo "number=$(( 5500 + ${RUN_NUMBER} ))" >> $GITHUB_OUTPUT
- name: Patch package.json version
env:
VERSION_NUMBER: ${{ steps.version.outputs.number }}
run: |
node <<'EOF'
const fs = require('fs');
const path = require('path');
const pkgPath = path.join(__dirname, 'apps', 'vscode-nightly', 'package.nightly.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath,'utf8'));
const [maj, min] = pkg.version.split('.');
pkg.version = `${maj}.${min}.${process.env.VERSION_NUMBER}`;
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
console.log(`🔖 Nightly version set to ${pkg.version}`);
EOF
- name: Build VSIX
run: pnpm vsix:nightly # Produces bin/roo-code-nightly-0.0.[count].vsix
- name: Publish to VS Code Marketplace
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: npx vsce publish --packagePath "bin/$(/bin/ls bin | head -n1)"
- name: Publish to Open VSX Registry
env:
OVSX_PAT: ${{ secrets.OVSX_PAT }}
run: npx ovsx publish "bin/$(ls bin | head -n1)"

View file

@ -1,67 +1,67 @@
name: Update Contributors # Refresh contrib.rocks image cache
on:
workflow_dispatch:
workflow_dispatch:
permissions:
contents: write
pull-requests: write
contents: write
pull-requests: write
jobs:
refresh-contrib-cache:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
refresh-contrib-cache:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Bump cacheBust in all README files
run: |
set -euo pipefail
TS="$(date +%s)"
# Target only the root README.md and localized READMEs under locales/*/README.md
mapfile -t FILES < <(git ls-files README.md 'locales/*/README.md' || true)
- name: Bump cacheBust in all README files
run: |
set -euo pipefail
TS="$(date +%s)"
# Target only the root README.md and localized READMEs under locales/*/README.md
mapfile -t FILES < <(git ls-files README.md 'locales/*/README.md' || true)
if [ "${#FILES[@]}" -eq 0 ]; then
echo "No target README files found." >&2
exit 1
fi
if [ "${#FILES[@]}" -eq 0 ]; then
echo "No target README files found." >&2
exit 1
fi
UPDATED=0
for f in "${FILES[@]}"; do
if grep -q 'cacheBust=' "$f"; then
# Use portable sed in GNU environment of ubuntu-latest
sed -i -E "s/cacheBust=[0-9]+/cacheBust=${TS}/g" "$f"
echo "Updated cacheBust in $f"
UPDATED=1
else
echo "Warning: cacheBust parameter not found in $f" >&2
fi
done
UPDATED=0
for f in "${FILES[@]}"; do
if grep -q 'cacheBust=' "$f"; then
# Use portable sed in GNU environment of ubuntu-latest
sed -i -E "s/cacheBust=[0-9]+/cacheBust=${TS}/g" "$f"
echo "Updated cacheBust in $f"
UPDATED=1
else
echo "Warning: cacheBust parameter not found in $f" >&2
fi
done
if [ "$UPDATED" -eq 0 ]; then
echo "No files were updated. Ensure READMEs embed contrib.rocks with cacheBust param." >&2
exit 1
fi
if [ "$UPDATED" -eq 0 ]; then
echo "No files were updated. Ensure READMEs embed contrib.rocks with cacheBust param." >&2
exit 1
fi
- name: Detect changes
id: changes
run: |
if git diff --quiet; then
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Detect changes
id: changes
run: |
if git diff --quiet; then
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request
if: steps.changes.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "docs: update contributors list [skip ci]"
committer: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
branch: refresh-contrib-cache
delete-branch: true
title: "Refresh contrib.rocks image cache (all READMEs)"
body: |
Automated refresh of the contrib.rocks image cache by bumping the cacheBust parameter in README.md and locales/*/README.md.
base: main
- name: Create Pull Request
if: steps.changes.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "docs: update contributors list [skip ci]"
committer: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
branch: refresh-contrib-cache
delete-branch: true
title: "Refresh contrib.rocks image cache (all READMEs)"
body: |
Automated refresh of the contrib.rocks image cache by bumping the cacheBust parameter in README.md and locales/*/README.md.
base: main

View file

@ -5,7 +5,7 @@ on:
branches:
- main
paths:
- 'apps/web-roo-code/**'
- "apps/web-roo-code/**"
workflow_dispatch:
concurrency:
@ -25,11 +25,11 @@ jobs:
- name: Check if VERCEL_TOKEN exists
id: check
run: |
if [ -n "${{ secrets.VERCEL_TOKEN }}" ]; then
echo "has-vercel-token=true" >> $GITHUB_OUTPUT
else
echo "has-vercel-token=false" >> $GITHUB_OUTPUT
fi
if [ -n "${{ secrets.VERCEL_TOKEN }}" ]; then
echo "has-vercel-token=true" >> $GITHUB_OUTPUT
else
echo "has-vercel-token=false" >> $GITHUB_OUTPUT
fi
deploy:
runs-on: ubuntu-latest

14
.gitignore vendored
View file

@ -23,7 +23,7 @@ AGENTS.local.md
# Test environment
.test_env
.vscode-test/
.venv/
# Docs
docs/_site/
@ -55,3 +55,15 @@ qdrant_storage/
plans/
roo-cli-*.tar.gz*
# VS Code MCP analytics file — do not commit
.vscode/mcp.json
# --- Orchestration Sidecar (Phase 1) ---
# We commit the .yaml spec and .py hooks, but NEVER the local data
.orchestration/*.db
.orchestration/*.db-journal
.orchestration/*.db-wal
.orchestration/zvec_store/
.orchestration/agent_trace.jsonl

10
.intentignore Normal file
View file

@ -0,0 +1,10 @@
# GLOBAL SECURITY BOUNDARY
.env
.git/
node_modules/
.orchestration/trace_storage.db
.clinerules
# INTENT-SPECIFIC PROTECTION
# Prevents the AI from modifying the core orchestration logic
.orchestration/hooks/

BIN
.nvmrc

Binary file not shown.

View file

@ -0,0 +1,15 @@
active_intents:
- id: "INT-001"
name: "Initialize TRACE Governance"
status: "COMPLETED"
owned_scope: ["src/hooks/**"]
# ... other fields ...
- id: "INT-002"
name: "Refactor Auth Middleware"
status: "IN_PROGRESS"
owned_scope: ["src/auth/**", "src/middleware/jwt.ts"]
constraints:
- "Must maintain backward compatibility with Basic Auth"
acceptance_criteria:
- "JWT rotation tests pass"

View file

@ -0,0 +1 @@
{"id": "init-uuid", "timestamp": "2026-02-16T12:00:00Z", "intent_id": "INT-001", "vcs": { "revision_id": "initial" }, "files": [{"relative_path": "src/hooks/Gatekeeper.ts", "conversations": [{"url": "init_session", "contributor": {"entity_type": "AI", "model_identifier": "claude-3-5-sonnet"}, "ranges": [{"start_line": 1, "end_line": 1, "content_hash": "sha256:0000000"}]}]}]}{"mutation_class":"INTENT_EVOLUTION","timestamp":"2026-02-20T19:57:01Z","content_hash":"f3d8ec89f06e5a685faba375e55a2115aaab6ba51a6ef2ff336a6415d079c2f4","related_reqs":["REQ-001"],"file_path":".orchestration/phase2_check.txt","intent_id":"INT-002"}

View file

@ -0,0 +1,34 @@
import sqlite3
import os
# Logic: Define the path for our Sidecar DB
db_path = os.path.join(".orchestration", "trace_storage.db")
def init_db():
try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Logic: Create the table for your Agent Traces (Phase 1 Data Model)
cursor.execute('''
CREATE TABLE IF NOT EXISTS agent_traces (
id TEXT PRIMARY KEY,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
intent_id TEXT,
file_path TEXT,
content_hash TEXT,
metadata TEXT
)
''')
# Logic: Optimization for low RAM - use Disk as Virtual RAM
cursor.execute("PRAGMA mmap_size = 268435456;") # 256MB mmap
conn.commit()
conn.close()
print(f"✅ Orchestration Ledger Initialized at {db_path}")
except Exception as e:
print(f"❌ Error: {e}")
if __name__ == "__main__":
init_db()

View file

@ -0,0 +1,43 @@
import hashlib
import sqlite3
import sys
import os
import uuid
# Logic: Maintain consistent pathing for Windows/Linux
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
DB_PATH = os.path.join(BASE_DIR, ".orchestration", "trace_storage.db")
def calculate_sha256(content):
"""Normalized hashing: ignores whitespace and line endings to ensure
consistency across different editors/OS."""
normalized = "".join(content.split()).encode('utf-8')
return hashlib.sha256(normalized).hexdigest()
def log_change(intent_id, file_path, content):
content_hash = calculate_sha256(content)
trace_id = str(uuid.uuid4())
try:
conn = sqlite3.connect(DB_PATH)
# Performance: Use Write-Ahead Logging for faster disk I/O
conn.execute("PRAGMA journal_mode=WAL;")
cursor = conn.cursor()
cursor.execute('''
INSERT INTO agent_traces (id, intent_id, file_path, content_hash)
VALUES (?, ?, ?, ?)
''', (trace_id, intent_id, file_path, content_hash))
conn.commit()
conn.close()
return content_hash
except Exception as e:
return f"Database Error: {e}"
if __name__ == "__main__":
# Command: python tmd.py <intent_id> <file_path> <content>
if len(sys.argv) >= 4:
intent, path, code = sys.argv[1], sys.argv[2], sys.argv[3]
result_hash = log_change(intent, path, code)
print(f"HASH_VERIFIED:{result_hash}")
else:
print("Usage: python tmd.py <intent_id> <file_path> <content>")

View file

View file

@ -0,0 +1 @@
Human edited this first!

View file

@ -0,0 +1,11 @@
{
"version": "1.0.0",
"command_classification": {
"safe": ["read_file", "list_files", "search_files"],
"destructive": ["write_to_file", "delete_file", "execute_command"]
},
"error_protocol": {
"type": "JSON_TOOL_ERROR",
"template": "Scope Violation: {intent_id} is not authorized to edit {file}. Request scope expansion."
}
}

View file

@ -11,10 +11,10 @@ Resolve merge conflicts for a specific pull request by analyzing git history, co
1. **Provide a PR number** (e.g., `#123` or just `123`)
2. The workflow will automatically:
- Fetch PR information (title, description, branches)
- Checkout the PR branch
- Rebase onto the target branch to reveal conflicts
- Analyze and resolve conflicts using git history
- Fetch PR information (title, description, branches)
- Checkout the PR branch
- Rebase onto the target branch to reveal conflicts
- Analyze and resolve conflicts using git history
## Workflow Steps
@ -39,6 +39,7 @@ git status --porcelain | grep "^UU"
### 3. Analyze Each Conflict
For each conflicted file:
- Read the conflict markers
- Run `git blame` on conflicting sections
- Fetch commit messages for context
@ -47,6 +48,7 @@ For each conflicted file:
### 4. Apply Resolution Strategy
Based on the analysis:
- **Bugfixes** generally take precedence over features
- **Recent changes** are often more relevant (unless older is a security fix)
- **Combine** non-conflicting changes when possible

View file

@ -9,32 +9,35 @@ Perform translation and localization tasks for the Roo Code extension. This comm
## Quick Start
1. **Identify the translation scope:**
- If a specific language code is provided (e.g., `de`, `zh-CN`), focus on that language
- If `all` is specified, translate to all supported languages
- If a string key is provided, locate and translate that specific string
- If a file path is provided, work with that translation file
- If a specific language code is provided (e.g., `de`, `zh-CN`), focus on that language
- If `all` is specified, translate to all supported languages
- If a string key is provided, locate and translate that specific string
- If a file path is provided, work with that translation file
2. **Supported languages:** ca, de, en, es, fr, hi, id, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW
3. **Translation locations:**
- Core Extension: `src/i18n/locales/`
- WebView UI: `webview-ui/src/i18n/locales/`
- Core Extension: `src/i18n/locales/`
- WebView UI: `webview-ui/src/i18n/locales/`
## Workflow
1. If adding new strings:
- Add the English string first
- Ask for confirmation before translating to other languages
- Use `apply_diff` for efficient file updates
- Add the English string first
- Ask for confirmation before translating to other languages
- Use `apply_diff` for efficient file updates
2. If updating existing strings:
- Identify all affected language files
- Update English first, then propagate changes
- Identify all affected language files
- Update English first, then propagate changes
3. Validate your changes:
```bash
node scripts/find-missing-translations.js
```
```bash
node scripts/find-missing-translations.js
```
## Key Guidelines

View file

@ -0,0 +1,17 @@
TOOL EXECUTION RULES (CRITICAL):
Always use Roo tools when interacting with the workspace.
After EVERY tool call, you MUST provide a short confirmation message describing the result before continuing.
Workflow:
1. Call ONE tool at a time.
2. Wait for the result.
3. Briefly summarize the result.
4. Then decide the next action.
Never call multiple tools in one response.
Never end a response immediately after a tool call.
Always include a short natural-language confirmation after tool usage.
Prefer small sequential actions over complex plans.

View file

@ -105,16 +105,16 @@ Verify the resolution and prepare for commit:
## Git Commands Reference
| Command | Purpose |
|---------|---------|
| `gh pr checkout [PR_NUMBER] --force` | Force checkout the PR branch |
| `git fetch origin main` | Get the latest main branch |
| `GIT_EDITOR=true git rebase origin/main` | Rebase current branch onto main (non-interactive) |
| `git blame -L [start],[end] [commit] -- [file]` | Get commit information for specific lines |
| `git show --format="%H%n%an%n%ae%n%ad%n%s%n%b" --no-patch [sha]` | Get commit metadata |
| `git show [sha] -- [file]` | Get the actual changes made in a commit |
| `git ls-files -u` | List unmerged files with stage information |
| `GIT_EDITOR=true git rebase --continue` | Continue rebase after resolving conflicts |
| Command | Purpose |
| ---------------------------------------------------------------- | ------------------------------------------------- |
| `gh pr checkout [PR_NUMBER] --force` | Force checkout the PR branch |
| `git fetch origin main` | Get the latest main branch |
| `GIT_EDITOR=true git rebase origin/main` | Rebase current branch onto main (non-interactive) |
| `git blame -L [start],[end] [commit] -- [file]` | Get commit information for specific lines |
| `git show --format="%H%n%an%n%ae%n%ad%n%s%n%b" --no-patch [sha]` | Get commit metadata |
| `git show [sha] -- [file]` | Get the actual changes made in a commit |
| `git ls-files -u` | List unmerged files with stage information |
| `GIT_EDITOR=true git rebase --continue` | Continue rebase after resolving conflicts |
## Best Practices
@ -141,12 +141,12 @@ Look beyond the immediate conflict to understand related changes in tests, docum
## Resolution Heuristics
| Category | Rule | Exception |
|----------|------|-----------|
| Bugfix vs Feature | Bugfixes generally take precedence | When features include the fix |
| Recent vs Old | More recent changes are often more relevant | When older changes are security patches |
| Test Updates | Changes with test updates are likely more complete | - |
| Formatting vs Logic | Logic changes take precedence over formatting | - |
| Category | Rule | Exception |
| ------------------- | -------------------------------------------------- | --------------------------------------- |
| Bugfix vs Feature | Bugfixes generally take precedence | When features include the fix |
| Recent vs Old | More recent changes are often more relevant | When older changes are security patches |
| Test Updates | Changes with test updates are likely more complete | - |
| Formatting vs Logic | Logic changes take precedence over formatting | - |
## Common Pitfalls

View file

@ -29,10 +29,10 @@ Localize all strings into the following locale files: ca, de, en, es, fr, hi, id
The VSCode extension has two main areas that require localization:
| Component | Path | Purpose |
|-----------|------|---------|
| **Core Extension** | `src/i18n/locales/` | Extension backend strings |
| **WebView UI** | `webview-ui/src/i18n/locales/` | User interface strings |
| Component | Path | Purpose |
| ------------------ | ------------------------------ | ------------------------- |
| **Core Extension** | `src/i18n/locales/` | Extension backend strings |
| **WebView UI** | `webview-ui/src/i18n/locales/` | User interface strings |
## Brand Voice, Tone, and Word Choice
@ -77,17 +77,19 @@ This guidance file is loaded at runtime and should be consulted for the latest b
### Trans Component Example
Translation string:
```json
"changeSettings": "You can always change this at the bottom of the <settingsLink>settings</settingsLink>"
```
React component usage:
```tsx
<Trans
i18nKey="welcome:telemetry.changeSettings"
components={{
settingsLink: <VSCodeLink href="#" onClick={handleOpenSettings} />
}}
i18nKey="welcome:telemetry.changeSettings"
components={{
settingsLink: <VSCodeLink href="#" onClick={handleOpenSettings} />,
}}
/>
```
@ -105,19 +107,21 @@ React component usage:
1. First add or modify English strings, then ask for confirmation before translating to all other languages
2. Use this process for each localization task:
1. Identify where the string appears in the UI/codebase
2. Understand the context and purpose of the string
3. Update English translation first
4. Use the `search_files` tool to find JSON keys that are near new keys in English translations but do not yet exist in the other language files for `apply_diff` SEARCH context
5. Create appropriate translations for all other supported languages utilizing the `search_files` result using `apply_diff` without reading every file
6. Do not output the translated text into the chat, just modify the files
7. Validate your changes with the missing translations script
1. Identify where the string appears in the UI/codebase
2. Understand the context and purpose of the string
3. Update English translation first
4. Use the `search_files` tool to find JSON keys that are near new keys in English translations but do not yet exist in the other language files for `apply_diff` SEARCH context
5. Create appropriate translations for all other supported languages utilizing the `search_files` result using `apply_diff` without reading every file
6. Do not output the translated text into the chat, just modify the files
7. Validate your changes with the missing translations script
3. Flag or comment if an English source string is incomplete ("please see this...") to avoid truncated or unclear translations
4. For UI elements, distinguish between:
- Button labels: Use short imperative commands ("Save", "Cancel")
- Tooltip text: Can be slightly more descriptive
- Button labels: Use short imperative commands ("Save", "Cancel")
- Tooltip text: Can be slightly more descriptive
5. Preserve the original perspective: If text is a user command directed at the software, ensure the translation maintains this direction

View file

@ -146,3 +146,37 @@ customModes:
- command
- mcp
source: project
- slug: deepseek-code
name: DeepSeek Code
roleDefinition: |-
You are Roo, an expert software engineer optimized for tool-driven workflows using DeepSeek models.
You strictly use tools to inspect, analyze, and modify code. You work step-by-step, prioritizing workspace inspection before reasoning. You avoid long explanations and instead take concrete actions using available tools.
You focus on precise coding assistance, repository understanding, debugging, and safe code modifications.
whenToUse: Use this mode when working with DeepSeek models for coding, debugging, repository exploration, and file modifications requiring strict tool usage.
description: DeepSeek-optimized coding assistant
groups:
- read
- edit
- command
- mcp
source: project
customInstructions: |
TOOL EXECUTION RULES (CRITICAL):
Always use Roo tools when interacting with the workspace.
After EVERY tool call, you MUST provide a short confirmation message describing the result before continuing.
Workflow:
1. Call ONE tool at a time.
2. Wait for the result.
3. Briefly summarize the result.
4. Then decide the next action.
Never call multiple tools in one response.
Never end a response immediately after a tool call.
Always include a short natural-language confirmation after tool usage.
Prefer small sequential actions over complex plans.

76
ARCHITECTURE_NOTES.md Normal file
View file

@ -0,0 +1,76 @@
# ARCHITECTURE REPORT: The Governed AI-Native IDE
**Role:** Forward Deployed Engineer (FDE)
**Framework:** TRACE (Trusted Runtime for Autonomous Containment and Evidence)
---
## Executive Summary
This report formalizes the architecture for a governed AI-Native IDE, engineered to bridge the Provenance Gap between probabilistic reasoning and deterministic software engineering (Entire, 2026). By embedding infrastructure-level mediation into the IDE tool loop, we replace "vibe coding" with cryptographic verification and structured intent (TRACE, 2026; Bora, 2024).
---
## 1. How the VS Code Extension Works: Dual-Process Topology
To ensure security and performance, the extension operates as a distributed system within VS Code (VS Code, 2026):
### Extension Host (Logic Layer)
A Node.js process where the core orchestrator (`src/core/Cline.ts`) manages the agentic loop. It has full privileges for filesystem access and terminal execution (VS Code, 2026).
### Webview (UI Layer)
A restricted React sandbox for the user interface. It communicates with the Host via the `postMessage` API, ensuring reasoning-heavy tasks do not block the UI thread (Ansible, 2026).
> My architecture intercepts the Thinking-Acting-Observing loop at the Extension Host level, mediating every system-level tool call (e.g., `write_to_file`) before execution (Roo Code, 2026).
---
## 2. Code & Design Architecture: The Shared Brain
Based on my Phase 0 archaeological dig into the Roo Code "nervous system," I have implemented two critical intervention points (Bockeler, 2025):
- **The Prompt Factory (`src/core/prompts/system.ts`)**
Modified the prompt builder to move from "Instruction-based" to "Context-based" engineering. The agent is now architecturally barred from code synthesis until it completes a mandatory "Reasoning Handshake" (Bora, 2024; Bockeler, 2025).
- **The Shared Brain (`AGENT.md`)**
Acts as a persistent project "Constitution." It records lessons learned from failed verification loops, preventing Context Rot during parallel agent sessions (LF Projects, 2025).
---
## 3. Architectural Decisions: The TRACE Hook Engine
I have implemented the TRACE framework to derive assurance from infrastructure mediation rather than model behavior (TRACE, 2026).
- **Command Classification:** Every tool is classified as Safe (Read/Search) or Destructive (Write/Execute) (Roo Code, 2026).
- **The Handshake (Pre-Hook):** Mutation is prohibited until the agent calls `select_active_intent(intent_id)`. This pauses the promise chain and injects `What-Boundaries-Success` (WBS) constraints from `.orchestration/active_intents.yaml` into the current context (Bora, 2024).
- **Autonomous Recovery:** If the engine blocks an action, it returns a standardized JSON error to the LLM, allowing the "silicon worker" to self-correct without human intervention (TRACE, 2026).
---
## 4. Diagrams & Schemas
### Advanced Intent-Code Traceability Flow
I integrated Nancy Levesons Seven Levels of Intent Specification to provide a vertical trace from Level 1 (System Purpose) to Level 5 (Physical Code) (Navarro et al., 2001).
### Code Snippet (Sequence Diagram)
```mermaid
sequenceDiagram
participant LLM as AI Agent (Builder)
participant Hook as TRACE Hook Engine
participant AST as Tree-sitter Parser
participant FS as .orchestration/ (Ledger)
LLM->>Hook: select_active_intent(INT-001)
Note right of Hook: Loads Level 3 WBS Constraints
Hook-->>LLM: Injected XML Constraints
LLM->>Hook: write_file(src/auth.ts, content)
Hook->>AST: Parse Block to AST Node
AST-->>Hook: Structural Identity
Hook->>Hook: Calculate H = SHA-256(Normalize(Block))
Hook->>FS: Append Trace to agent_trace.jsonl
Hook-->>LLM: Success (Level 6 Operations Data)
```

5
CLAUDE.md Normal file
View file

@ -0,0 +1,5 @@

## [PHASE 4] Lesson Learned: Concurrency & Stale States
- **Incident:** STALE_FILE_CONFLICT detected.
- **Verification:** System successfully blocked a write where Disk Hash != Memory Hash.
- **Protocol Update:** Agents must perform a 'fresh read' before any destructive tool call to ensure the Semantic Ledger remains accurate.

View file

@ -1,159 +1,152 @@
const path = require('path');
const fs = require('fs');
const matter = require('gray-matter');
const path = require("path")
const fs = require("fs")
const matter = require("gray-matter")
/**
* Get published blog posts for sitemap
* Note: This runs at build time, so recently-scheduled posts may lag
*/
function getPublishedBlogPosts() {
const BLOG_DIR = path.join(process.cwd(), 'src/content/blog');
if (!fs.existsSync(BLOG_DIR)) {
return [];
}
const files = fs.readdirSync(BLOG_DIR).filter(f => f.endsWith('.md'));
const posts = [];
// Get current time in PT for publish check
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/Los_Angeles',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
});
const parts = formatter.formatToParts(new Date());
const get = (type) => parts.find(p => p.type === type)?.value ?? '';
const nowDate = `${get('year')}-${get('month')}-${get('day')}`;
const nowMinutes = parseInt(get('hour')) * 60 + parseInt(get('minute'));
for (const file of files) {
const filepath = path.join(BLOG_DIR, file);
const raw = fs.readFileSync(filepath, 'utf8');
const { data } = matter(raw);
// Check if post is published
if (data.status !== 'published') continue;
// Parse publish time
const timeMatch = data.publish_time_pt?.match(/^(1[0-2]|[1-9]):([0-5][0-9])(am|pm)$/i);
if (!timeMatch) continue;
let hours = parseInt(timeMatch[1]);
const mins = parseInt(timeMatch[2]);
const isPm = timeMatch[3].toLowerCase() === 'pm';
if (hours === 12) hours = isPm ? 12 : 0;
else if (isPm) hours += 12;
const postMinutes = hours * 60 + mins;
// Check if post is past publish date/time
const isPublished = nowDate > data.publish_date ||
(nowDate === data.publish_date && nowMinutes >= postMinutes);
if (isPublished && data.slug) {
posts.push(data.slug);
}
}
return posts;
const BLOG_DIR = path.join(process.cwd(), "src/content/blog")
if (!fs.existsSync(BLOG_DIR)) {
return []
}
const files = fs.readdirSync(BLOG_DIR).filter((f) => f.endsWith(".md"))
const posts = []
// Get current time in PT for publish check
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone: "America/Los_Angeles",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
})
const parts = formatter.formatToParts(new Date())
const get = (type) => parts.find((p) => p.type === type)?.value ?? ""
const nowDate = `${get("year")}-${get("month")}-${get("day")}`
const nowMinutes = parseInt(get("hour")) * 60 + parseInt(get("minute"))
for (const file of files) {
const filepath = path.join(BLOG_DIR, file)
const raw = fs.readFileSync(filepath, "utf8")
const { data } = matter(raw)
// Check if post is published
if (data.status !== "published") continue
// Parse publish time
const timeMatch = data.publish_time_pt?.match(/^(1[0-2]|[1-9]):([0-5][0-9])(am|pm)$/i)
if (!timeMatch) continue
let hours = parseInt(timeMatch[1])
const mins = parseInt(timeMatch[2])
const isPm = timeMatch[3].toLowerCase() === "pm"
if (hours === 12) hours = isPm ? 12 : 0
else if (isPm) hours += 12
const postMinutes = hours * 60 + mins
// Check if post is past publish date/time
const isPublished = nowDate > data.publish_date || (nowDate === data.publish_date && nowMinutes >= postMinutes)
if (isPublished && data.slug) {
posts.push(data.slug)
}
}
return posts
}
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: process.env.NEXT_PUBLIC_SITE_URL || 'https://roocode.com',
generateRobotsTxt: true,
generateIndexSitemap: false, // We don't need index sitemap for a small site
changefreq: 'monthly',
priority: 0.7,
sitemapSize: 5000,
exclude: [
'/api/*',
'/server-sitemap-index.xml',
'/404',
'/500',
'/_not-found',
],
robotsTxtOptions: {
policies: [
{
userAgent: '*',
allow: '/',
},
],
additionalSitemaps: [
// Add any additional sitemaps here if needed in the future
],
},
// Custom transform function to set specific priorities and change frequencies
transform: async (config, path) => {
// Set custom priority for specific pages
let priority = config.priority;
let changefreq = config.changefreq;
if (path === '/') {
priority = 1.0;
changefreq = 'yearly';
} else if (path === '/enterprise' || path === '/evals') {
priority = 0.8;
changefreq = 'monthly';
} else if (path === '/privacy' || path === '/terms') {
priority = 0.5;
changefreq = 'yearly';
} else if (path === '/blog') {
priority = 0.8;
changefreq = 'weekly';
} else if (path.startsWith('/blog/')) {
priority = 0.7;
changefreq = 'monthly';
}
return {
loc: path,
changefreq,
priority,
lastmod: config.autoLastmod ? new Date().toISOString() : undefined,
alternateRefs: config.alternateRefs ?? [],
};
},
additionalPaths: async (config) => {
const result = [];
// Add the /evals page since it's a dynamic route
result.push({
loc: '/evals',
changefreq: 'monthly',
priority: 0.8,
lastmod: new Date().toISOString(),
});
// Add /blog index
result.push({
loc: '/blog',
changefreq: 'weekly',
priority: 0.8,
lastmod: new Date().toISOString(),
});
// Add published blog posts
try {
const slugs = getPublishedBlogPosts();
for (const slug of slugs) {
result.push({
loc: `/blog/${slug}`,
changefreq: 'monthly',
priority: 0.7,
lastmod: new Date().toISOString(),
});
}
} catch (e) {
console.warn('Could not load blog posts for sitemap:', e.message);
}
return result;
},
};
siteUrl: process.env.NEXT_PUBLIC_SITE_URL || "https://roocode.com",
generateRobotsTxt: true,
generateIndexSitemap: false, // We don't need index sitemap for a small site
changefreq: "monthly",
priority: 0.7,
sitemapSize: 5000,
exclude: ["/api/*", "/server-sitemap-index.xml", "/404", "/500", "/_not-found"],
robotsTxtOptions: {
policies: [
{
userAgent: "*",
allow: "/",
},
],
additionalSitemaps: [
// Add any additional sitemaps here if needed in the future
],
},
// Custom transform function to set specific priorities and change frequencies
transform: async (config, path) => {
// Set custom priority for specific pages
let priority = config.priority
let changefreq = config.changefreq
if (path === "/") {
priority = 1.0
changefreq = "yearly"
} else if (path === "/enterprise" || path === "/evals") {
priority = 0.8
changefreq = "monthly"
} else if (path === "/privacy" || path === "/terms") {
priority = 0.5
changefreq = "yearly"
} else if (path === "/blog") {
priority = 0.8
changefreq = "weekly"
} else if (path.startsWith("/blog/")) {
priority = 0.7
changefreq = "monthly"
}
return {
loc: path,
changefreq,
priority,
lastmod: config.autoLastmod ? new Date().toISOString() : undefined,
alternateRefs: config.alternateRefs ?? [],
}
},
additionalPaths: async (config) => {
const result = []
// Add the /evals page since it's a dynamic route
result.push({
loc: "/evals",
changefreq: "monthly",
priority: 0.8,
lastmod: new Date().toISOString(),
})
// Add /blog index
result.push({
loc: "/blog",
changefreq: "weekly",
priority: 0.8,
lastmod: new Date().toISOString(),
})
// Add published blog posts
try {
const slugs = getPublishedBlogPosts()
for (const slug of slugs) {
result.push({
loc: `/blog/${slug}`,
changefreq: "monthly",
priority: 0.7,
lastmod: new Date().toISOString(),
})
}
} catch (e) {
console.warn("Could not load blog posts for sitemap:", e.message)
}
return result
},
}

View file

@ -1,6 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View file

@ -37,8 +37,8 @@ export default async function ExtensionPage() {
<p className="mt-4 max-w-lg text-lg text-muted-foreground text-center md:text-left sm:mt-6">
Specialized modes stay on task and ship great code.
<br />
Fully model-agnostic so you can use the best (or most cost-effective) model for
each task.
Fully model-agnostic so you can use the best (or most cost-effective) model for each
task.
</p>
<p className="max-w-lg text-lg text-muted-foreground text-center md:text-left sm:mt-6">
Stop chasing this week&apos;s hot new model or CLI tool and go deep with Roo Code.

View file

@ -291,11 +291,7 @@ export default function PricingPage() {
<li>To pay for Cloud Agents running time (${PRICE_CREDITS}/hour)</li>
<li>
To pay for AI model inference costs (
<a
href="/provider"
target="_blank"
rel="noopener noreferrer"
className="underline">
<a href="/provider" target="_blank" rel="noopener noreferrer" className="underline">
varies by model
</a>
)

View file

@ -1,22 +1,21 @@
version: 1.3
pr_review:
# Modify confidence_threshold to show fewer/more comments. Increase this to show fewer, but higher quality comments.
# If theres too much noise, we suggest 0.9. The default value is 0.7.
confidence_threshold: 0.7
# If quiet mode is enabled, Ellipsis will only leave reviews when it has comments, so “Looks good to me” reviews
# will be skipped. This can reduce clutter.
quiet: true
# You can disable automatic code review using auto_review_enabled. This will override any global settings you
# have configured via the web UI.
auto_review_enabled: true
# Modify confidence_threshold to show fewer/more comments. Increase this to show fewer, but higher quality comments.
# If theres too much noise, we suggest 0.9. The default value is 0.7.
confidence_threshold: 0.7
# You can enable auto-review on draft PRs using auto_review_draft. This will override any global settings you
# have configured via the web UI.
auto_review_draft: false
# You can allow Ellipsis to approve PRs using enable_approve_prs. Note: in common branch GitHub protection configurations,
# the Ellipsis approval will count towards the approval total and allow the PR to be merged when it otherwise may not be.
enable_approve_prs: false
# If quiet mode is enabled, Ellipsis will only leave reviews when it has comments, so “Looks good to me” reviews
# will be skipped. This can reduce clutter.
quiet: true
# You can disable automatic code review using auto_review_enabled. This will override any global settings you
# have configured via the web UI.
auto_review_enabled: true
# You can enable auto-review on draft PRs using auto_review_draft. This will override any global settings you
# have configured via the web UI.
auto_review_draft: false
# You can allow Ellipsis to approve PRs using enable_approve_prs. Note: in common branch GitHub protection configurations,
# the Ellipsis approval will count towards the approval total and allow the PR to be merged when it otherwise may not be.
enable_approve_prs: false

View file

@ -1,74 +1,140 @@
{
"name": "roo-code",
"packageManager": "pnpm@10.8.1",
"engines": {
"node": "20.19.2"
},
"scripts": {
"preinstall": "node scripts/bootstrap.mjs",
"prepare": "husky",
"install": "node scripts/bootstrap.mjs",
"install:all": "node scripts/bootstrap.mjs",
"lint": "turbo lint --log-order grouped --output-logs new-only",
"check-types": "turbo check-types --log-order grouped --output-logs new-only",
"test": "turbo test --log-order grouped --output-logs new-only",
"format": "turbo format --log-order grouped --output-logs new-only",
"build": "turbo build --log-order grouped --output-logs new-only",
"bundle": "turbo bundle --log-order grouped --output-logs new-only",
"bundle:nightly": "turbo bundle:nightly --log-order grouped --output-logs new-only",
"vsix": "turbo vsix --log-order grouped --output-logs new-only",
"vsix:nightly": "turbo vsix:nightly --log-order grouped --output-logs new-only",
"clean": "turbo clean --log-order grouped --output-logs new-only && rimraf dist out bin .vite-port .turbo",
"install:vsix": "pnpm install --frozen-lockfile && pnpm clean && pnpm vsix && node scripts/install-vsix.js",
"install:vsix:nightly": "pnpm install --frozen-lockfile && pnpm clean && pnpm vsix:nightly && node scripts/install-vsix.js --nightly",
"code-server:install": "node scripts/code-server.js",
"changeset:version": "cp CHANGELOG.md src/CHANGELOG.md && changeset version && cp -vf src/CHANGELOG.md .",
"knip": "knip --include files",
"evals": "dotenvx run -f packages/evals/.env.development packages/evals/.env.local -- docker compose -f packages/evals/docker-compose.yml --profile server --profile runner up --build --scale runner=0",
"npm:publish:types": "pnpm --filter @roo-code/types npm:publish"
},
"devDependencies": {
"@changesets/cli": "^2.27.10",
"@dotenvx/dotenvx": "^1.34.0",
"@roo-code/config-typescript": "workspace:^",
"@types/glob": "^9.0.0",
"@types/node": "^24.1.0",
"@vscode/vsce": "3.3.2",
"esbuild": "^0.25.0",
"eslint": "^9.27.0",
"glob": "^11.1.0",
"husky": "^9.1.7",
"knip": "^5.44.4",
"lint-staged": "^16.0.0",
"mkdirp": "^3.0.1",
"only-allow": "^1.2.1",
"ovsx": "0.10.4",
"prettier": "^3.4.2",
"rimraf": "^6.0.1",
"tsx": "^4.19.3",
"turbo": "^2.5.6",
"typescript": "5.8.3"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css,md}": [
"prettier --write"
]
},
"pnpm": {
"onlyBuiltDependencies": [
"@vscode/ripgrep"
],
"overrides": {
"tar-fs": ">=3.1.1",
"esbuild": ">=0.25.0",
"undici": ">=5.29.0",
"brace-expansion": "^2.0.2",
"form-data": ">=4.0.4",
"bluebird": ">=3.7.2",
"glob": ">=11.1.0",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.5",
"zod": "3.25.76"
}
}
}
"name": "roo-code",
"displayName": "Roo Code",
"description": "An AI-native IDE extension that can use your CLI and editor",
"version": "3.3.2",
"icon": "assets/icons/robot.svg",
"galleryBanner": {
"color": "#1e1e1e",
"theme": "dark"
},
"publisher": "RooCode",
"repository": {
"type": "git",
"url": "https://github.com/Roo-Code/Roo-Code"
},
"categories": [
"AI",
"Programming Languages",
"Education",
"Assistant"
],
"keywords": [
"ai",
"llm",
"agentic",
"coding assistant",
"anthropic",
"claude",
"openai",
"gemini",
"ollama"
],
"activationEvents": [
"onView:roo-code.SidebarProvider"
],
"main": "./dist/extension.js",
"browser": "./dist/extension.js",
"bin": {
"roo": "./bin/roo.js"
},
"engines": {
"vscode": "^1.84.0"
},
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "roo-code-SidebarContainer",
"icon": "assets/icons/robot.svg",
"title": "Roo Code"
}
]
},
"views": {
"roo-code-SidebarContainer": [
{
"type": "webview",
"id": "roo-code.SidebarProvider",
"name": ""
}
]
},
"commands": [
{
"command": "roo-code.plusButtonClicked",
"title": "New Task",
"icon": "$(add)"
},
{
"command": "roo-code.mcpButtonClicked",
"title": "MCP Servers",
"icon": "$(server-process)"
},
{
"command": "roo-code.historyButtonClicked",
"title": "History",
"icon": "$(history)"
},
{
"command": "roo-code.settingsButtonClicked",
"title": "Settings",
"icon": "$(settings-gear)"
},
{
"command": "roo-code.openInNewTab",
"title": "Open In New Tab"
}
],
"menus": {
"view/title": [
{
"command": "roo-code.plusButtonClicked",
"group": "navigation@1",
"when": "view == roo-code.SidebarProvider"
},
{
"command": "roo-code.mcpButtonClicked",
"group": "navigation@2",
"when": "view == roo-code.SidebarProvider"
},
{
"command": "roo-code.historyButtonClicked",
"group": "navigation@3",
"when": "view == roo-code.SidebarProvider"
},
{
"command": "roo-code.settingsButtonClicked",
"group": "navigation@4",
"when": "view == roo-code.SidebarProvider"
}
],
"commandPalette": [
{
"command": "roo-code.openInNewTab",
"when": "true"
}
]
},
"configuration": {
"title": "Roo Code",
"properties": {
"roo-code.instructions": {
"type": "string",
"default": "",
"description": "Custom instructions to add to the end of every system prompt.",
"editPresentation": "multilineText"
}
}
}
},
"scripts": {
"vscode:prepublish": "npm run package",
"compile": "node esbuild.js",
"bundle": "node esbuild.js",
"package": "node esbuild.js --production",
"watch": "node esbuild.js --watch",
"check-types": "tsc --noEmit",
"lint": "eslint src --ext ts"
}
}

10
pnpm-lock.yaml generated
View file

@ -32,6 +32,9 @@ importers:
'@types/glob':
specifier: ^9.0.0
version: 9.0.0
'@types/js-yaml':
specifier: ^4.0.9
version: 4.0.9
'@types/node':
specifier: ^24.1.0
version: 24.2.1
@ -4552,6 +4555,9 @@ packages:
'@types/js-cookie@2.2.7':
resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==}
'@types/js-yaml@4.0.9':
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@ -14763,6 +14769,8 @@ snapshots:
'@types/js-cookie@2.2.7': {}
'@types/js-yaml@4.0.9': {}
'@types/json-schema@7.0.15': {}
'@types/katex@0.16.7': {}
@ -15099,7 +15107,7 @@ snapshots:
sirv: 3.0.1
tinyglobby: 0.2.14
tinyrainbow: 2.0.0
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.2.1)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.17.57)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0)
'@vitest/utils@3.2.4':
dependencies:

View file

@ -0,0 +1,30 @@
import * as fs from 'fs';
import * as yaml from 'js-yaml'; // You may need to npm install js-yaml
import * as path from 'path';
export async function selectActiveIntent(intentId: string, workspaceRoot: string) {
const configPath = path.join(workspaceRoot, '.orchestration', 'active_intents.yaml');
// 1. Read the Intent File
const fileContents = fs.readFileSync(configPath, 'utf8');
const data = yaml.load(fileContents) as any;
// 2. Find the specific Intent
const intent = data.active_intents.find((i: any) => i.id === intentId);
if (!intent) {
throw new Error(`Intent ID ${intentId} not found in .orchestration/active_intents.yaml`);
}
// 3. Return the XML Context (This is what the AI sees)
return `
<intent_context>
<id>${intent.id}</id>
<name>${intent.name}</name>
<owned_scope>${intent.owned_scope.join(', ')}</owned_scope>
<constraints>
${intent.constraints.map((c: string) => `- ${c}`).join('\n ')}
</constraints>
</intent_context>
`;
}

38
src/hooks/HookEngine.ts Normal file
View file

@ -0,0 +1,38 @@
import { IntentManager } from "./IntentManager"
import { TraceLogger } from "./TraceLogger"
export class HookEngine {
private intentManager = new IntentManager()
private traceLogger = new TraceLogger()
/**
* Intercepts the Thinking-Acting loop at the Extension Host level (VS Code, 2026).
*/
async preToolUse(toolName: string, args: any): Promise<string | void> {
// Classification: Mediation depends on the risk level of the tool (TRACE, 2026)
const isDestructive = ["write_to_file", "execute_command", "apply_diff"].includes(toolName)
if (toolName === "select_active_intent") {
return await this.intentManager.loadIntentContext(args.intent_id)
}
// Enforcement: Fail-Closed halting if no intent is active (TRACE, 2026)
if (isDestructive && !this.intentManager.getActiveIntent()) {
throw new Error(
`Governance Violation: Mutation tool '${toolName}' blocked. You must call 'select_active_intent' first.`,
)
}
if (toolName === "write_to_file") {
this.intentManager.verifyScope(args.path)
}
}
async postToolUse(toolName: string, args: any, result: string): Promise<void> {
const activeIntentId = this.intentManager.getActiveIntent()
if (toolName === "write_to_file" && activeIntentId) {
await this.traceLogger.logMutation(activeIntentId, args.path, args.content)
}
}
}

View file

@ -0,0 +1,75 @@
import * as fs from "fs/promises"
import * as path from "path"
import * as yaml from "js-yaml"
import { WBSContext } from "./types"
export class IntentManager {
private activeIntent: WBSContext | null = null
private orchestrationPath = path.join(process.cwd(), ".orchestration", "active_intents.yaml")
async loadIntentContext(intentId: string): Promise<string> {
try {
const fileContent = await fs.readFile(this.orchestrationPath, "utf8")
const data: any = yaml.load(fileContent)
if (!data?.active_intents) {
throw new Error("No active_intents section found in YAML")
}
const intent = data.active_intents.find((i: any) => i.id === intentId)
if (!intent) {
throw new Error(`Intent ID ${intentId} not found in .orchestration/active_intents.yaml`)
}
// Ensure all arrays are properly typed
const what: string[] = Array.isArray(intent.what) ? intent.what : [String(intent.what)]
const constraints: string[] = Array.isArray(intent.constraints)
? intent.constraints
: [String(intent.constraints)]
const acceptance_criteria: string[] = Array.isArray(intent.acceptance_criteria)
? intent.acceptance_criteria
: [String(intent.acceptance_criteria)]
const owned_scope: string[] = Array.isArray(intent.owned_scope)
? intent.owned_scope
: [String(intent.owned_scope)]
// Map to full WBSContext type
this.activeIntent = {
id: intent.id,
what,
constraints,
acceptance_criteria,
owned_scope,
boundaries: constraints,
success: acceptance_criteria,
} as WBSContext
return `<intent_context>
ID: ${intent.id}
WHAT: ${what.join(", ")}
BOUNDARIES: ${constraints.join(", ")}
SUCCESS: ${acceptance_criteria.join(", ")}
OWNED_SCOPE: ${owned_scope.join(", ")}
</intent_context>`
} catch (error: any) {
throw new Error(`Intent Handshake Failed: ${error.message}`)
}
}
getActiveIntent(): string | null {
return this.activeIntent?.id || null
}
verifyScope(targetPath: string): void {
if (!this.activeIntent) return
const ownedScope: string[] = this.activeIntent.owned_scope ?? []
const isAuthorized = ownedScope.some((pattern: string) => targetPath.includes(pattern.replace("/**", "")))
if (!isAuthorized) {
throw new Error(`Scope Violation: Intent ${this.activeIntent.id} is not authorized to edit ${targetPath}`)
}
}
}

47
src/hooks/TraceLogger.ts Normal file
View file

@ -0,0 +1,47 @@
import * as crypto from "crypto"
import * as fs from "fs/promises"
import * as path from "path"
import { TraceRecord } from "./types"
export class TraceLogger {
private ledgerPath = path.join(process.cwd(), ".orchestration", "agent_trace.jsonl")
/**
* Calculates the content hash H = SHA-256(Normalize(Block))
* to provide Structural Identity (Dropstone, 2025).
*/
private calculateSpatialHash(content: string): string {
const normalized = content.replace(/\s+/g, " ").trim() // Normalize whitespace
return crypto.createHash("sha256").update(normalized).digest("hex")
}
async logMutation(intentId: string, filePath: string, content: string): Promise<void> {
const hash = this.calculateSpatialHash(content)
const record: TraceRecord = {
version: "1.0",
id: crypto.randomUUID(),
timestamp: new Date().toISOString(),
files: [
{
path: filePath,
conversations: [
{
contributor: { type: "ai" },
ranges: [
{
start_line: 1, // Full file write context
end_line: content.split("\n").length,
content_hash: `sha256:${hash}`,
},
],
},
],
},
],
}
// Append to the hash-chained evidence log (TRACE, 2026)
await fs.appendFile(this.ledgerPath, JSON.stringify(record) + "\n")
}
}

48
src/hooks/types.ts Normal file
View file

@ -0,0 +1,48 @@
/**
* MutationClass differentiates between changes that preserve existing
* business logic (Refactor) and those that introduce new requirements (Evolution).
*/
export enum MutationClass {
AST_REFACTOR = "AST_REFACTOR",
INTENT_EVOLUTION = "INTENT_EVOLUTION",
}
/**
* WBSContext formalizes the "What-Boundaries-Success" framework
* to reduce the LLM's solution space through structured constraints.
*/
export interface WBSContext {
id: string // e.g., "INT-001"
owned_scope: string[] // Glob patterns of authorized files
what: string[] // Functional requirements
boundaries: string[] // "Soft constraints" to prevent hallucinations
success: string[] // Measurable criteria for "Definition of Done"
}
/**
* TraceRecord follows the Agent Trace specification to provide
* a position-independent audit log of AI contributions.
*/
export interface TraceRecord {
version: string
id: string
timestamp: string
vcs?: {
type: "git" | "jj" | "hg" | "svn"
revision: string
}
files: Array<{
path: string
conversations: Array<{
contributor: {
type: "human" | "ai" | "mixed" | "unknown"
model_id?: string
}
ranges: Array<{
start_line: number
end_line: number
content_hash?: string
}>
}>
}>
}

46
webview-ui/.gitignore vendored
View file

@ -1,27 +1,37 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
# --------------------------------------------------
# Dependencies
# --------------------------------------------------
node_modules/
.pnp/
.pnp.js
# testing
/coverage
# pnpm
pnpm-lock.yaml
# production
/build
# --------------------------------------------------
# Build Outputs
# --------------------------------------------------
dist/
build/
out/
.next/
coverage/
*.tsbuildinfo
# misc
.DS_Store
# Webview / Vite
webview-ui/dist/
webview-ui/.vite/
# --------------------------------------------------
# Environment Files
# --------------------------------------------------
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*storybook.log
tsconfig.tsbuildinfo
# --------------------------------------------------
# Logs
# --------------------------------------------------
npm-debug.log