🌳
pt0/sharedF/gitDestructivePatternsF.mts
3export const destructiveGitPatterns = [
4 /^stash(?!\s+(list|show))\b/, /reset\b.*--hard/,
5 /checkout (HEAD --|-- )/, /^restore /, /revert /, /clean -f/,
6 /push\b.*(--force|-f)/, /branch -D/,
7 /^rm\b/,
8]
10const confirmFlagRe = /^--confirmedByUser(?:=(.*))?$/
12export const hasUserConfirmedGit = (args: string[]) => {
13 for (let i = 0; i < args.length; i++) {
14 const m = args[i].match(confirmFlagRe)
15 if (!m) continue
16 const val = m[1] ?? args[i + 1]
17 if (val === ptConfirmSecret) return true
18 }
19 return false
22export const stripConfirmFlag = (args: string[]) => {
23 const out: string[] = []
24 for (let i = 0; i < args.length; i++) {
25 const m = args[i].match(confirmFlagRe)
26 if (!m) { out.push(args[i]); continue }
27 if (m[1] === undefined && args[i + 1] === ptConfirmSecret) i++
28 }
29 return out
32export const isDestructiveGitArgs = (args: string[]) => {
33 const argStr = args.join(' ')
34 return destructiveGitPatterns.some(p => p.test(argStr))
37const gitTokenRe = /(^|\s)(?:[^\s;|&<>]*\/)?git(\s|$)/
38const shellSepRe = /[;|&<>]/
40export const findUnconfirmedDestructiveGit = (cmd: string): string | null => {
41 for (const segment of cmd.split(shellSepRe)) {
42 const m = segment.match(gitTokenRe)
43 if (!m) continue
44 const gitIdx = segment.indexOf('git', m.index!)
45 const argStr = segment.slice(gitIdx + 3).trim()
46 if (!argStr) continue
47 const args = argStr.split(/\s+/)
48 if (hasUserConfirmedGit(args)) continue
49 if (isDestructiveGitArgs(args)) return `git ${argStr}`
50 }
51 return null