(command: string)
| 213 | * collapse logic. |
| 214 | */ |
| 215 | export function isShellCommandTargetingMemory(command: string): boolean { |
| 216 | const configDir = getClaudeConfigHomeDir() |
| 217 | const memoryBase = getMemoryBaseDir() |
| 218 | const autoMemDir = isAutoMemoryEnabled() |
| 219 | ? getAutoMemPath().replace(/[/\\]+$/, '') |
| 220 | : '' |
| 221 | |
| 222 | // Quick check: does the command mention the config, memory base, or |
| 223 | // auto-mem directory? Compare in forward-slash form (PowerShell on Windows |
| 224 | // may use either separator while configDir uses the platform-native one). |
| 225 | // On Windows also check the MinGW form (/c/...) since BashTool runs under |
| 226 | // Git Bash which emits that encoding. On Linux/Mac, configDir is already |
| 227 | // posix so only one form to check — and crucially, windowsPathToPosixPath |
| 228 | // is NOT called, so Linux paths like /m/foo aren't misinterpreted as MinGW. |
| 229 | const commandCmp = toComparable(command) |
| 230 | const dirs = [configDir, memoryBase, autoMemDir].filter(Boolean) |
| 231 | const matchesAnyDir = dirs.some(d => { |
| 232 | if (commandCmp.includes(toComparable(d))) return true |
| 233 | if (IS_WINDOWS) { |
| 234 | // BashTool on Windows (Git Bash) emits /c/Users/... — check MinGW form too |
| 235 | return commandCmp.includes(windowsPathToPosixPath(d).toLowerCase()) |
| 236 | } |
| 237 | return false |
| 238 | }) |
| 239 | if (!matchesAnyDir) { |
| 240 | return false |
| 241 | } |
| 242 | |
| 243 | // Extract absolute path-like tokens. Matches Unix absolute paths (/foo/bar), |
| 244 | // Windows drive-letter paths (C:\foo, C:/foo), and MinGW paths (/c/foo — |
| 245 | // they're /-prefixed so the regex already captures them). Bare backslash |
| 246 | // tokens (\foo) are intentionally excluded — they appear in regex/grep |
| 247 | // patterns and would cause false-positive memory classification after |
| 248 | // normalization flips backslashes to forward slashes. |
| 249 | const matches = command.match(/(?:[A-Za-z]:[/\\]|\/)[^\s'"]+/g) |
| 250 | if (!matches) { |
| 251 | return false |
| 252 | } |
| 253 | |
| 254 | for (const match of matches) { |
| 255 | // Strip trailing shell metacharacters that could be adjacent to a path |
| 256 | const cleanPath = match.replace(/[,;|&>]+$/, '') |
| 257 | // On Windows, convert MinGW /c/... → native C:\... at this single |
| 258 | // point. Downstream predicates (isAutoManagedMemoryFile, isMemoryDirectory, |
| 259 | // isAutoMemPath, isAgentMemoryPath) then receive native paths and only |
| 260 | // need toComparable() for matching. On other platforms, paths are already |
| 261 | // native — no conversion, so /m/foo etc. pass through unmodified. |
| 262 | const nativePath = IS_WINDOWS |
| 263 | ? posixPathToWindowsPath(cleanPath) |
| 264 | : cleanPath |
| 265 | if (isAutoManagedMemoryFile(nativePath) || isMemoryDirectory(nativePath)) { |
| 266 | return true |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | return false |
| 271 | } |
| 272 |
no test coverage detected