mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-09 22:31:08 +00:00
fix: Update shell-quote to v1.8.3 and use it for command pattern extraction
- Updated shell-quote from v1.8.2 to v1.8.3 in webview-ui - Added shell-quote v1.8.3 to backend dependencies - Updated shared extract-command-pattern module to use shell-quote for proper parsing - Added @types/shell-quote for TypeScript support - Ensures consistent command pattern extraction across frontend and backend
This commit is contained in:
parent
db8c60ceff
commit
82c3e927b8
24 changed files with 138 additions and 90 deletions
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
|
|
@ -723,6 +723,9 @@ importers:
|
|||
serialize-error:
|
||||
specifier: ^12.0.0
|
||||
version: 12.0.0
|
||||
shell-quote:
|
||||
specifier: 1.8.3
|
||||
version: 1.8.3
|
||||
simple-git:
|
||||
specifier: ^3.27.0
|
||||
version: 3.27.0
|
||||
|
|
@ -814,6 +817,9 @@ importers:
|
|||
'@types/ps-tree':
|
||||
specifier: ^1.1.6
|
||||
version: 1.1.6
|
||||
'@types/shell-quote':
|
||||
specifier: ^1.7.5
|
||||
version: 1.7.5
|
||||
'@types/stream-json':
|
||||
specifier: ^1.7.8
|
||||
version: 1.7.8
|
||||
|
|
@ -1028,8 +1034,8 @@ importers:
|
|||
specifier: ^0.6.0
|
||||
version: 0.6.2
|
||||
shell-quote:
|
||||
specifier: ^1.8.2
|
||||
version: 1.8.2
|
||||
specifier: ^1.8.3
|
||||
version: 1.8.3
|
||||
shiki:
|
||||
specifier: ^3.2.1
|
||||
version: 3.4.1
|
||||
|
|
@ -8478,10 +8484,6 @@ packages:
|
|||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
shell-quote@1.8.2:
|
||||
resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
shell-quote@1.8.3:
|
||||
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -13295,7 +13297,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@22.15.29)(@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.50)(@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:
|
||||
|
|
@ -17293,7 +17295,7 @@ snapshots:
|
|||
minimatch: 10.0.1
|
||||
pidtree: 0.6.0
|
||||
read-package-json-fast: 4.0.0
|
||||
shell-quote: 1.8.2
|
||||
shell-quote: 1.8.3
|
||||
which: 5.0.0
|
||||
|
||||
npm-run-path@4.0.1:
|
||||
|
|
@ -18522,10 +18524,7 @@ snapshots:
|
|||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
shell-quote@1.8.2: {}
|
||||
|
||||
shell-quote@1.8.3:
|
||||
optional: true
|
||||
shell-quote@1.8.3: {}
|
||||
|
||||
shiki@3.4.1:
|
||||
dependencies:
|
||||
|
|
|
|||
|
|
@ -15,85 +15,10 @@ import { ExitCodeDetails, RooTerminalCallbacks, RooTerminalProcess } from "../..
|
|||
import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry"
|
||||
import { Terminal } from "../../integrations/terminal/Terminal"
|
||||
import { t } from "../../i18n"
|
||||
import { extractCommandPattern } from "../../shared/extract-command-pattern"
|
||||
|
||||
class ShellIntegrationError extends Error {}
|
||||
|
||||
/**
|
||||
* Extract the base command pattern from a full command string.
|
||||
* For example: "gh pr checkout 1234" -> "gh pr checkout"
|
||||
*
|
||||
* @param command The full command string
|
||||
* @returns The base command pattern suitable for whitelisting
|
||||
*/
|
||||
function extractCommandPattern(command: string): string {
|
||||
if (!command?.trim()) return ""
|
||||
|
||||
// Split by whitespace, handling quoted strings
|
||||
const parts: string[] = []
|
||||
let current = ""
|
||||
let inQuotes = false
|
||||
let quoteChar = ""
|
||||
|
||||
for (let i = 0; i < command.length; i++) {
|
||||
const char = command[i]
|
||||
const prevChar = i > 0 ? command[i - 1] : ""
|
||||
|
||||
if ((char === '"' || char === "'") && prevChar !== "\\") {
|
||||
if (!inQuotes) {
|
||||
inQuotes = true
|
||||
quoteChar = char
|
||||
} else if (char === quoteChar) {
|
||||
inQuotes = false
|
||||
quoteChar = ""
|
||||
}
|
||||
current += char
|
||||
} else if (char === " " && !inQuotes) {
|
||||
if (current) {
|
||||
parts.push(current)
|
||||
current = ""
|
||||
}
|
||||
} else {
|
||||
current += char
|
||||
}
|
||||
}
|
||||
|
||||
if (current) {
|
||||
parts.push(current)
|
||||
}
|
||||
|
||||
// Extract pattern parts, stopping at arguments
|
||||
const patternParts: string[] = []
|
||||
|
||||
for (const part of parts) {
|
||||
// Remove quotes for analysis
|
||||
const unquoted = part.replace(/^["']|["']$/g, "")
|
||||
|
||||
// Stop at common argument patterns:
|
||||
// - Pure numbers (like PR numbers, PIDs, etc.)
|
||||
// - Flags starting with - or --
|
||||
// - File paths or URLs
|
||||
// - Variable assignments (KEY=VALUE)
|
||||
// - Operators (&&, ||, |, ;, >, <, etc.)
|
||||
if (
|
||||
/^\d+$/.test(unquoted) ||
|
||||
unquoted.startsWith("-") ||
|
||||
unquoted.includes("/") ||
|
||||
unquoted.includes("\\") ||
|
||||
unquoted.includes("=") ||
|
||||
unquoted.startsWith("http") ||
|
||||
unquoted.includes(".") ||
|
||||
["&&", "||", "|", ";", ">", "<", ">>", "<<", "&"].includes(unquoted)
|
||||
) {
|
||||
// Stop collecting pattern parts
|
||||
break
|
||||
}
|
||||
patternParts.push(part)
|
||||
}
|
||||
|
||||
// Return the base command pattern
|
||||
return patternParts.join(" ")
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a command pattern to the whitelist
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -407,6 +407,7 @@
|
|||
"sanitize-filename": "^1.6.3",
|
||||
"say": "^0.16.0",
|
||||
"serialize-error": "^12.0.0",
|
||||
"shell-quote": "1.8.3",
|
||||
"simple-git": "^3.27.0",
|
||||
"sound-play": "^1.1.0",
|
||||
"stream-json": "^1.8.0",
|
||||
|
|
@ -439,6 +440,7 @@
|
|||
"@types/node-ipc": "^9.2.3",
|
||||
"@types/proper-lockfile": "^4.1.4",
|
||||
"@types/ps-tree": "^1.1.6",
|
||||
"@types/shell-quote": "^1.7.5",
|
||||
"@types/stream-json": "^1.7.8",
|
||||
"@types/string-similarity": "^4.0.2",
|
||||
"@types/tmp": "^0.2.6",
|
||||
|
|
|
|||
50
src/shared/extract-command-pattern.ts
Normal file
50
src/shared/extract-command-pattern.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { parse } from "shell-quote"
|
||||
|
||||
type ShellToken = string | { op: string } | { command: string }
|
||||
|
||||
/**
|
||||
* Extract the base command pattern from a full command string.
|
||||
* For example: "gh pr checkout 1234" -> "gh pr checkout"
|
||||
*
|
||||
* Uses shell-quote v1.8.3 for proper shell parsing.
|
||||
*
|
||||
* @param command The full command string
|
||||
* @returns The base command pattern suitable for whitelisting
|
||||
*/
|
||||
export function extractCommandPattern(command: string): string {
|
||||
if (!command?.trim()) return ""
|
||||
|
||||
// Parse the command to get tokens
|
||||
const tokens = parse(command.trim()) as ShellToken[]
|
||||
const patternParts: string[] = []
|
||||
|
||||
for (const token of tokens) {
|
||||
if (typeof token === "string") {
|
||||
// Check if this token looks like an argument (number, flag, etc.)
|
||||
// Common patterns to stop at:
|
||||
// - Pure numbers (like PR numbers, PIDs, etc.)
|
||||
// - Flags starting with - or --
|
||||
// - File paths or URLs
|
||||
// - Variable assignments (KEY=VALUE)
|
||||
if (
|
||||
/^\d+$/.test(token) ||
|
||||
token.startsWith("-") ||
|
||||
token.includes("/") ||
|
||||
token.includes("\\") ||
|
||||
token.includes("=") ||
|
||||
token.startsWith("http") ||
|
||||
token.includes(".")
|
||||
) {
|
||||
// Stop collecting pattern parts
|
||||
break
|
||||
}
|
||||
patternParts.push(token)
|
||||
} else if (typeof token === "object" && "op" in token) {
|
||||
// Stop at operators
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Return the base command pattern
|
||||
return patternParts.join(" ")
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
"remark-gfm": "^4.0.1",
|
||||
"remark-math": "^6.0.0",
|
||||
"remove-markdown": "^0.6.0",
|
||||
"shell-quote": "^1.8.2",
|
||||
"shell-quote": "^1.8.3",
|
||||
"shiki": "^3.2.1",
|
||||
"source-map": "^0.7.4",
|
||||
"styled-components": "^6.1.13",
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
setClineAsk("command")
|
||||
setEnableButtons(!isPartial)
|
||||
setPrimaryButtonText(t("chat:runCommand.title"))
|
||||
setSecondaryButtonText("Add & Run")
|
||||
setSecondaryButtonText(t("chat:addAndRun.title"))
|
||||
setTertiaryButtonText(t("chat:reject.title"))
|
||||
break
|
||||
case "command_output":
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "Desar",
|
||||
"tooltip": "Desa els canvis del fitxer"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rebutjar",
|
||||
"tooltip": "Rebutja aquesta acció"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "Speichern",
|
||||
"tooltip": "Dateiänderungen speichern"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Ablehnen",
|
||||
"tooltip": "Diese Aktion ablehnen"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"tokensUsed": "Tokens used: {{used}} of {{total}}",
|
||||
"reservedForResponse": "Reserved for model response: {{amount}} tokens"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Reject",
|
||||
"tooltip": "Reject this action"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"tokensUsed": "Tokens utilizados: {{used}} de {{total}}",
|
||||
"reservedForResponse": "Reservado para respuesta del modelo: {{amount}} tokens"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rechazar",
|
||||
"tooltip": "Rechazar esta acción"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "Enregistrer",
|
||||
"tooltip": "Sauvegarder les modifications du fichier"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rejeter",
|
||||
"tooltip": "Rejeter cette action"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "सहेजें",
|
||||
"tooltip": "फ़ाइल परिवर्तन सहेजें"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "अस्वीकार करें",
|
||||
"tooltip": "इस क्रिया को अस्वीकार करें"
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@
|
|||
"tokensUsed": "Token digunakan: {{used}} dari {{total}}",
|
||||
"reservedForResponse": "Dicadangkan untuk respons model: {{amount}} token"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Tolak",
|
||||
"tooltip": "Tolak aksi ini"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "Salva",
|
||||
"tooltip": "Salva le modifiche al file"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rifiuta",
|
||||
"tooltip": "Rifiuta questa azione"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "保存",
|
||||
"tooltip": "ファイル変更を保存"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "拒否",
|
||||
"tooltip": "このアクションを拒否"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "저장",
|
||||
"tooltip": "파일 변경사항 저장"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "거부",
|
||||
"tooltip": "이 작업 거부"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"tokensUsed": "Gebruikte tokens: {{used}} van {{total}}",
|
||||
"reservedForResponse": "Gereserveerd voor modelantwoord: {{amount}} tokens"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Weigeren",
|
||||
"tooltip": "Deze actie weigeren"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "Zapisz",
|
||||
"tooltip": "Zapisz zmiany w pliku"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Odrzuć",
|
||||
"tooltip": "Odrzuć tę akcję"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "Salvar",
|
||||
"tooltip": "Salvar as alterações do arquivo"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Rejeitar",
|
||||
"tooltip": "Rejeitar esta ação"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"tokensUsed": "Использовано токенов: {{used}} из {{total}}",
|
||||
"reservedForResponse": "Зарезервировано для ответа модели: {{amount}} токенов"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Отклонить",
|
||||
"tooltip": "Отклонить это действие"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "Kaydet",
|
||||
"tooltip": "Dosya değişikliklerini kaydet"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Reddet",
|
||||
"tooltip": "Bu eylemi reddet"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "Lưu",
|
||||
"tooltip": "Lưu các thay đổi tệp"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "Từ chối",
|
||||
"tooltip": "Từ chối hành động này"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "保存",
|
||||
"tooltip": "保存文件更改"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "拒绝",
|
||||
"tooltip": "拒绝此操作"
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"title": "儲存",
|
||||
"tooltip": "儲存檔案變更"
|
||||
},
|
||||
"addAndRun": {
|
||||
"title": "Add & Run",
|
||||
"tooltip": "Add command to whitelist and run it"
|
||||
},
|
||||
"reject": {
|
||||
"title": "拒絕",
|
||||
"tooltip": "拒絕此操作"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue