feat: Add GitHub Actions bot integration

- Created GitHubActionsService for managing GitHub Actions workflows
- Added GitHub Actions button to UI toolbar
- Created GitHubActionsView component for workflow management
- Added command registration for GitHub Actions bot
- Added configuration settings for GitHub Actions
- Added localization strings for GitHub Actions feature
- Integrated GitHub Actions view into the main app navigation

This feature allows users to:
- Install GitHub Actions workflows for automated issue and PR handling
- Enable/disable the GitHub Actions bot
- Configure workflow templates
- Set up GitHub repository secrets for API integration
This commit is contained in:
Roo Code 2025-09-21 04:29:12 +00:00
parent ceb9d2b9f2
commit 322a2230f1
9 changed files with 718 additions and 4 deletions

View file

@ -34,6 +34,7 @@ export const commandIds = [
"mcpButtonClicked",
"historyButtonClicked",
"marketplaceButtonClicked",
"githubActionsButtonClicked",
"popoutButtonClicked",
"cloudButtonClicked",
"settingsButtonClicked",

View file

@ -158,6 +158,14 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
if (!visibleProvider) return
visibleProvider.postMessageToWebview({ type: "action", action: "marketplaceButtonClicked" })
},
githubActionsButtonClicked: () => {
const visibleProvider = getVisibleProviderOrLog(outputChannel)
if (!visibleProvider) return
TelemetryService.instance.captureTitleButtonClicked("githubActions")
visibleProvider.postMessageToWebview({ type: "action", action: "githubActionsButtonClicked" })
},
showHumanRelayDialog: (params: { requestId: string; promptText: string }) => {
const panel = getPanel()

View file

@ -95,6 +95,11 @@
"title": "%command.marketplace.title%",
"icon": "$(extensions)"
},
{
"command": "roo-cline.githubActionsButtonClicked",
"title": "%command.githubActions.title%",
"icon": "$(github-action)"
},
{
"command": "roo-cline.popoutButtonClicked",
"title": "%command.openInEditor.title%",
@ -254,9 +259,14 @@
"when": "view == roo-cline.SidebarProvider"
},
{
"command": "roo-cline.popoutButtonClicked",
"command": "roo-cline.githubActionsButtonClicked",
"group": "overflow@4",
"when": "view == roo-cline.SidebarProvider"
},
{
"command": "roo-cline.popoutButtonClicked",
"group": "overflow@5",
"when": "view == roo-cline.SidebarProvider"
}
],
"editor/title": [
@ -296,9 +306,14 @@
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
},
{
"command": "roo-cline.popoutButtonClicked",
"command": "roo-cline.githubActionsButtonClicked",
"group": "overflow@4",
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
},
{
"command": "roo-cline.popoutButtonClicked",
"group": "overflow@5",
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
}
]
},
@ -417,6 +432,31 @@
"minimum": 1,
"maximum": 200,
"description": "%settings.codeIndex.embeddingBatchSize.description%"
},
"rooCode.githubActions.enabled": {
"type": "boolean",
"default": false,
"description": "%settings.githubActions.enabled.description%"
},
"rooCode.githubActions.workflowsPath": {
"type": "string",
"default": ".github/workflows",
"description": "%settings.githubActions.workflowsPath.description%"
},
"rooCode.githubActions.autoInstall": {
"type": "boolean",
"default": false,
"description": "%settings.githubActions.autoInstall.description%"
},
"rooCode.githubActions.defaultBranch": {
"type": "string",
"default": "main",
"description": "%settings.githubActions.defaultBranch.description%"
},
"rooCode.githubActions.botToken": {
"type": "string",
"default": "",
"description": "%settings.githubActions.botToken.description%"
}
}
}

View file

@ -10,6 +10,7 @@
"command.prompts.title": "Modes",
"command.history.title": "History",
"command.marketplace.title": "Marketplace",
"command.githubActions.title": "GitHub Actions",
"command.openInEditor.title": "Open in Editor",
"command.cloud.title": "Cloud",
"command.settings.title": "Settings",
@ -41,5 +42,10 @@
"settings.useAgentRules.description": "Enable loading of AGENTS.md files for agent-specific rules (see https://agent-rules.org/)",
"settings.apiRequestTimeout.description": "Maximum time in seconds to wait for API responses (0 = no timeout, 1-3600s, default: 600s). Higher values are recommended for local providers like LM Studio and Ollama that may need more processing time.",
"settings.newTaskRequireTodos.description": "Require todos parameter when creating new tasks with the new_task tool",
"settings.codeIndex.embeddingBatchSize.description": "The batch size for embedding operations during code indexing. Adjust this based on your API provider's limits. Default is 60."
"settings.codeIndex.embeddingBatchSize.description": "The batch size for embedding operations during code indexing. Adjust this based on your API provider's limits. Default is 60.",
"settings.githubActions.enabled.description": "Enable GitHub Actions bot integration",
"settings.githubActions.workflowsPath.description": "Path to GitHub Actions workflows directory (relative to workspace root)",
"settings.githubActions.autoInstall.description": "Automatically install GitHub Actions workflows when opening a repository",
"settings.githubActions.defaultBranch.description": "Default branch name for GitHub Actions workflows",
"settings.githubActions.botToken.description": "GitHub token for the Actions bot (optional, for advanced features)"
}

View file

@ -0,0 +1,394 @@
import * as vscode from "vscode"
import * as path from "path"
import * as fs from "fs/promises"
import { safeWriteJson } from "../../utils/safeWriteJson"
export interface GitHubActionsConfig {
enabled: boolean
workflowsPath: string
autoInstall: boolean
defaultBranch: string
botToken?: string
}
export interface WorkflowTemplate {
name: string
description: string
fileName: string
content: string
}
export class GitHubActionsService {
private static instance: GitHubActionsService | undefined
private config: GitHubActionsConfig
private outputChannel: vscode.OutputChannel
private workspaceRoot: string | undefined
private constructor(outputChannel: vscode.OutputChannel, workspaceRoot: string | undefined) {
this.outputChannel = outputChannel
this.workspaceRoot = workspaceRoot
this.config = this.loadConfig()
}
public static getInstance(outputChannel?: vscode.OutputChannel, workspaceRoot?: string): GitHubActionsService {
if (!GitHubActionsService.instance) {
if (!outputChannel) {
throw new Error("OutputChannel is required for first initialization")
}
GitHubActionsService.instance = new GitHubActionsService(outputChannel, workspaceRoot)
}
return GitHubActionsService.instance
}
private loadConfig(): GitHubActionsConfig {
const config = vscode.workspace.getConfiguration("rooCode.githubActions")
return {
enabled: config.get<boolean>("enabled", false),
workflowsPath: config.get<string>("workflowsPath", ".github/workflows"),
autoInstall: config.get<boolean>("autoInstall", false),
defaultBranch: config.get<string>("defaultBranch", "main"),
botToken: config.get<string>("botToken"),
}
}
public async updateConfig(newConfig: Partial<GitHubActionsConfig>): Promise<void> {
this.config = { ...this.config, ...newConfig }
const config = vscode.workspace.getConfiguration("rooCode.githubActions")
for (const [key, value] of Object.entries(newConfig)) {
await config.update(key, value, vscode.ConfigurationTarget.Global)
}
}
public getConfig(): GitHubActionsConfig {
return { ...this.config }
}
public isEnabled(): boolean {
return this.config.enabled
}
public async enable(): Promise<void> {
await this.updateConfig({ enabled: true })
this.outputChannel.appendLine("GitHub Actions bot enabled")
}
public async disable(): Promise<void> {
await this.updateConfig({ enabled: false })
this.outputChannel.appendLine("GitHub Actions bot disabled")
}
private getWorkflowTemplates(): WorkflowTemplate[] {
return [
{
name: "Roo Code Issue Handler",
description: "Automatically handle GitHub issues with Roo Code",
fileName: "roo-code-issues.yml",
content: `name: Roo Code Issue Handler
on:
issues:
types: [opened, edited]
issue_comment:
types: [created]
jobs:
handle-issue:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Process issue with Roo Code
env:
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
ROO_CODE_API_KEY: \${{ secrets.ROO_CODE_API_KEY }}
run: |
# This is a placeholder for the actual Roo Code CLI tool
# that would process the issue
echo "Processing issue #\${{ github.event.issue.number }}"
echo "Title: \${{ github.event.issue.title }}"
echo "Body: \${{ github.event.issue.body }}"
- name: Create pull request if needed
if: success()
uses: peter-evans/create-pull-request@v5
with:
token: \${{ secrets.GITHUB_TOKEN }}
commit-message: "fix: Automated fix for issue #\${{ github.event.issue.number }}"
title: "Fix for issue #\${{ github.event.issue.number }}"
body: |
This PR was automatically created by Roo Code to address issue #\${{ github.event.issue.number }}.
## Changes
- Automated fix implementation
Closes #\${{ github.event.issue.number }}
branch: roo-code/issue-\${{ github.event.issue.number }}
`,
},
{
name: "Roo Code PR Review",
description: "Automatically review pull requests with Roo Code",
fileName: "roo-code-pr-review.yml",
content: `name: Roo Code PR Review
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
review-pr:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Review PR with Roo Code
env:
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
ROO_CODE_API_KEY: \${{ secrets.ROO_CODE_API_KEY }}
run: |
# This is a placeholder for the actual Roo Code CLI tool
# that would review the PR
echo "Reviewing PR #\${{ github.event.pull_request.number }}"
echo "Title: \${{ github.event.pull_request.title }}"
- name: Post review comment
if: success()
uses: actions/github-script@v7
with:
github-token: \${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.pulls.createReview({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
body: '## Roo Code Review\\n\\nThis PR has been automatically reviewed by Roo Code.\\n\\n✅ No issues found.',
event: 'COMMENT'
});
`,
},
{
name: "Roo Code Auto-Fix",
description: "Automatically fix code issues on push",
fileName: "roo-code-auto-fix.yml",
content: `name: Roo Code Auto-Fix
on:
push:
branches: [ main, develop ]
paths:
- '**.ts'
- '**.tsx'
- '**.js'
- '**.jsx'
- '**.py'
- '**.java'
jobs:
auto-fix:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: \${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Run Roo Code auto-fix
env:
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
ROO_CODE_API_KEY: \${{ secrets.ROO_CODE_API_KEY }}
run: |
# This is a placeholder for the actual Roo Code CLI tool
# that would auto-fix issues
echo "Running Roo Code auto-fix on changed files"
- name: Commit and push if changed
run: |
git config --global user.name 'Roo Code Bot'
git config --global user.email 'bot@roo-code.com'
git add -A
if git diff --staged --quiet; then
echo "No changes to commit"
else
git commit -m "fix: Auto-fix by Roo Code bot"
git push
fi
`,
},
]
}
public async installWorkflows(selectedTemplates?: string[]): Promise<void> {
if (!this.workspaceRoot) {
throw new Error("No workspace folder open")
}
const templates = this.getWorkflowTemplates()
const templatesToInstall = selectedTemplates
? templates.filter((t) => selectedTemplates.includes(t.fileName))
: templates
const workflowsDir = path.join(this.workspaceRoot, this.config.workflowsPath)
// Create workflows directory if it doesn't exist
await fs.mkdir(workflowsDir, { recursive: true })
for (const template of templatesToInstall) {
const filePath = path.join(workflowsDir, template.fileName)
// Check if file already exists
try {
await fs.access(filePath)
const overwrite = await vscode.window.showWarningMessage(
`Workflow ${template.fileName} already exists. Overwrite?`,
"Yes",
"No",
)
if (overwrite !== "Yes") {
continue
}
} catch {
// File doesn't exist, proceed with creation
}
await fs.writeFile(filePath, template.content, "utf8")
this.outputChannel.appendLine(`Installed workflow: ${template.fileName}`)
}
vscode.window.showInformationMessage(
`Successfully installed ${templatesToInstall.length} GitHub Actions workflow(s)`,
)
}
public async setupBot(): Promise<void> {
// Guide user through bot setup
const steps = [
"1. Go to your repository settings on GitHub",
"2. Navigate to 'Secrets and variables' > 'Actions'",
"3. Add a new secret named 'ROO_CODE_API_KEY'",
"4. Generate an API key from Roo Code settings",
"5. Paste the API key as the secret value",
]
const message = `To complete GitHub Actions bot setup:\n\n${steps.join("\n")}`
const result = await vscode.window.showInformationMessage(
message,
"Open GitHub Settings",
"Copy Instructions",
"Close",
)
if (result === "Open GitHub Settings") {
const repoUrl = await this.getRepositoryUrl()
if (repoUrl) {
vscode.env.openExternal(vscode.Uri.parse(`${repoUrl}/settings/secrets/actions`))
}
} else if (result === "Copy Instructions") {
await vscode.env.clipboard.writeText(steps.join("\n"))
vscode.window.showInformationMessage("Setup instructions copied to clipboard")
}
}
private async getRepositoryUrl(): Promise<string | undefined> {
if (!this.workspaceRoot) {
return undefined
}
try {
const gitConfigPath = path.join(this.workspaceRoot, ".git", "config")
const gitConfig = await fs.readFile(gitConfigPath, "utf8")
// Extract remote origin URL
const match = gitConfig.match(/url = (.+)/)
if (match) {
let url = match[1]
// Convert SSH URL to HTTPS if needed
if (url.startsWith("git@github.com:")) {
url = url.replace("git@github.com:", "https://github.com/")
}
// Remove .git suffix if present
if (url.endsWith(".git")) {
url = url.slice(0, -4)
}
return url
}
} catch (error) {
this.outputChannel.appendLine(`Failed to get repository URL: ${error}`)
}
return undefined
}
public getAvailableTemplates(): WorkflowTemplate[] {
return this.getWorkflowTemplates()
}
public async checkWorkflowsInstalled(): Promise<boolean> {
if (!this.workspaceRoot) {
return false
}
const workflowsDir = path.join(this.workspaceRoot, this.config.workflowsPath)
try {
await fs.access(workflowsDir)
const files = await fs.readdir(workflowsDir)
return files.some((file) => file.startsWith("roo-code-"))
} catch {
return false
}
}
public async getInstalledWorkflows(): Promise<string[]> {
if (!this.workspaceRoot) {
return []
}
const workflowsDir = path.join(this.workspaceRoot, this.config.workflowsPath)
try {
const files = await fs.readdir(workflowsDir)
return files.filter((file) => file.startsWith("roo-code-") && file.endsWith(".yml"))
} catch {
return []
}
}
public dispose(): void {
// Clean up resources if needed
GitHubActionsService.instance = undefined
}
}

View file

@ -0,0 +1,2 @@
export { GitHubActionsService } from "./GitHubActionsService"
export type { GitHubActionsConfig, WorkflowTemplate } from "./GitHubActionsService"

View file

@ -133,6 +133,7 @@ export interface ExtensionMessage {
| "historyButtonClicked"
| "promptsButtonClicked"
| "marketplaceButtonClicked"
| "githubActionsButtonClicked"
| "cloudButtonClicked"
| "didBecomeVisible"
| "focusInput"

View file

@ -26,8 +26,9 @@ import { CloudView } from "./components/cloud/CloudView"
import { useAddNonInteractiveClickListener } from "./components/ui/hooks/useNonInteractiveClick"
import { TooltipProvider } from "./components/ui/tooltip"
import { STANDARD_TOOLTIP_DELAY } from "./components/ui/standard-tooltip"
import GitHubActionsView from "./components/githubActions/GitHubActionsView"
type Tab = "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "cloud"
type Tab = "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "cloud" | "githubActions"
interface HumanRelayDialogState {
isOpen: boolean
@ -62,6 +63,7 @@ const tabsByMessageAction: Partial<Record<NonNullable<ExtensionMessage["action"]
mcpButtonClicked: "mcp",
historyButtonClicked: "history",
marketplaceButtonClicked: "marketplace",
githubActionsButtonClicked: "githubActions",
cloudButtonClicked: "cloud",
}
@ -270,6 +272,7 @@ const App = () => {
onDone={() => switchTab("chat")}
/>
)}
{tab === "githubActions" && <GitHubActionsView onDone={() => switchTab("chat")} />}
<ChatView
ref={chatViewRef}
isHidden={tab !== "chat"}

View file

@ -0,0 +1,259 @@
import React, { useState, useEffect } from "react"
import { VSCodeCheckbox, VSCodeDivider } from "@vscode/webview-ui-toolkit/react"
import { Github, Check, X, AlertCircle } from "lucide-react"
import { vscode } from "@src/utils/vscode"
import { Button } from "@src/components/ui"
interface GitHubActionsViewProps {
onDone: () => void
}
interface WorkflowTemplate {
name: string
description: string
fileName: string
selected: boolean
}
const GitHubActionsView: React.FC<GitHubActionsViewProps> = ({ onDone }) => {
const [isEnabled, setIsEnabled] = useState(false)
const [workflowsInstalled, setWorkflowsInstalled] = useState(false)
const [templates, setTemplates] = useState<WorkflowTemplate[]>([
{
name: "Roo Code Issue Handler",
description: "Automatically handle GitHub issues with Roo Code",
fileName: "roo-code-issues.yml",
selected: true,
},
{
name: "Roo Code PR Review",
description: "Automatically review pull requests with Roo Code",
fileName: "roo-code-pr-review.yml",
selected: true,
},
{
name: "Roo Code Auto-Fix",
description: "Automatically fix code issues on push",
fileName: "roo-code-auto-fix.yml",
selected: false,
},
])
const [installing, setInstalling] = useState(false)
const [setupComplete, setSetupComplete] = useState(false)
useEffect(() => {
// Check if GitHub Actions is enabled
vscode.postMessage({ type: "githubActionsStatus" })
}, [])
const handleEnable = () => {
setIsEnabled(true)
vscode.postMessage({ type: "githubActionsEnable" })
}
const handleDisable = () => {
setIsEnabled(false)
vscode.postMessage({ type: "githubActionsDisable" })
}
const handleTemplateToggle = (index: number) => {
const newTemplates = [...templates]
newTemplates[index].selected = !newTemplates[index].selected
setTemplates(newTemplates)
}
const handleInstallWorkflows = async () => {
setInstalling(true)
const selectedTemplates = templates.filter((t) => t.selected).map((t) => t.fileName)
vscode.postMessage({
type: "githubActionsInstallWorkflows",
templates: selectedTemplates,
})
// Simulate installation delay
setTimeout(() => {
setInstalling(false)
setWorkflowsInstalled(true)
}, 2000)
}
const handleSetupBot = () => {
vscode.postMessage({ type: "githubActionsSetupBot" })
setSetupComplete(true)
}
return (
<div className="flex flex-col h-full">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-vscode-panel-border">
<div className="flex items-center gap-2">
<Github className="w-5 h-5" />
<h2 className="text-lg font-semibold">GitHub Actions Bot</h2>
</div>
<Button variant="ghost" size="sm" onClick={onDone}>
<X className="w-4 h-4" />
</Button>
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-4">
<div className="space-y-6">
{/* Enable/Disable Section */}
<div className="space-y-3">
<h3 className="text-base font-medium">Bot Status</h3>
<div className="flex items-center gap-3">
<div
className={`flex items-center gap-2 px-3 py-1 rounded-md ${
isEnabled
? "bg-green-500/10 text-green-500 border border-green-500/20"
: "bg-vscode-input-background text-vscode-descriptionForeground border border-vscode-panel-border"
}`}>
{isEnabled ? (
<>
<Check className="w-4 h-4" />
<span>Enabled</span>
</>
) : (
<>
<X className="w-4 h-4" />
<span>Disabled</span>
</>
)}
</div>
{isEnabled ? (
<Button variant="secondary" size="sm" onClick={handleDisable}>
Disable Bot
</Button>
) : (
<Button variant="default" size="sm" onClick={handleEnable}>
Enable Bot
</Button>
)}
</div>
</div>
<VSCodeDivider />
{/* Workflow Templates Section */}
<div className="space-y-3">
<h3 className="text-base font-medium">Workflow Templates</h3>
<p className="text-sm text-vscode-descriptionForeground">
Select the GitHub Actions workflows you want to install:
</p>
<div className="space-y-2">
{templates.map((template, index) => (
<div
key={template.fileName}
className="flex items-start gap-3 p-3 rounded-md bg-vscode-input-background border border-vscode-panel-border">
<VSCodeCheckbox
checked={template.selected}
onChange={() => handleTemplateToggle(index)}
/>
<div className="flex-1">
<div className="font-medium">{template.name}</div>
<div className="text-sm text-vscode-descriptionForeground mt-1">
{template.description}
</div>
<div className="text-xs text-vscode-descriptionForeground mt-1 font-mono">
{template.fileName}
</div>
</div>
</div>
))}
</div>
<div className="flex gap-2">
<Button
variant="default"
onClick={handleInstallWorkflows}
disabled={installing || !templates.some((t) => t.selected)}>
{installing ? "Installing..." : "Install Selected Workflows"}
</Button>
{workflowsInstalled && (
<div className="flex items-center gap-2 text-green-500">
<Check className="w-4 h-4" />
<span className="text-sm">Workflows installed successfully!</span>
</div>
)}
</div>
</div>
<VSCodeDivider />
{/* Setup Instructions Section */}
<div className="space-y-3">
<h3 className="text-base font-medium">Setup Instructions</h3>
<div className="space-y-3 p-4 rounded-md bg-vscode-input-background border border-vscode-panel-border">
<div className="flex items-start gap-2">
<AlertCircle className="w-4 h-4 text-yellow-500 mt-0.5 shrink-0" />
<div className="text-sm">
<p className="font-medium mb-2">To complete the setup:</p>
<ol className="list-decimal list-inside space-y-1 text-vscode-descriptionForeground">
<li>Go to your repository settings on GitHub</li>
<li>Navigate to "Secrets and variables" "Actions"</li>
<li>
Add a new secret named{" "}
<code className="px-1 py-0.5 bg-vscode-editor-background rounded">
ROO_CODE_API_KEY
</code>
</li>
<li>Generate an API key from Roo Code settings</li>
<li>Paste the API key as the secret value</li>
</ol>
</div>
</div>
</div>
<div className="flex gap-2">
<Button variant="default" onClick={handleSetupBot}>
Open Setup Guide
</Button>
{setupComplete && (
<div className="flex items-center gap-2 text-green-500">
<Check className="w-4 h-4" />
<span className="text-sm">Setup guide opened!</span>
</div>
)}
</div>
</div>
{/* Status Section */}
{isEnabled && workflowsInstalled && (
<>
<VSCodeDivider />
<div className="space-y-3">
<h3 className="text-base font-medium">Bot Status</h3>
<div className="p-4 rounded-md bg-green-500/10 border border-green-500/20">
<div className="flex items-center gap-2 text-green-500">
<Check className="w-5 h-5" />
<span className="font-medium">GitHub Actions Bot is Active</span>
</div>
<p className="text-sm text-vscode-descriptionForeground mt-2">
The bot will automatically process issues and pull requests based on your
installed workflows.
</p>
</div>
</div>
</>
)}
</div>
</div>
{/* Footer */}
<div className="p-4 border-t border-vscode-panel-border">
<div className="flex justify-between items-center">
<div className="text-xs text-vscode-descriptionForeground">
GitHub Actions integration for automated issue and PR handling
</div>
<Button variant="secondary" onClick={onDone}>
Close
</Button>
</div>
</div>
</div>
)
}
export default GitHubActionsView