| 140 | |
| 141 | // Helper: Parse grep/rg style commands (pattern then paths) |
| 142 | function parsePatternCommand( |
| 143 | args: string[], |
| 144 | flagsWithArgs: Set<string>, |
| 145 | defaults: string[] = [], |
| 146 | ): string[] { |
| 147 | const paths: string[] = [] |
| 148 | let patternFound = false |
| 149 | // SECURITY: Track `--` end-of-options delimiter. After `--`, all args are |
| 150 | // positional regardless of leading `-`. See filterOutFlags() doc comment. |
| 151 | let afterDoubleDash = false |
| 152 | |
| 153 | for (let i = 0; i < args.length; i++) { |
| 154 | const arg = args[i] |
| 155 | if (arg === undefined || arg === null) continue |
| 156 | |
| 157 | if (!afterDoubleDash && arg === '--') { |
| 158 | afterDoubleDash = true |
| 159 | continue |
| 160 | } |
| 161 | |
| 162 | if (!afterDoubleDash && arg.startsWith('-')) { |
| 163 | const flag = arg.split('=')[0] |
| 164 | // Pattern flags mark that we've found the pattern |
| 165 | if (flag && ['-e', '--regexp', '-f', '--file'].includes(flag)) { |
| 166 | patternFound = true |
| 167 | } |
| 168 | // Skip next arg if flag needs it |
| 169 | if (flag && flagsWithArgs.has(flag) && !arg.includes('=')) { |
| 170 | i++ |
| 171 | } |
| 172 | continue |
| 173 | } |
| 174 | |
| 175 | // First non-flag is pattern, rest are paths |
| 176 | if (!patternFound) { |
| 177 | patternFound = true |
| 178 | continue |
| 179 | } |
| 180 | paths.push(arg) |
| 181 | } |
| 182 | |
| 183 | return paths.length > 0 ? paths : defaults |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Extracts paths from command arguments for different path commands. |