( text: string, context: ToolUseContext, slashCommandName: string, shell?: FrontmatterShell, )
| 67 | * See docs/design/ps-shell-selection.md §5.3. |
| 68 | */ |
| 69 | export async function executeShellCommandsInPrompt( |
| 70 | text: string, |
| 71 | context: ToolUseContext, |
| 72 | slashCommandName: string, |
| 73 | shell?: FrontmatterShell, |
| 74 | ): Promise<string> { |
| 75 | let result = text |
| 76 | |
| 77 | // Resolve the tool once. `shell === undefined` and `shell === 'bash'` both |
| 78 | // hit BashTool. PowerShell only when the runtime gate allows — a skill |
| 79 | // author's frontmatter choice doesn't override the user's opt-in/out. |
| 80 | const shellTool: PromptShellTool = |
| 81 | shell === 'powershell' && isPowerShellToolEnabled() |
| 82 | ? getPowerShellTool() |
| 83 | : BashTool |
| 84 | |
| 85 | // INLINE_PATTERN's lookbehind is ~100x slower than BLOCK_PATTERN on large |
| 86 | // skill content (265µs vs 2µs @ 17KB). 93% of skills have no !` at all, |
| 87 | // so gate the expensive scan on a cheap substring check. BLOCK_PATTERN |
| 88 | // (```!) doesn't require !` in the text, so it's always scanned. |
| 89 | const blockMatches = text.matchAll(BLOCK_PATTERN) |
| 90 | const inlineMatches = text.includes('!`') ? text.matchAll(INLINE_PATTERN) : [] |
| 91 | |
| 92 | await Promise.all( |
| 93 | [...blockMatches, ...inlineMatches].map(async match => { |
| 94 | const command = match[1]?.trim() |
| 95 | if (command) { |
| 96 | try { |
| 97 | // Check permissions before executing |
| 98 | const permissionResult = await hasPermissionsToUseTool( |
| 99 | shellTool, |
| 100 | { command }, |
| 101 | context, |
| 102 | createAssistantMessage({ content: [] }), |
| 103 | '', |
| 104 | ) |
| 105 | |
| 106 | if (permissionResult.behavior !== 'allow') { |
| 107 | logForDebugging( |
| 108 | `Shell command permission check failed for command in ${slashCommandName}: ${command}. Error: ${permissionResult.message}`, |
| 109 | ) |
| 110 | throw new MalformedCommandError( |
| 111 | `Shell command permission check failed for pattern "${match[0]}": ${permissionResult.message || 'Permission denied'}`, |
| 112 | ) |
| 113 | } |
| 114 | |
| 115 | const { data } = await shellTool.call({ command }, context) |
| 116 | // Reuse the same persistence flow as regular Bash tool calls |
| 117 | const toolResultBlock = await processToolResultBlock( |
| 118 | shellTool, |
| 119 | data, |
| 120 | randomUUID(), |
| 121 | ) |
| 122 | // Extract the string content from the block |
| 123 | const output = |
| 124 | typeof toolResultBlock.content === 'string' |
| 125 | ? toolResultBlock.content |
| 126 | : formatBashOutput(data.stdout, data.stderr) |
no test coverage detected